Re: [PATCH 2/3] scsi: fc_transport: Fix TOCTOU races and workqueue
From: Hannes Reinecke
Date: Mon Jul 06 2026 - 10:17:09 EST
On 4/9/26 5:12 PM, Kyle Mahlkuch wrote:
Fix the TOCTOU races in workqueue access, use READ_ONCE() inThis is wrong. The only way when this will become an issue is
fc_queue_work(), fc_flush_work(), fc_queue_devloss_work(), and
fc_flush_devloss().
The workqueue destruction in fc_remove_host() uses WRITE_ONCE() to set
the pointer to NULL to prevents new work, flushing the work queued
before NULL, then safely destroying it.
Signed-off-by: Thinh Tran <thinhtr@xxxxxxxxxxxxx>
Signed-off-by: Kyle Mahlkuch <kmahlkuc@xxxxxxxxxxxxx>
---
drivers/scsi/scsi_transport_fc.c | 36 ++++++++++++++++++++++----------
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/ scsi_transport_fc.c
index 3a821afee9bc..123b22b52640 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -2774,16 +2774,18 @@ EXPORT_SYMBOL(fc_release_transport);
static int
fc_queue_work(struct Scsi_Host *shost, struct work_struct *work)
{
- if (unlikely(!fc_host_work_q(shost))) {
+ struct workqueue_struct *wq = READ_ONCE(fc_host_work_q(shost));
+
+ if (unlikely(!wq)) {
printk(KERN_ERR
"ERROR: FC host '%s' attempted to queue work, "
"when no workqueue created.\n", shost->hostt->name);
dump_stack();
-
return -EINVAL;
}
- return queue_work(fc_host_work_q(shost), work);
+ /* Use local copy to prevent TOCTOU race */
+ return queue_work(wq, work);
}
if someone calls fc_queue_work() after the queue has been deleted,
ie when the Scsi Host is in the process of being shutdown.
And I would argue that it's a programming error if some driver would
scheduled workqueue elements at that time; the driver should know that
the host is about to be terminated, and should not try to schedule
anything here.
Seeing that every driver is using this sequence:
fc_remove_host()
scsi_remove_host()
and scsi_remove_host() is setting the 'SHOST_CANCEL' state
it would be far better to set the 'SHOST_CANCEL' state first and
then call fc_remove_host().
Then we can have an easy check in fc_queue_work() to disallow any
new elements when the host state is SHOST_CANCEL (or SHOST_DELETE).
I'll send a patch.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@xxxxxxx +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich