[PATCH] scsi: aha152x: fix use-after-free of the bottom-half work
Fan Wu <[email protected]> Mon, 3 Aug 2026 08:03:45 +0000
| Newsgroups | org.kernel.vger.linux-scsi,org.kernel.vger.linux-kernel,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
The interrupt handler schedules a module-global work_struct that neither
aha152x_release() nor the scsi_add_host() failure path cancels before
freeing the host, so a worker armed by a just-returned interrupt can run
after the host is freed. Because the worker is shared across hosts,
draining it on one host's release cannot stop another host's interrupt
handler from re-arming it.
Make the work_struct per-host, initialize it once at probe, and cancel it
from both free paths. Each host's interrupt handler then arms only its own
work, so cancel_work_sync() is sufficient to drain it before the host is
freed.
This issue was found by an in-house static analysis tool.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: [email protected]
Assisted-by: Codex:gpt-5.6
Signed-off-by: Fan Wu <[email protected]>
---
drivers/scsi/aha152x.c | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c
index e3ccb6b..1616ea7 100644
--- a/drivers/scsi/aha152x.c
+++ b/drivers/scsi/aha152x.c
@@ -509,6 +509,9 @@ struct aha152x_hostdata {
struct pnp_dev *pnpdev;
#endif
struct list_head host_list;
+
+ struct work_struct work;
+ /* per-host bottom-half worker */
};
@@ -635,6 +638,7 @@ static struct {
/* setup & interrupt */
static irqreturn_t intr(int irq, void *dev_id);
+static void run(struct work_struct *work);
static void reset_ports(struct Scsi_Host *shpnt);
static void aha152x_error(struct Scsi_Host *shpnt, char *msg);
static void done(struct Scsi_Host *shpnt, unsigned char status_byte,
@@ -759,6 +763,7 @@ struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup)
}
spin_lock_init(&QLOCK);
+ INIT_WORK(&HOSTDATA(shpnt)->work, run);
RECONNECT = setup->reconnect;
SYNCHRONOUS = setup->synchronous;
PARITY = setup->parity;
@@ -856,6 +861,7 @@ struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup)
return shpnt;
out_host_put:
+ cancel_work_sync(&HOSTDATA(shpnt)->work);
list_del(&HOSTDATA(shpnt)->host_list);
scsi_host_put(shpnt);
@@ -870,6 +876,7 @@ void aha152x_release(struct Scsi_Host *shpnt)
scsi_remove_host(shpnt);
if (shpnt->irq)
free_irq(shpnt->irq, shpnt);
+ cancel_work_sync(&HOSTDATA(shpnt)->work);
#if !defined(AHA152X_PCMCIA)
if (shpnt->io_port)
@@ -1314,21 +1321,16 @@ static void done(struct Scsi_Host *shpnt, unsigned char status_byte,
printk(KERN_ERR "aha152x: done() called outside of command\n");
}
-static struct work_struct aha152x_tq;
-
/*
* Run service completions on the card with interrupts enabled.
*
*/
static void run(struct work_struct *work)
{
- struct aha152x_hostdata *hd;
+ struct aha152x_hostdata *hd = container_of(work, struct aha152x_hostdata, work);
+ struct Scsi_Host *shost = container_of((void *)hd, struct Scsi_Host, hostdata);
- list_for_each_entry(hd, &aha152x_host_list, host_list) {
- struct Scsi_Host *shost = container_of((void *)hd, struct Scsi_Host, hostdata);
-
- is_complete(shost);
- }
+ is_complete(shost);
}
/*
@@ -1371,8 +1373,7 @@ static irqreturn_t intr(int irqno, void *dev_id)
HOSTDATA(shpnt)->service=1;
/* Poke the BH handler */
- INIT_WORK(&aha152x_tq, run);
- schedule_work(&aha152x_tq);
+ schedule_work(&HOSTDATA(shpnt)->work);
}
DO_UNLOCK(flags);
--
2.34.1