From: Greg Kroah-Hartman Date: Wed, 4 Jan 2023 14:01:49 +0000 (+0100) Subject: 4.14-stable patches X-Git-Tag: v6.1.4~62 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ba6ca11de70fec6b73e5bcca0eb3257a818faa61;p=thirdparty%2Fkernel%2Fstable-queue.git 4.14-stable patches added patches: dm-cache-fix-abba-deadlock-between-shrink_slab-and-dm_cache_metadata_abort.patch dm-cache-fix-uaf-in-destroy.patch dm-cache-set-needs_check-flag-after-aborting-metadata.patch dm-thin-fix-uaf-in-run_timer_softirq.patch dm-thin-use-last-transaction-s-pmd-root-when-commit-failed.patch --- diff --git a/queue-4.14/dm-cache-fix-abba-deadlock-between-shrink_slab-and-dm_cache_metadata_abort.patch b/queue-4.14/dm-cache-fix-abba-deadlock-between-shrink_slab-and-dm_cache_metadata_abort.patch new file mode 100644 index 00000000000..e08d415c42c --- /dev/null +++ b/queue-4.14/dm-cache-fix-abba-deadlock-between-shrink_slab-and-dm_cache_metadata_abort.patch @@ -0,0 +1,114 @@ +From 352b837a5541690d4f843819028cf2b8be83d424 Mon Sep 17 00:00:00 2001 +From: Mike Snitzer +Date: Wed, 30 Nov 2022 13:26:32 -0500 +Subject: dm cache: Fix ABBA deadlock between shrink_slab and dm_cache_metadata_abort + +From: Mike Snitzer + +commit 352b837a5541690d4f843819028cf2b8be83d424 upstream. + +Same ABBA deadlock pattern fixed in commit 4b60f452ec51 ("dm thin: Fix +ABBA deadlock between shrink_slab and dm_pool_abort_metadata") to +DM-cache's metadata. + +Reported-by: Zhihao Cheng +Cc: stable@vger.kernel.org +Fixes: 028ae9f76f29 ("dm cache: add fail io mode and needs_check flag") +Signed-off-by: Mike Snitzer +Signed-off-by: Greg Kroah-Hartman +--- + drivers/md/dm-cache-metadata.c | 54 +++++++++++++++++++++++++++++++++++------ + 1 file changed, 47 insertions(+), 7 deletions(-) + +--- a/drivers/md/dm-cache-metadata.c ++++ b/drivers/md/dm-cache-metadata.c +@@ -550,11 +550,13 @@ static int __create_persistent_data_obje + return r; + } + +-static void __destroy_persistent_data_objects(struct dm_cache_metadata *cmd) ++static void __destroy_persistent_data_objects(struct dm_cache_metadata *cmd, ++ bool destroy_bm) + { + dm_sm_destroy(cmd->metadata_sm); + dm_tm_destroy(cmd->tm); +- dm_block_manager_destroy(cmd->bm); ++ if (destroy_bm) ++ dm_block_manager_destroy(cmd->bm); + } + + typedef unsigned long (*flags_mutator)(unsigned long); +@@ -825,7 +827,7 @@ static struct dm_cache_metadata *lookup_ + cmd2 = lookup(bdev); + if (cmd2) { + mutex_unlock(&table_lock); +- __destroy_persistent_data_objects(cmd); ++ __destroy_persistent_data_objects(cmd, true); + kfree(cmd); + return cmd2; + } +@@ -873,7 +875,7 @@ void dm_cache_metadata_close(struct dm_c + mutex_unlock(&table_lock); + + if (!cmd->fail_io) +- __destroy_persistent_data_objects(cmd); ++ __destroy_persistent_data_objects(cmd, true); + kfree(cmd); + } + } +@@ -1807,14 +1809,52 @@ int dm_cache_metadata_needs_check(struct + + int dm_cache_metadata_abort(struct dm_cache_metadata *cmd) + { +- int r; ++ int r = -EINVAL; ++ struct dm_block_manager *old_bm = NULL, *new_bm = NULL; ++ ++ /* fail_io is double-checked with cmd->root_lock held below */ ++ if (unlikely(cmd->fail_io)) ++ return r; ++ ++ /* ++ * Replacement block manager (new_bm) is created and old_bm destroyed outside of ++ * cmd root_lock to avoid ABBA deadlock that would result (due to life-cycle of ++ * shrinker associated with the block manager's bufio client vs cmd root_lock). ++ * - must take shrinker_rwsem without holding cmd->root_lock ++ */ ++ new_bm = dm_block_manager_create(cmd->bdev, DM_CACHE_METADATA_BLOCK_SIZE << SECTOR_SHIFT, ++ CACHE_MAX_CONCURRENT_LOCKS); + + WRITE_LOCK(cmd); +- __destroy_persistent_data_objects(cmd); +- r = __create_persistent_data_objects(cmd, false); ++ if (cmd->fail_io) { ++ WRITE_UNLOCK(cmd); ++ goto out; ++ } ++ ++ __destroy_persistent_data_objects(cmd, false); ++ old_bm = cmd->bm; ++ if (IS_ERR(new_bm)) { ++ DMERR("could not create block manager during abort"); ++ cmd->bm = NULL; ++ r = PTR_ERR(new_bm); ++ goto out_unlock; ++ } ++ ++ cmd->bm = new_bm; ++ r = __open_or_format_metadata(cmd, false); ++ if (r) { ++ cmd->bm = NULL; ++ goto out_unlock; ++ } ++ new_bm = NULL; ++out_unlock: + if (r) + cmd->fail_io = true; + WRITE_UNLOCK(cmd); ++ dm_block_manager_destroy(old_bm); ++out: ++ if (new_bm && !IS_ERR(new_bm)) ++ dm_block_manager_destroy(new_bm); + + return r; + } diff --git a/queue-4.14/dm-cache-fix-uaf-in-destroy.patch b/queue-4.14/dm-cache-fix-uaf-in-destroy.patch new file mode 100644 index 00000000000..08edae0d963 --- /dev/null +++ b/queue-4.14/dm-cache-fix-uaf-in-destroy.patch @@ -0,0 +1,33 @@ +From 6a459d8edbdbe7b24db42a5a9f21e6aa9e00c2aa Mon Sep 17 00:00:00 2001 +From: Luo Meng +Date: Tue, 29 Nov 2022 10:48:49 +0800 +Subject: dm cache: Fix UAF in destroy() + +From: Luo Meng + +commit 6a459d8edbdbe7b24db42a5a9f21e6aa9e00c2aa upstream. + +Dm_cache also has the same UAF problem when dm_resume() +and dm_destroy() are concurrent. + +Therefore, cancelling timer again in destroy(). + +Cc: stable@vger.kernel.org +Fixes: c6b4fcbad044e ("dm: add cache target") +Signed-off-by: Luo Meng +Signed-off-by: Mike Snitzer +Signed-off-by: Greg Kroah-Hartman +--- + drivers/md/dm-cache-target.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/md/dm-cache-target.c ++++ b/drivers/md/dm-cache-target.c +@@ -2034,6 +2034,7 @@ static void destroy(struct cache *cache) + if (cache->prison) + dm_bio_prison_destroy_v2(cache->prison); + ++ cancel_delayed_work_sync(&cache->waker); + if (cache->wq) + destroy_workqueue(cache->wq); + diff --git a/queue-4.14/dm-cache-set-needs_check-flag-after-aborting-metadata.patch b/queue-4.14/dm-cache-set-needs_check-flag-after-aborting-metadata.patch new file mode 100644 index 00000000000..fe9c6feed3d --- /dev/null +++ b/queue-4.14/dm-cache-set-needs_check-flag-after-aborting-metadata.patch @@ -0,0 +1,47 @@ +From 6b9973861cb2e96dcd0bb0f1baddc5c034207c5c Mon Sep 17 00:00:00 2001 +From: Mike Snitzer +Date: Wed, 30 Nov 2022 14:02:47 -0500 +Subject: dm cache: set needs_check flag after aborting metadata + +From: Mike Snitzer + +commit 6b9973861cb2e96dcd0bb0f1baddc5c034207c5c upstream. + +Otherwise the commit that will be aborted will be associated with the +metadata objects that will be torn down. Must write needs_check flag +to metadata with a reset block manager. + +Found through code-inspection (and compared against dm-thin.c). + +Cc: stable@vger.kernel.org +Fixes: 028ae9f76f29 ("dm cache: add fail io mode and needs_check flag") +Signed-off-by: Mike Snitzer +Signed-off-by: Greg Kroah-Hartman +--- + drivers/md/dm-cache-target.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/md/dm-cache-target.c ++++ b/drivers/md/dm-cache-target.c +@@ -1019,16 +1019,16 @@ static void abort_transaction(struct cac + if (get_cache_mode(cache) >= CM_READ_ONLY) + return; + +- if (dm_cache_metadata_set_needs_check(cache->cmd)) { +- DMERR("%s: failed to set 'needs_check' flag in metadata", dev_name); +- set_cache_mode(cache, CM_FAIL); +- } +- + DMERR_LIMIT("%s: aborting current metadata transaction", dev_name); + if (dm_cache_metadata_abort(cache->cmd)) { + DMERR("%s: failed to abort metadata transaction", dev_name); + set_cache_mode(cache, CM_FAIL); + } ++ ++ if (dm_cache_metadata_set_needs_check(cache->cmd)) { ++ DMERR("%s: failed to set 'needs_check' flag in metadata", dev_name); ++ set_cache_mode(cache, CM_FAIL); ++ } + } + + static void metadata_operation_failed(struct cache *cache, const char *op, int r) diff --git a/queue-4.14/dm-thin-fix-uaf-in-run_timer_softirq.patch b/queue-4.14/dm-thin-fix-uaf-in-run_timer_softirq.patch new file mode 100644 index 00000000000..995784ca12f --- /dev/null +++ b/queue-4.14/dm-thin-fix-uaf-in-run_timer_softirq.patch @@ -0,0 +1,100 @@ +From 88430ebcbc0ec637b710b947738839848c20feff Mon Sep 17 00:00:00 2001 +From: Luo Meng +Date: Tue, 29 Nov 2022 10:48:47 +0800 +Subject: dm thin: Fix UAF in run_timer_softirq() + +From: Luo Meng + +commit 88430ebcbc0ec637b710b947738839848c20feff upstream. + +When dm_resume() and dm_destroy() are concurrent, it will +lead to UAF, as follows: + + BUG: KASAN: use-after-free in __run_timers+0x173/0x710 + Write of size 8 at addr ffff88816d9490f0 by task swapper/0/0 + + Call Trace: + + dump_stack_lvl+0x73/0x9f + print_report.cold+0x132/0xaa2 + _raw_spin_lock_irqsave+0xcd/0x160 + __run_timers+0x173/0x710 + kasan_report+0xad/0x110 + __run_timers+0x173/0x710 + __asan_store8+0x9c/0x140 + __run_timers+0x173/0x710 + call_timer_fn+0x310/0x310 + pvclock_clocksource_read+0xfa/0x250 + kvm_clock_read+0x2c/0x70 + kvm_clock_get_cycles+0xd/0x20 + ktime_get+0x5c/0x110 + lapic_next_event+0x38/0x50 + clockevents_program_event+0xf1/0x1e0 + run_timer_softirq+0x49/0x90 + __do_softirq+0x16e/0x62c + __irq_exit_rcu+0x1fa/0x270 + irq_exit_rcu+0x12/0x20 + sysvec_apic_timer_interrupt+0x8e/0xc0 + +One of the concurrency UAF can be shown as below: + + use free +do_resume | + __find_device_hash_cell | + dm_get | + atomic_inc(&md->holders) | + | dm_destroy + | __dm_destroy + | if (!dm_suspended_md(md)) + | atomic_read(&md->holders) + | msleep(1) + dm_resume | + __dm_resume | + dm_table_resume_targets | + pool_resume | + do_waker #add delay work | + dm_put | + atomic_dec(&md->holders) | + | dm_table_destroy + | pool_dtr + | __pool_dec + | __pool_destroy + | destroy_workqueue + | kfree(pool) # free pool + time out +__do_softirq + run_timer_softirq # pool has already been freed + +This can be easily reproduced using: + 1. create thin-pool + 2. dmsetup suspend pool + 3. dmsetup resume pool + 4. dmsetup remove_all # Concurrent with 3 + +The root cause of this UAF bug is that dm_resume() adds timer after +dm_destroy() skips cancelling the timer because of suspend status. +After timeout, it will call run_timer_softirq(), however pool has +already been freed. The concurrency UAF bug will happen. + +Therefore, cancelling timer again in __pool_destroy(). + +Cc: stable@vger.kernel.org +Fixes: 991d9fa02da0d ("dm: add thin provisioning target") +Signed-off-by: Luo Meng +Signed-off-by: Mike Snitzer +Signed-off-by: Greg Kroah-Hartman +--- + drivers/md/dm-thin.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/md/dm-thin.c ++++ b/drivers/md/dm-thin.c +@@ -2932,6 +2932,8 @@ static void __pool_destroy(struct pool * + dm_bio_prison_destroy(pool->prison); + dm_kcopyd_client_destroy(pool->copier); + ++ cancel_delayed_work_sync(&pool->waker); ++ cancel_delayed_work_sync(&pool->no_space_timeout); + if (pool->wq) + destroy_workqueue(pool->wq); + diff --git a/queue-4.14/dm-thin-use-last-transaction-s-pmd-root-when-commit-failed.patch b/queue-4.14/dm-thin-use-last-transaction-s-pmd-root-when-commit-failed.patch new file mode 100644 index 00000000000..53f2e1abfed --- /dev/null +++ b/queue-4.14/dm-thin-use-last-transaction-s-pmd-root-when-commit-failed.patch @@ -0,0 +1,84 @@ +From 7991dbff6849f67e823b7cc0c15e5a90b0549b9f Mon Sep 17 00:00:00 2001 +From: Zhihao Cheng +Date: Thu, 8 Dec 2022 22:28:02 +0800 +Subject: dm thin: Use last transaction's pmd->root when commit failed + +From: Zhihao Cheng + +commit 7991dbff6849f67e823b7cc0c15e5a90b0549b9f upstream. + +Recently we found a softlock up problem in dm thin pool btree lookup +code due to corrupted metadata: + + Kernel panic - not syncing: softlockup: hung tasks + CPU: 7 PID: 2669225 Comm: kworker/u16:3 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996) + Workqueue: dm-thin do_worker [dm_thin_pool] + Call Trace: + + dump_stack+0x9c/0xd3 + panic+0x35d/0x6b9 + watchdog_timer_fn.cold+0x16/0x25 + __run_hrtimer+0xa2/0x2d0 + + RIP: 0010:__relink_lru+0x102/0x220 [dm_bufio] + __bufio_new+0x11f/0x4f0 [dm_bufio] + new_read+0xa3/0x1e0 [dm_bufio] + dm_bm_read_lock+0x33/0xd0 [dm_persistent_data] + ro_step+0x63/0x100 [dm_persistent_data] + btree_lookup_raw.constprop.0+0x44/0x220 [dm_persistent_data] + dm_btree_lookup+0x16f/0x210 [dm_persistent_data] + dm_thin_find_block+0x12c/0x210 [dm_thin_pool] + __process_bio_read_only+0xc5/0x400 [dm_thin_pool] + process_thin_deferred_bios+0x1a4/0x4a0 [dm_thin_pool] + process_one_work+0x3c5/0x730 + +Following process may generate a broken btree mixed with fresh and +stale btree nodes, which could get dm thin trapped in an infinite loop +while looking up data block: + Transaction 1: pmd->root = A, A->B->C // One path in btree + pmd->root = X, X->Y->Z // Copy-up + Transaction 2: X,Z is updated on disk, Y write failed. + // Commit failed, dm thin becomes read-only. + process_bio_read_only + dm_thin_find_block + __find_block + dm_btree_lookup(pmd->root) +The pmd->root points to a broken btree, Y may contain stale node +pointing to any block, for example X, which gets dm thin trapped into +a dead loop while looking up Z. + +Fix this by setting pmd->root in __open_metadata(), so that dm thin +will use the last transaction's pmd->root if commit failed. + +Fetch a reproducer in [Link]. + +Linke: https://bugzilla.kernel.org/show_bug.cgi?id=216790 +Cc: stable@vger.kernel.org +Fixes: 991d9fa02da0 ("dm: add thin provisioning target") +Signed-off-by: Zhihao Cheng +Acked-by: Joe Thornber +Signed-off-by: Mike Snitzer +Signed-off-by: Greg Kroah-Hartman +--- + drivers/md/dm-thin-metadata.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/drivers/md/dm-thin-metadata.c ++++ b/drivers/md/dm-thin-metadata.c +@@ -660,6 +660,15 @@ static int __open_metadata(struct dm_poo + goto bad_cleanup_data_sm; + } + ++ /* ++ * For pool metadata opening process, root setting is redundant ++ * because it will be set again in __begin_transaction(). But dm ++ * pool aborting process really needs to get last transaction's ++ * root to avoid accessing broken btree. ++ */ ++ pmd->root = le64_to_cpu(disk_super->data_mapping_root); ++ pmd->details_root = le64_to_cpu(disk_super->device_details_root); ++ + __setup_btree_details(pmd); + dm_bm_unlock(sblock); + diff --git a/queue-4.14/series b/queue-4.14/series index 9e63338deaf..2094f1bb058 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -274,3 +274,8 @@ media-stv0288-use-explicitly-signed-char.patch ktest.pl-minconfig-unset-configs-instead-of-just-removing-them.patch arm-ux500-do-not-directly-dereference-__iomem.patch selftests-use-optional-usercflags-and-userldflags.patch +dm-cache-fix-abba-deadlock-between-shrink_slab-and-dm_cache_metadata_abort.patch +dm-thin-use-last-transaction-s-pmd-root-when-commit-failed.patch +dm-thin-fix-uaf-in-run_timer_softirq.patch +dm-cache-fix-uaf-in-destroy.patch +dm-cache-set-needs_check-flag-after-aborting-metadata.patch