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; }