
From: Omar Sandoval <osandov@fb.com> mainline inclusion from mainline-v5.19-rc1 commit 2256e901f5bddc56e24089c96f27b77da932dfcc category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I9QR71 CVE: CVE-2024-35956 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- When btrfs_qgroup_inherit(), btrfs_alloc_tree_block, or btrfs_insert_root() fail in create_subvol(), we return without freeing anon_dev. Reorganize the error handling in create_subvol() to fix this. Reviewed-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me> Signed-off-by: Omar Sandoval <osandov@fb.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Conflicts: fs/btrfs/ioctl.c [fix context diff] Signed-off-by: Ye Bin <yebin10@huawei.com> --- fs/btrfs/ioctl.c | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f71eaedef2ec..dd1d50c7a4dd 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -592,7 +592,7 @@ static noinline int create_subvol(struct inode *dir, struct inode *inode; int ret; int err; - dev_t anon_dev = 0; + dev_t anon_dev; u64 objectid; u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID; u64 index = 0; @@ -603,11 +603,7 @@ static noinline int create_subvol(struct inode *dir, ret = btrfs_find_free_objectid(fs_info->tree_root, &objectid); if (ret) - goto fail_free; - - ret = get_anon_bdev(&anon_dev); - if (ret < 0) - goto fail_free; + goto out_root_item; /* * Don't create subvolume whose level is not zero. Or qgroup will be @@ -615,9 +611,13 @@ static noinline int create_subvol(struct inode *dir, */ if (btrfs_qgroup_level(objectid)) { ret = -ENOSPC; - goto fail_free; + goto out_root_item; } + ret = get_anon_bdev(&anon_dev); + if (ret < 0) + goto out_root_item; + btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP); /* * The same as the snapshot creation, please see the comment @@ -625,26 +625,26 @@ static noinline int create_subvol(struct inode *dir, */ ret = btrfs_subvolume_reserve_metadata(root, &block_rsv, 8, false); if (ret) - goto fail_free; + goto out_anon_dev; trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { ret = PTR_ERR(trans); btrfs_subvolume_release_metadata(root, &block_rsv); - goto fail_free; + goto out_anon_dev; } trans->block_rsv = &block_rsv; trans->bytes_reserved = block_rsv.size; ret = btrfs_qgroup_inherit(trans, 0, objectid, inherit); if (ret) - goto fail; + goto out; leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0, BTRFS_NESTING_NORMAL); if (IS_ERR(leaf)) { ret = PTR_ERR(leaf); - goto fail; + goto out; } btrfs_mark_buffer_dirty(leaf); @@ -697,7 +697,7 @@ static noinline int create_subvol(struct inode *dir, */ btrfs_free_tree_block(trans, root, leaf, 0, 1); free_extent_buffer(leaf); - goto fail; + goto out; } free_extent_buffer(leaf); @@ -706,12 +706,11 @@ static noinline int create_subvol(struct inode *dir, key.offset = (u64)-1; new_root = btrfs_get_new_fs_root(fs_info, objectid, &anon_dev); if (IS_ERR(new_root)) { - free_anon_bdev(anon_dev); ret = PTR_ERR(new_root); btrfs_abort_transaction(trans, ret); - goto fail; + goto out; } - /* Freeing will be done in btrfs_put_root() of new_root */ + /* anon_dev is owned by new_root now. */ anon_dev = 0; btrfs_record_root_in_trans(trans, new_root); @@ -721,7 +720,7 @@ static noinline int create_subvol(struct inode *dir, if (ret) { /* We potentially lose an unused inode item here */ btrfs_abort_transaction(trans, ret); - goto fail; + goto out; } mutex_lock(&new_root->objectid_mutex); @@ -734,28 +733,28 @@ static noinline int create_subvol(struct inode *dir, ret = btrfs_set_inode_index(BTRFS_I(dir), &index); if (ret) { btrfs_abort_transaction(trans, ret); - goto fail; + goto out; } ret = btrfs_insert_dir_item(trans, name, namelen, BTRFS_I(dir), &key, BTRFS_FT_DIR, index); if (ret) { btrfs_abort_transaction(trans, ret); - goto fail; + goto out; } btrfs_i_size_write(BTRFS_I(dir), dir->i_size + namelen * 2); ret = btrfs_update_inode(trans, root, dir); if (ret) { btrfs_abort_transaction(trans, ret); - goto fail; + goto out; } ret = btrfs_add_root_ref(trans, objectid, root->root_key.objectid, btrfs_ino(BTRFS_I(dir)), index, name, namelen); if (ret) { btrfs_abort_transaction(trans, ret); - goto fail; + goto out; } ret = btrfs_uuid_tree_add(trans, root_item->uuid, @@ -763,8 +762,7 @@ static noinline int create_subvol(struct inode *dir, if (ret) btrfs_abort_transaction(trans, ret); -fail: - kfree(root_item); +out: trans->block_rsv = NULL; trans->bytes_reserved = 0; btrfs_subvolume_release_metadata(root, &block_rsv); @@ -779,11 +777,10 @@ static noinline int create_subvol(struct inode *dir, return PTR_ERR(inode); d_instantiate(dentry, inode); } - return ret; - -fail_free: +out_anon_dev: if (anon_dev) free_anon_bdev(anon_dev); +out_root_item: kfree(root_item); return ret; } -- 2.31.1