From: Greg Kroah-Hartman Date: Tue, 1 Aug 2023 07:52:10 +0000 (+0200) Subject: 5.4-stable patches X-Git-Tag: v5.15.124~20 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6f9f85c57dfa377451f1f5c89a3fb49e54b9c3eb;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: btrfs-fix-race-between-quota-disable-and-quota-assign-ioctls.patch btrfs-qgroup-remove-one-time-use-variables-for-quota_root-checks.patch btrfs-qgroup-return-enotconn-instead-of-einval-when-quotas-are-not-enabled.patch --- diff --git a/queue-5.4/btrfs-fix-race-between-quota-disable-and-quota-assign-ioctls.patch b/queue-5.4/btrfs-fix-race-between-quota-disable-and-quota-assign-ioctls.patch new file mode 100644 index 00000000000..77bd3a97507 --- /dev/null +++ b/queue-5.4/btrfs-fix-race-between-quota-disable-and-quota-assign-ioctls.patch @@ -0,0 +1,194 @@ +From 2f1a6be12ab6c8470d5776e68644726c94257c54 Mon Sep 17 00:00:00 2001 +From: Filipe Manana +Date: Wed, 22 Mar 2023 10:33:28 +0000 +Subject: btrfs: fix race between quota disable and quota assign ioctls + +From: Filipe Manana + +commit 2f1a6be12ab6c8470d5776e68644726c94257c54 upstream. + +The quota assign ioctl can currently run in parallel with a quota disable +ioctl call. The assign ioctl uses the quota root, while the disable ioctl +frees that root, and therefore we can have a use-after-free triggered in +the assign ioctl, leading to a trace like the following when KASAN is +enabled: + + [672.723][T736] BUG: KASAN: slab-use-after-free in btrfs_search_slot+0x2962/0x2db0 + [672.723][T736] Read of size 8 at addr ffff888022ec0208 by task btrfs_search_sl/27736 + [672.724][T736] + [672.725][T736] CPU: 1 PID: 27736 Comm: btrfs_search_sl Not tainted 6.3.0-rc3 #37 + [672.723][T736] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 + [672.727][T736] Call Trace: + [672.728][T736] + [672.728][T736] dump_stack_lvl+0xd9/0x150 + [672.725][T736] print_report+0xc1/0x5e0 + [672.720][T736] ? __virt_addr_valid+0x61/0x2e0 + [672.727][T736] ? __phys_addr+0xc9/0x150 + [672.725][T736] ? btrfs_search_slot+0x2962/0x2db0 + [672.722][T736] kasan_report+0xc0/0xf0 + [672.729][T736] ? btrfs_search_slot+0x2962/0x2db0 + [672.724][T736] btrfs_search_slot+0x2962/0x2db0 + [672.723][T736] ? fs_reclaim_acquire+0xba/0x160 + [672.722][T736] ? split_leaf+0x13d0/0x13d0 + [672.726][T736] ? rcu_is_watching+0x12/0xb0 + [672.723][T736] ? kmem_cache_alloc+0x338/0x3c0 + [672.722][T736] update_qgroup_status_item+0xf7/0x320 + [672.724][T736] ? add_qgroup_rb+0x3d0/0x3d0 + [672.739][T736] ? do_raw_spin_lock+0x12d/0x2b0 + [672.730][T736] ? spin_bug+0x1d0/0x1d0 + [672.737][T736] btrfs_run_qgroups+0x5de/0x840 + [672.730][T736] ? btrfs_qgroup_rescan_worker+0xa70/0xa70 + [672.738][T736] ? __del_qgroup_relation+0x4ba/0xe00 + [672.738][T736] btrfs_ioctl+0x3d58/0x5d80 + [672.735][T736] ? tomoyo_path_number_perm+0x16a/0x550 + [672.737][T736] ? tomoyo_execute_permission+0x4a0/0x4a0 + [672.731][T736] ? btrfs_ioctl_get_supported_features+0x50/0x50 + [672.737][T736] ? __sanitizer_cov_trace_switch+0x54/0x90 + [672.734][T736] ? do_vfs_ioctl+0x132/0x1660 + [672.730][T736] ? vfs_fileattr_set+0xc40/0xc40 + [672.730][T736] ? _raw_spin_unlock_irq+0x2e/0x50 + [672.732][T736] ? sigprocmask+0xf2/0x340 + [672.737][T736] ? __fget_files+0x26a/0x480 + [672.732][T736] ? bpf_lsm_file_ioctl+0x9/0x10 + [672.738][T736] ? btrfs_ioctl_get_supported_features+0x50/0x50 + [672.736][T736] __x64_sys_ioctl+0x198/0x210 + [672.736][T736] do_syscall_64+0x39/0xb0 + [672.731][T736] entry_SYSCALL_64_after_hwframe+0x63/0xcd + [672.739][T736] RIP: 0033:0x4556ad + [672.742][T736] + [672.743][T736] + [672.748][T736] Allocated by task 27677: + [672.743][T736] kasan_save_stack+0x22/0x40 + [672.741][T736] kasan_set_track+0x25/0x30 + [672.741][T736] __kasan_kmalloc+0xa4/0xb0 + [672.749][T736] btrfs_alloc_root+0x48/0x90 + [672.746][T736] btrfs_create_tree+0x146/0xa20 + [672.744][T736] btrfs_quota_enable+0x461/0x1d20 + [672.743][T736] btrfs_ioctl+0x4a1c/0x5d80 + [672.747][T736] __x64_sys_ioctl+0x198/0x210 + [672.749][T736] do_syscall_64+0x39/0xb0 + [672.744][T736] entry_SYSCALL_64_after_hwframe+0x63/0xcd + [672.756][T736] + [672.757][T736] Freed by task 27677: + [672.759][T736] kasan_save_stack+0x22/0x40 + [672.759][T736] kasan_set_track+0x25/0x30 + [672.756][T736] kasan_save_free_info+0x2e/0x50 + [672.751][T736] ____kasan_slab_free+0x162/0x1c0 + [672.758][T736] slab_free_freelist_hook+0x89/0x1c0 + [672.752][T736] __kmem_cache_free+0xaf/0x2e0 + [672.752][T736] btrfs_put_root+0x1ff/0x2b0 + [672.759][T736] btrfs_quota_disable+0x80a/0xbc0 + [672.752][T736] btrfs_ioctl+0x3e5f/0x5d80 + [672.756][T736] __x64_sys_ioctl+0x198/0x210 + [672.753][T736] do_syscall_64+0x39/0xb0 + [672.765][T736] entry_SYSCALL_64_after_hwframe+0x63/0xcd + [672.769][T736] + [672.768][T736] The buggy address belongs to the object at ffff888022ec0000 + [672.768][T736] which belongs to the cache kmalloc-4k of size 4096 + [672.769][T736] The buggy address is located 520 bytes inside of + [672.769][T736] freed 4096-byte region [ffff888022ec0000, ffff888022ec1000) + [672.760][T736] + [672.764][T736] The buggy address belongs to the physical page: + [672.761][T736] page:ffffea00008bb000 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x22ec0 + [672.766][T736] head:ffffea00008bb000 order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0 + [672.779][T736] flags: 0xfff00000010200(slab|head|node=0|zone=1|lastcpupid=0x7ff) + [672.770][T736] raw: 00fff00000010200 ffff888012842140 ffffea000054ba00 dead000000000002 + [672.770][T736] raw: 0000000000000000 0000000000040004 00000001ffffffff 0000000000000000 + [672.771][T736] page dumped because: kasan: bad access detected + [672.778][T736] page_owner tracks the page as allocated + [672.777][T736] page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd2040(__GFP_IO|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 88 + [672.779][T736] get_page_from_freelist+0x119c/0x2d50 + [672.779][T736] __alloc_pages+0x1cb/0x4a0 + [672.776][T736] alloc_pages+0x1aa/0x270 + [672.773][T736] allocate_slab+0x260/0x390 + [672.771][T736] ___slab_alloc+0xa9a/0x13e0 + [672.778][T736] __slab_alloc.constprop.0+0x56/0xb0 + [672.771][T736] __kmem_cache_alloc_node+0x136/0x320 + [672.789][T736] __kmalloc+0x4e/0x1a0 + [672.783][T736] tomoyo_realpath_from_path+0xc3/0x600 + [672.781][T736] tomoyo_path_perm+0x22f/0x420 + [672.782][T736] tomoyo_path_unlink+0x92/0xd0 + [672.780][T736] security_path_unlink+0xdb/0x150 + [672.788][T736] do_unlinkat+0x377/0x680 + [672.788][T736] __x64_sys_unlink+0xca/0x110 + [672.789][T736] do_syscall_64+0x39/0xb0 + [672.783][T736] entry_SYSCALL_64_after_hwframe+0x63/0xcd + [672.784][T736] page last free stack trace: + [672.787][T736] free_pcp_prepare+0x4e5/0x920 + [672.787][T736] free_unref_page+0x1d/0x4e0 + [672.784][T736] __unfreeze_partials+0x17c/0x1a0 + [672.797][T736] qlist_free_all+0x6a/0x180 + [672.796][T736] kasan_quarantine_reduce+0x189/0x1d0 + [672.797][T736] __kasan_slab_alloc+0x64/0x90 + [672.793][T736] kmem_cache_alloc+0x17c/0x3c0 + [672.799][T736] getname_flags.part.0+0x50/0x4e0 + [672.799][T736] getname_flags+0x9e/0xe0 + [672.792][T736] vfs_fstatat+0x77/0xb0 + [672.791][T736] __do_sys_newlstat+0x84/0x100 + [672.798][T736] do_syscall_64+0x39/0xb0 + [672.796][T736] entry_SYSCALL_64_after_hwframe+0x63/0xcd + [672.790][T736] + [672.791][T736] Memory state around the buggy address: + [672.799][T736] ffff888022ec0100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb + [672.805][T736] ffff888022ec0180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb + [672.802][T736] >ffff888022ec0200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb + [672.809][T736] ^ + [672.809][T736] ffff888022ec0280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb + [672.809][T736] ffff888022ec0300: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb + +Fix this by having the qgroup assign ioctl take the qgroup ioctl mutex +before calling btrfs_run_qgroups(), which is what all qgroup ioctls should +call. + +Reported-by: butt3rflyh4ck +Link: https://lore.kernel.org/linux-btrfs/CAFcO6XN3VD8ogmHwqRk4kbiwtpUSNySu2VAxN8waEPciCHJvMA@mail.gmail.com/ +CC: stable@vger.kernel.org # 5.10+ +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Harshvardhan Jha +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/ioctl.c | 2 ++ + fs/btrfs/qgroup.c | 11 ++++++++++- + 2 files changed, 12 insertions(+), 1 deletion(-) + +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -4917,7 +4917,9 @@ static long btrfs_ioctl_qgroup_assign(st + } + + /* update qgroup status and info */ ++ mutex_lock(&fs_info->qgroup_ioctl_lock); + err = btrfs_run_qgroups(trans); ++ mutex_unlock(&fs_info->qgroup_ioctl_lock); + if (err < 0) + btrfs_handle_fs_error(fs_info, err, + "failed to update qgroup status and info"); +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -2721,13 +2721,22 @@ cleanup: + } + + /* +- * called from commit_transaction. Writes all changed qgroups to disk. ++ * Writes all changed qgroups to disk. ++ * Called by the transaction commit path and the qgroup assign ioctl. + */ + int btrfs_run_qgroups(struct btrfs_trans_handle *trans) + { + struct btrfs_fs_info *fs_info = trans->fs_info; + int ret = 0; + ++ /* ++ * In case we are called from the qgroup assign ioctl, assert that we ++ * are holding the qgroup_ioctl_lock, otherwise we can race with a quota ++ * disable operation (ioctl) and access a freed quota root. ++ */ ++ if (trans->transaction->state != TRANS_STATE_COMMIT_DOING) ++ lockdep_assert_held(&fs_info->qgroup_ioctl_lock); ++ + if (!fs_info->quota_root) + return ret; + diff --git a/queue-5.4/btrfs-qgroup-remove-one-time-use-variables-for-quota_root-checks.patch b/queue-5.4/btrfs-qgroup-remove-one-time-use-variables-for-quota_root-checks.patch new file mode 100644 index 00000000000..b00ca2cadc1 --- /dev/null +++ b/queue-5.4/btrfs-qgroup-remove-one-time-use-variables-for-quota_root-checks.patch @@ -0,0 +1,173 @@ +From e3b0edd29737d44137fc7583a9c185abda6e23b8 Mon Sep 17 00:00:00 2001 +From: Marcos Paulo de Souza +Date: Mon, 25 Nov 2019 21:58:50 -0300 +Subject: btrfs: qgroup: remove one-time use variables for quota_root checks + +From: Marcos Paulo de Souza + +commit e3b0edd29737d44137fc7583a9c185abda6e23b8 upstream. + +Remove some variables that are set only to be checked later, and never +used. + +Reviewed-by: Qu Wenruo +Signed-off-by: Marcos Paulo de Souza +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Harshvardhan Jha +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/qgroup.c | 34 ++++++++++------------------------ + 1 file changed, 10 insertions(+), 24 deletions(-) + +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -1395,7 +1395,6 @@ int btrfs_add_qgroup_relation(struct btr + u64 dst) + { + struct btrfs_fs_info *fs_info = trans->fs_info; +- struct btrfs_root *quota_root; + struct btrfs_qgroup *parent; + struct btrfs_qgroup *member; + struct btrfs_qgroup_list *list; +@@ -1411,8 +1410,7 @@ int btrfs_add_qgroup_relation(struct btr + return -ENOMEM; + + mutex_lock(&fs_info->qgroup_ioctl_lock); +- quota_root = fs_info->quota_root; +- if (!quota_root) { ++ if (!fs_info->quota_root) { + ret = -EINVAL; + goto out; + } +@@ -1459,7 +1457,6 @@ static int __del_qgroup_relation(struct + u64 dst) + { + struct btrfs_fs_info *fs_info = trans->fs_info; +- struct btrfs_root *quota_root; + struct btrfs_qgroup *parent; + struct btrfs_qgroup *member; + struct btrfs_qgroup_list *list; +@@ -1472,8 +1469,7 @@ static int __del_qgroup_relation(struct + if (!tmp) + return -ENOMEM; + +- quota_root = fs_info->quota_root; +- if (!quota_root) { ++ if (!fs_info->quota_root) { + ret = -EINVAL; + goto out; + } +@@ -1539,11 +1535,11 @@ int btrfs_create_qgroup(struct btrfs_tra + int ret = 0; + + mutex_lock(&fs_info->qgroup_ioctl_lock); +- quota_root = fs_info->quota_root; +- if (!quota_root) { ++ if (!fs_info->quota_root) { + ret = -EINVAL; + goto out; + } ++ quota_root = fs_info->quota_root; + qgroup = find_qgroup_rb(fs_info, qgroupid); + if (qgroup) { + ret = -EEXIST; +@@ -1568,14 +1564,12 @@ out: + int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) + { + struct btrfs_fs_info *fs_info = trans->fs_info; +- struct btrfs_root *quota_root; + struct btrfs_qgroup *qgroup; + struct btrfs_qgroup_list *list; + int ret = 0; + + mutex_lock(&fs_info->qgroup_ioctl_lock); +- quota_root = fs_info->quota_root; +- if (!quota_root) { ++ if (!fs_info->quota_root) { + ret = -EINVAL; + goto out; + } +@@ -1617,7 +1611,6 @@ int btrfs_limit_qgroup(struct btrfs_tran + struct btrfs_qgroup_limit *limit) + { + struct btrfs_fs_info *fs_info = trans->fs_info; +- struct btrfs_root *quota_root; + struct btrfs_qgroup *qgroup; + int ret = 0; + /* Sometimes we would want to clear the limit on this qgroup. +@@ -1627,8 +1620,7 @@ int btrfs_limit_qgroup(struct btrfs_tran + const u64 CLEAR_VALUE = -1; + + mutex_lock(&fs_info->qgroup_ioctl_lock); +- quota_root = fs_info->quota_root; +- if (!quota_root) { ++ if (!fs_info->quota_root) { + ret = -EINVAL; + goto out; + } +@@ -2734,10 +2726,9 @@ cleanup: + int btrfs_run_qgroups(struct btrfs_trans_handle *trans) + { + struct btrfs_fs_info *fs_info = trans->fs_info; +- struct btrfs_root *quota_root = fs_info->quota_root; + int ret = 0; + +- if (!quota_root) ++ if (!fs_info->quota_root) + return ret; + + spin_lock(&fs_info->qgroup_lock); +@@ -3000,7 +2991,6 @@ static bool qgroup_check_limits(const st + static int qgroup_reserve(struct btrfs_root *root, u64 num_bytes, bool enforce, + enum btrfs_qgroup_rsv_type type) + { +- struct btrfs_root *quota_root; + struct btrfs_qgroup *qgroup; + struct btrfs_fs_info *fs_info = root->fs_info; + u64 ref_root = root->root_key.objectid; +@@ -3019,8 +3009,7 @@ static int qgroup_reserve(struct btrfs_r + enforce = false; + + spin_lock(&fs_info->qgroup_lock); +- quota_root = fs_info->quota_root; +- if (!quota_root) ++ if (!fs_info->quota_root) + goto out; + + qgroup = find_qgroup_rb(fs_info, ref_root); +@@ -3087,7 +3076,6 @@ void btrfs_qgroup_free_refroot(struct bt + u64 ref_root, u64 num_bytes, + enum btrfs_qgroup_rsv_type type) + { +- struct btrfs_root *quota_root; + struct btrfs_qgroup *qgroup; + struct ulist_node *unode; + struct ulist_iterator uiter; +@@ -3105,8 +3093,7 @@ void btrfs_qgroup_free_refroot(struct bt + } + spin_lock(&fs_info->qgroup_lock); + +- quota_root = fs_info->quota_root; +- if (!quota_root) ++ if (!fs_info->quota_root) + goto out; + + qgroup = find_qgroup_rb(fs_info, ref_root); +@@ -3997,7 +3984,6 @@ void __btrfs_qgroup_free_meta(struct btr + static void qgroup_convert_meta(struct btrfs_fs_info *fs_info, u64 ref_root, + int num_bytes) + { +- struct btrfs_root *quota_root = fs_info->quota_root; + struct btrfs_qgroup *qgroup; + struct ulist_node *unode; + struct ulist_iterator uiter; +@@ -4005,7 +3991,7 @@ static void qgroup_convert_meta(struct b + + if (num_bytes == 0) + return; +- if (!quota_root) ++ if (!fs_info->quota_root) + return; + + spin_lock(&fs_info->qgroup_lock); diff --git a/queue-5.4/btrfs-qgroup-return-enotconn-instead-of-einval-when-quotas-are-not-enabled.patch b/queue-5.4/btrfs-qgroup-return-enotconn-instead-of-einval-when-quotas-are-not-enabled.patch new file mode 100644 index 00000000000..6e6e51efcc1 --- /dev/null +++ b/queue-5.4/btrfs-qgroup-return-enotconn-instead-of-einval-when-quotas-are-not-enabled.patch @@ -0,0 +1,79 @@ +From 8a36e408d40606e21cd4e2dd9601004a67b14868 Mon Sep 17 00:00:00 2001 +From: Marcos Paulo de Souza +Date: Mon, 25 Nov 2019 21:58:51 -0300 +Subject: btrfs: qgroup: return ENOTCONN instead of EINVAL when quotas are not enabled + +From: Marcos Paulo de Souza + +commit 8a36e408d40606e21cd4e2dd9601004a67b14868 upstream. + +[PROBLEM] +qgroup create/remove code is currently returning EINVAL when the user +tries to create a qgroup on a subvolume without quota enabled. EINVAL is +already being used for too many error scenarios so that is hard to +depict what is the problem. + +[FIX] +Currently scrub and balance code return -ENOTCONN when the user tries to +cancel/pause and no scrub or balance is currently running for the +desired subvolume. Do the same here by returning -ENOTCONN when a user +tries to create/delete/assing/list a qgroup on a subvolume without quota +enabled. + +Reviewed-by: Qu Wenruo +Signed-off-by: Marcos Paulo de Souza +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Harshvardhan Jha +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/qgroup.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -1411,7 +1411,7 @@ int btrfs_add_qgroup_relation(struct btr + + mutex_lock(&fs_info->qgroup_ioctl_lock); + if (!fs_info->quota_root) { +- ret = -EINVAL; ++ ret = -ENOTCONN; + goto out; + } + member = find_qgroup_rb(fs_info, src); +@@ -1470,7 +1470,7 @@ static int __del_qgroup_relation(struct + return -ENOMEM; + + if (!fs_info->quota_root) { +- ret = -EINVAL; ++ ret = -ENOTCONN; + goto out; + } + +@@ -1536,7 +1536,7 @@ int btrfs_create_qgroup(struct btrfs_tra + + mutex_lock(&fs_info->qgroup_ioctl_lock); + if (!fs_info->quota_root) { +- ret = -EINVAL; ++ ret = -ENOTCONN; + goto out; + } + quota_root = fs_info->quota_root; +@@ -1570,7 +1570,7 @@ int btrfs_remove_qgroup(struct btrfs_tra + + mutex_lock(&fs_info->qgroup_ioctl_lock); + if (!fs_info->quota_root) { +- ret = -EINVAL; ++ ret = -ENOTCONN; + goto out; + } + +@@ -1621,7 +1621,7 @@ int btrfs_limit_qgroup(struct btrfs_tran + + mutex_lock(&fs_info->qgroup_ioctl_lock); + if (!fs_info->quota_root) { +- ret = -EINVAL; ++ ret = -ENOTCONN; + goto out; + } + diff --git a/queue-5.4/series b/queue-5.4/series index 3029a5f5c8d..da1c8cbd06d 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -81,3 +81,6 @@ dm-cache-policy-smq-ensure-io-doesn-t-prevent-cleaner-policy-progress.patch acpi-processor-perflib-use-the-no-limit-frequency-qos.patch acpi-processor-perflib-avoid-updating-frequency-qos-unnecessarily.patch cpufreq-intel_pstate-drop-acpi-_pss-states-table-patching.patch +btrfs-qgroup-remove-one-time-use-variables-for-quota_root-checks.patch +btrfs-qgroup-return-enotconn-instead-of-einval-when-quotas-are-not-enabled.patch +btrfs-fix-race-between-quota-disable-and-quota-assign-ioctls.patch