From: Greg Kroah-Hartman Date: Thu, 12 Mar 2026 20:08:29 +0000 (+0100) Subject: 6.12-stable patches X-Git-Tag: v6.12.77~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f4fe89725c1e0473572211ce020bf5710c2c651c;p=thirdparty%2Fkernel%2Fstable-queue.git 6.12-stable patches added patches: ata-libata-cancel-pending-work-after-clearing-deferred_qc.patch ata-libata-core-fix-cancellation-of-a-port-deferred-qc-work.patch ata-libata-eh-correctly-handle-deferred-qc-timeouts.patch ext4-fix-potential-null-deref-in-ext4_mb_init.patch --- diff --git a/queue-6.12/ata-libata-cancel-pending-work-after-clearing-deferred_qc.patch b/queue-6.12/ata-libata-cancel-pending-work-after-clearing-deferred_qc.patch new file mode 100644 index 0000000000..18dbddeaef --- /dev/null +++ b/queue-6.12/ata-libata-cancel-pending-work-after-clearing-deferred_qc.patch @@ -0,0 +1,84 @@ +From aac9b27f7c1f2b2cf7f50a9ca633ecbbcaf22af9 Mon Sep 17 00:00:00 2001 +From: Niklas Cassel +Date: Tue, 3 Mar 2026 11:03:42 +0100 +Subject: ata: libata: cancel pending work after clearing deferred_qc + +From: Niklas Cassel + +commit aac9b27f7c1f2b2cf7f50a9ca633ecbbcaf22af9 upstream. + +Syzbot reported a WARN_ON() in ata_scsi_deferred_qc_work(), caused by +ap->ops->qc_defer() returning non-zero before issuing the deferred qc. + +ata_scsi_schedule_deferred_qc() is called during each command completion. +This function will check if there is a deferred QC, and if +ap->ops->qc_defer() returns zero, meaning that it is possible to queue the +deferred qc at this time (without being deferred), then it will queue the +work which will issue the deferred qc. + +Once the work get to run, which can potentially be a very long time after +the work was scheduled, there is a WARN_ON() if ap->ops->qc_defer() returns +non-zero. + +While we hold the ap->lock both when assigning and clearing deferred_qc, +and the work itself holds the ap->lock, the code currently does not cancel +the work after clearing the deferred qc. + +This means that the following scenario can happen: +1) One or several NCQ commands are queued. +2) A non-NCQ command is queued, gets stored in ap->deferred_qc. +3) Last NCQ command gets completed, work is queued to issue the deferred + qc. +4) Timeout or error happens, ap->deferred_qc is cleared. The queued work is + currently NOT canceled. +5) Port is reset. +6) One or several NCQ commands are queued. +7) A non-NCQ command is queued, gets stored in ap->deferred_qc. +8) Work is finally run. Yet at this time, there is still NCQ commands in + flight. + +The work in 8) really belongs to the non-NCQ command in 2), not to the +non-NCQ command in 7). The reason why the work is executed when it is not +supposed to, is because it was never canceled when ap->deferred_qc was +cleared in 4). Thus, ensure that we always cancel the work after clearing +ap->deferred_qc. + +Another potential fix would have been to let ata_scsi_deferred_qc_work() do +nothing if ap->ops->qc_defer() returns non-zero. However, canceling the +work when clearing ap->deferred_qc seems slightly more logical, as we hold +the ap->lock when clearing ap->deferred_qc, so we know that the work cannot +be holding the lock. (The function could be waiting for the lock, but that +is okay since it will do nothing if ap->deferred_qc is not set.) + +Reported-by: syzbot+bcaf842a1e8ead8dfb89@syzkaller.appspotmail.com +Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") +Fixes: eddb98ad9364 ("ata: libata-eh: correctly handle deferred qc timeouts") +Reviewed-by: Igor Pylypiv +Reviewed-by: Damien Le Moal +Signed-off-by: Niklas Cassel +Signed-off-by: Greg Kroah-Hartman +--- + drivers/ata/libata-eh.c | 1 + + drivers/ata/libata-scsi.c | 1 + + 2 files changed, 2 insertions(+) + +--- a/drivers/ata/libata-eh.c ++++ b/drivers/ata/libata-eh.c +@@ -661,6 +661,7 @@ void ata_scsi_cmd_error_handler(struct S + */ + WARN_ON_ONCE(qc->flags & ATA_QCFLAG_ACTIVE); + ap->deferred_qc = NULL; ++ cancel_work(&ap->deferred_qc_work); + set_host_byte(scmd, DID_TIME_OUT); + scsi_eh_finish_cmd(scmd, &ap->eh_done_q); + } else if (i < ATA_MAX_QUEUE) { +--- a/drivers/ata/libata-scsi.c ++++ b/drivers/ata/libata-scsi.c +@@ -1712,6 +1712,7 @@ void ata_scsi_requeue_deferred_qc(struct + + scmd = qc->scsicmd; + ap->deferred_qc = NULL; ++ cancel_work(&ap->deferred_qc_work); + ata_qc_free(qc); + scmd->result = (DID_SOFT_ERROR << 16); + scsi_done(scmd); diff --git a/queue-6.12/ata-libata-core-fix-cancellation-of-a-port-deferred-qc-work.patch b/queue-6.12/ata-libata-core-fix-cancellation-of-a-port-deferred-qc-work.patch new file mode 100644 index 0000000000..10e59f9664 --- /dev/null +++ b/queue-6.12/ata-libata-core-fix-cancellation-of-a-port-deferred-qc-work.patch @@ -0,0 +1,49 @@ +From 55db009926634b20955bd8abbee921adbc8d2cb4 Mon Sep 17 00:00:00 2001 +From: Damien Le Moal +Date: Fri, 20 Feb 2026 12:09:12 +0900 +Subject: ata: libata-core: fix cancellation of a port deferred qc work + +From: Damien Le Moal + +commit 55db009926634b20955bd8abbee921adbc8d2cb4 upstream. + +cancel_work_sync() is a sleeping function so it cannot be called with +the spin lock of a port being held. Move the call to this function in +ata_port_detach() after EH completes, with the port lock released, +together with other work cancellation calls. + +Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") +Signed-off-by: Damien Le Moal +Reviewed-by: Hannes Reinecke +Reviewed-by: Igor Pylypiv +Signed-off-by: Greg Kroah-Hartman +--- + drivers/ata/libata-core.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +--- a/drivers/ata/libata-core.c ++++ b/drivers/ata/libata-core.c +@@ -6132,10 +6132,6 @@ static void ata_port_detach(struct ata_p + } + } + +- /* Make sure the deferred qc work finished. */ +- cancel_work_sync(&ap->deferred_qc_work); +- WARN_ON(ap->deferred_qc); +- + /* Tell EH to disable all devices */ + ap->pflags |= ATA_PFLAG_UNLOADING; + ata_port_schedule_eh(ap); +@@ -6146,9 +6142,11 @@ static void ata_port_detach(struct ata_p + /* wait till EH commits suicide */ + ata_port_wait_eh(ap); + +- /* it better be dead now */ ++ /* It better be dead now and not have any remaining deferred qc. */ + WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED)); ++ WARN_ON(ap->deferred_qc); + ++ cancel_work_sync(&ap->deferred_qc_work); + cancel_delayed_work_sync(&ap->hotplug_task); + cancel_delayed_work_sync(&ap->scsi_rescan_task); + diff --git a/queue-6.12/ata-libata-eh-correctly-handle-deferred-qc-timeouts.patch b/queue-6.12/ata-libata-eh-correctly-handle-deferred-qc-timeouts.patch new file mode 100644 index 0000000000..76aa4c74f5 --- /dev/null +++ b/queue-6.12/ata-libata-eh-correctly-handle-deferred-qc-timeouts.patch @@ -0,0 +1,84 @@ +From eddb98ad9364b4e778768785d46cfab04ce52100 Mon Sep 17 00:00:00 2001 +From: Damien Le Moal +Date: Fri, 20 Feb 2026 13:43:00 +0900 +Subject: ata: libata-eh: correctly handle deferred qc timeouts + +From: Damien Le Moal + +commit eddb98ad9364b4e778768785d46cfab04ce52100 upstream. + +A deferred qc may timeout while waiting for the device queue to drain +to be submitted. In such case, since the qc is not active, +ata_scsi_cmd_error_handler() ends up calling scsi_eh_finish_cmd(), +which frees the qc. But as the port deferred_qc field still references +this finished/freed qc, the deferred qc work may eventually attempt to +call ata_qc_issue() against this invalid qc, leading to errors such as +reported by UBSAN (syzbot run): + +UBSAN: shift-out-of-bounds in drivers/ata/libata-core.c:5166:24 +shift exponent 4210818301 is too large for 64-bit type 'long long unsigned int' +... +Call Trace: + + __dump_stack lib/dump_stack.c:94 [inline] + dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120 + ubsan_epilogue+0xa/0x30 lib/ubsan.c:233 + __ubsan_handle_shift_out_of_bounds+0x279/0x2a0 lib/ubsan.c:494 + ata_qc_issue.cold+0x38/0x9f drivers/ata/libata-core.c:5166 + ata_scsi_deferred_qc_work+0x154/0x1f0 drivers/ata/libata-scsi.c:1679 + process_one_work+0x9d7/0x1920 kernel/workqueue.c:3275 + process_scheduled_works kernel/workqueue.c:3358 [inline] + worker_thread+0x5da/0xe40 kernel/workqueue.c:3439 + kthread+0x370/0x450 kernel/kthread.c:467 + ret_from_fork+0x754/0xd80 arch/x86/kernel/process.c:158 + ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 + + +Fix this by checking if the qc of a timed out SCSI command is a deferred +one, and in such case, clear the port deferred_qc field and finish the +SCSI command with DID_TIME_OUT. + +Reported-by: syzbot+1f77b8ca15336fff21ff@syzkaller.appspotmail.com +Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") +Signed-off-by: Damien Le Moal +Reviewed-by: Hannes Reinecke +Reviewed-by: Igor Pylypiv +Signed-off-by: Greg Kroah-Hartman +--- + drivers/ata/libata-eh.c | 22 +++++++++++++++++++--- + 1 file changed, 19 insertions(+), 3 deletions(-) + +--- a/drivers/ata/libata-eh.c ++++ b/drivers/ata/libata-eh.c +@@ -642,12 +642,28 @@ void ata_scsi_cmd_error_handler(struct S + set_host_byte(scmd, DID_OK); + + ata_qc_for_each_raw(ap, qc, i) { +- if (qc->flags & ATA_QCFLAG_ACTIVE && +- qc->scsicmd == scmd) ++ if (qc->scsicmd != scmd) ++ continue; ++ if ((qc->flags & ATA_QCFLAG_ACTIVE) || ++ qc == ap->deferred_qc) + break; + } + +- if (i < ATA_MAX_QUEUE) { ++ if (qc == ap->deferred_qc) { ++ /* ++ * This is a deferred command that timed out while ++ * waiting for the command queue to drain. Since the qc ++ * is not active yet (deferred_qc is still set, so the ++ * deferred qc work has not issued the command yet), ++ * simply signal the timeout by finishing the SCSI ++ * command and clear the deferred qc to prevent the ++ * deferred qc work from issuing this qc. ++ */ ++ WARN_ON_ONCE(qc->flags & ATA_QCFLAG_ACTIVE); ++ ap->deferred_qc = NULL; ++ set_host_byte(scmd, DID_TIME_OUT); ++ scsi_eh_finish_cmd(scmd, &ap->eh_done_q); ++ } else if (i < ATA_MAX_QUEUE) { + /* the scmd has an associated qc */ + if (!(qc->flags & ATA_QCFLAG_EH)) { + /* which hasn't failed yet, timeout */ diff --git a/queue-6.12/ext4-fix-potential-null-deref-in-ext4_mb_init.patch b/queue-6.12/ext4-fix-potential-null-deref-in-ext4_mb_init.patch new file mode 100644 index 0000000000..e5aef3d44b --- /dev/null +++ b/queue-6.12/ext4-fix-potential-null-deref-in-ext4_mb_init.patch @@ -0,0 +1,81 @@ +From 3c3fac6bc0a9c00dbe65d8dc0d3a282afe4d3188 Mon Sep 17 00:00:00 2001 +From: Baokun Li +Date: Mon, 25 Aug 2025 11:38:30 +0800 +Subject: ext4: fix potential null deref in ext4_mb_init() + +From: Baokun Li + +commit 3c3fac6bc0a9c00dbe65d8dc0d3a282afe4d3188 upstream. + +In ext4_mb_init(), ext4_mb_avg_fragment_size_destroy() may be called +when sbi->s_mb_avg_fragment_size remains uninitialized (e.g., if groupinfo +slab cache allocation fails). Since ext4_mb_avg_fragment_size_destroy() +lacks null pointer checking, this leads to a null pointer dereference. + +================================================================== +EXT4-fs: no memory for groupinfo slab cache +BUG: kernel NULL pointer dereference, address: 0000000000000000 +PGD 0 P4D 0 +Oops: Oops: 0002 [#1] SMP PTI +CPU:2 UID: 0 PID: 87 Comm:mount Not tainted 6.17.0-rc2 #1134 PREEMPT(none) +RIP: 0010:_raw_spin_lock_irqsave+0x1b/0x40 +Call Trace: + + xa_destroy+0x61/0x130 + ext4_mb_init+0x483/0x540 + __ext4_fill_super+0x116d/0x17b0 + ext4_fill_super+0xd3/0x280 + get_tree_bdev_flags+0x132/0x1d0 + vfs_get_tree+0x29/0xd0 + do_new_mount+0x197/0x300 + __x64_sys_mount+0x116/0x150 + do_syscall_64+0x50/0x1c0 + entry_SYSCALL_64_after_hwframe+0x76/0x7e +================================================================== + +Therefore, add necessary null check to ext4_mb_avg_fragment_size_destroy() +to prevent this issue. The same fix is also applied to +ext4_mb_largest_free_orders_destroy(). + +Reported-by: syzbot+1713b1aa266195b916c2@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=1713b1aa266195b916c2 +Cc: stable@kernel.org +Fixes: f7eaacbb4e54 ("ext4: convert free groups order lists to xarrays") +Signed-off-by: Baokun Li +Reviewed-by: Zhang Yi +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Theodore Ts'o +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/mballoc.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/fs/ext4/mballoc.c ++++ b/fs/ext4/mballoc.c +@@ -3678,16 +3678,26 @@ static void ext4_discard_work(struct wor + + static inline void ext4_mb_avg_fragment_size_destroy(struct ext4_sb_info *sbi) + { ++ if (!sbi->s_mb_avg_fragment_size) ++ return; ++ + for (int i = 0; i < MB_NUM_ORDERS(sbi->s_sb); i++) + xa_destroy(&sbi->s_mb_avg_fragment_size[i]); ++ + kfree(sbi->s_mb_avg_fragment_size); ++ sbi->s_mb_avg_fragment_size = NULL; + } + + static inline void ext4_mb_largest_free_orders_destroy(struct ext4_sb_info *sbi) + { ++ if (!sbi->s_mb_largest_free_orders) ++ return; ++ + for (int i = 0; i < MB_NUM_ORDERS(sbi->s_sb); i++) + xa_destroy(&sbi->s_mb_largest_free_orders[i]); ++ + kfree(sbi->s_mb_largest_free_orders); ++ sbi->s_mb_largest_free_orders = NULL; + } + + int ext4_mb_init(struct super_block *sb) diff --git a/queue-6.12/series b/queue-6.12/series index 9d4feddaf5..a209b1c19d 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -258,3 +258,7 @@ apparmor-fix-unprivileged-local-user-can-do-privileged-policy-management.patch apparmor-fix-differential-encoding-verification.patch apparmor-fix-race-on-rawdata-dereference.patch apparmor-fix-race-between-freeing-data-and-fs-accessing-it.patch +ext4-fix-potential-null-deref-in-ext4_mb_init.patch +ata-libata-core-fix-cancellation-of-a-port-deferred-qc-work.patch +ata-libata-eh-correctly-handle-deferred-qc-timeouts.patch +ata-libata-cancel-pending-work-after-clearing-deferred_qc.patch