From: Greg Kroah-Hartman Date: Thu, 12 Mar 2026 20:08:41 +0000 (+0100) Subject: 6.18-stable patches X-Git-Tag: v6.12.77~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=82a38678be77e9e477308f3ecf1a27836245e3cd;p=thirdparty%2Fkernel%2Fstable-queue.git 6.18-stable patches added patches: ata-libata-cancel-pending-work-after-clearing-deferred_qc.patch --- diff --git a/queue-6.18/ata-libata-cancel-pending-work-after-clearing-deferred_qc.patch b/queue-6.18/ata-libata-cancel-pending-work-after-clearing-deferred_qc.patch new file mode 100644 index 0000000000..756d27b1e8 --- /dev/null +++ b/queue-6.18/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 +@@ -659,6 +659,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 +@@ -1698,6 +1698,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.18/series b/queue-6.18/series index 7113305bcd..fad7ef8fc6 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -11,3 +11,4 @@ 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 +ata-libata-cancel-pending-work-after-clearing-deferred_qc.patch