From: Ranjan Kumar <ranjan.kumar@broadcom.com>
To: linux-scsi@vger.kernel.org, martin.petersen@oracle.com
Cc: sathya.prakash@broadcom.com, chandrakanth.patil@broadcom.com,
vishakhavc@google.com, ipylypiv@google.com,
Ranjan Kumar <ranjan.kumar@broadcom.com>,
Sashiko <sashiko-bot@kernel.org>
Subject: [PATCH v3 07/10] mpi3mr: Fix firmware event reference leak during cleanup
Date: Fri, 24 Jul 2026 15:55:02 +0530 [thread overview]
Message-ID: <20260724102505.115136-8-ranjan.kumar@broadcom.com> (raw)
In-Reply-To: <20260724102505.115136-1-ranjan.kumar@broadcom.com>
During firmware event cleanup, when an event is currently executing or
pending at the SCSI mid-layer, the driver sets a discard flag and exits
the cleanup routine early. This early exit skips the normal cancel path,
resulting in the firmware event reference count not being decremented,
leading to a reference leak.
Further analysis of the firmware event handling revealed and fixed
additional concurrency issues:
1. TOCTOU Race: mpi3mr_cleanup_fwevt_list() read current_event
locklessly, allowing the worker thread to free it concurrently.
Fix this by safely acquiring the reference under fwevt_lock.
2. Use-After-Free: mpi3mr_dequeue_fwevt() dropped the event reference
before returning it to the caller. Remove this drop and add a
balancing put to the end of mpi3mr_cancel_work() so the caller
retains the reference during cancellation.
3. Soft Lockup/Deadlock: mpi3mr_fwevt_bh() temporarily dropped
fwevt_lock while moving an event from the list to current_event.
This race window allowed driver unload (rmmod) to intervene and
deadlock. Fix this by inlining the list deletion so the lock is
held continuously.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260626114109.43685-1-ranjan.kumar@broadcom.com?part=7
Closes: https://sashiko.dev/#/patchset/20260708183305.244485-1-ranjan.kumar@broadcom.com?part=7
Signed-off-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
---
drivers/scsi/mpi3mr/mpi3mr_os.c | 84 +++++++++++++++++++--------------
1 file changed, 48 insertions(+), 36 deletions(-)
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
index 39624fae9131..545570d490fa 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_os.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -282,32 +282,6 @@ void mpi3mr_hdb_trigger_data_event(struct mpi3mr_ioc *mrioc,
mpi3mr_fwevt_add_to_list(mrioc, fwevt);
}
-/**
- * mpi3mr_fwevt_del_from_list - Delete firmware event from list
- * @mrioc: Adapter instance reference
- * @fwevt: Firmware event reference
- *
- * Delete the given firmware event from the firmware event list.
- *
- * Return: Nothing.
- */
-static void mpi3mr_fwevt_del_from_list(struct mpi3mr_ioc *mrioc,
- struct mpi3mr_fwevt *fwevt)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&mrioc->fwevt_lock, flags);
- if (!list_empty(&fwevt->list)) {
- list_del_init(&fwevt->list);
- /*
- * Put fwevt reference count after
- * removing it from fwevt_list
- */
- mpi3mr_fwevt_put(fwevt);
- }
- spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
-}
-
/**
* mpi3mr_dequeue_fwevt - Dequeue firmware event from the list
* @mrioc: Adapter instance reference
@@ -327,11 +301,7 @@ static struct mpi3mr_fwevt *mpi3mr_dequeue_fwevt(
fwevt = list_first_entry(&mrioc->fwevt_list,
struct mpi3mr_fwevt, list);
list_del_init(&fwevt->list);
- /*
- * Put fwevt reference count after
- * removing it from fwevt_list
- */
- mpi3mr_fwevt_put(fwevt);
+
}
spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
@@ -365,6 +335,11 @@ static void mpi3mr_cancel_work(struct mpi3mr_fwevt *fwevt)
*/
mpi3mr_fwevt_put(fwevt);
}
+
+ /*
+ * Drop the reference count that was acquired by the caller.
+ */
+ mpi3mr_fwevt_put(fwevt);
}
/**
@@ -379,16 +354,39 @@ static void mpi3mr_cancel_work(struct mpi3mr_fwevt *fwevt)
void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc)
{
struct mpi3mr_fwevt *fwevt = NULL;
+ unsigned long flags;
+ /*
+ * Safely read current_event under lock to prevent TOCTOU race
+ * with the firmware event worker thread.
+ */
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
if ((list_empty(&mrioc->fwevt_list) && !mrioc->current_event) ||
- !mrioc->fwevt_worker_thread)
+ !mrioc->fwevt_worker_thread) {
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
return;
+ }
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
while ((fwevt = mpi3mr_dequeue_fwevt(mrioc)))
mpi3mr_cancel_work(fwevt);
- if (mrioc->current_event) {
- fwevt = mrioc->current_event;
+ /*
+ * Safely read current_event under lock to prevent TOCTOU race
+ * with the firmware event worker thread.
+ */
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ fwevt = mrioc->current_event;
+ if (fwevt) {
+ /*
+ * Take a reference to ensure the event is not freed by the
+ * worker thread while we are evaluating or cancelling it.
+ */
+ mpi3mr_fwevt_get(fwevt);
+ }
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+
+ if (fwevt) {
/*
* Don't call cancel_work_sync() API for the
* fwevt work if the controller reset is
@@ -399,6 +397,7 @@ void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc)
*/
if (current_work() == &fwevt->work || fwevt->pending_at_sml) {
fwevt->discard = 1;
+ mpi3mr_fwevt_put(fwevt);
return;
}
@@ -2129,9 +2128,19 @@ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc,
u16 perst_id, handle, dev_info;
struct mpi3_device0_sas_sata_format *sasinf = NULL;
unsigned int timeout;
+ unsigned long flags;
- mpi3mr_fwevt_del_from_list(mrioc, fwevt);
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ if (!list_empty(&fwevt->list)) {
+ list_del_init(&fwevt->list);
+ /*
+ * Put fwevt reference count after
+ * removing it from fwevt_list
+ */
+ mpi3mr_fwevt_put(fwevt);
+ }
mrioc->current_event = fwevt;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
if (mrioc->stop_drv_processing) {
dprint_event_bh(mrioc, "ignoring event(0x%02x) in the bottom half handler\n"
@@ -2264,9 +2273,12 @@ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc,
mpi3mr_process_event_ack(mrioc, fwevt->event_id,
fwevt->evt_ctx);
out:
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ mrioc->current_event = NULL;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+
/* Put fwevt reference count to neutralize kref_init increment */
mpi3mr_fwevt_put(fwevt);
- mrioc->current_event = NULL;
}
/**
--
2.47.3
next prev parent reply other threads:[~2026-07-24 10:32 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-24 10:24 [PATCH v3 00/10] mpi3mr: Few Enhancements and minor fixes Ranjan Kumar
2026-07-24 10:24 ` [PATCH v3 01/10] mpi3mr: Skip device shutdown during unload per controller configuration Ranjan Kumar
2026-07-24 10:24 ` [PATCH v3 02/10] mpi3mr: Update MPI Headers to revision 41 Ranjan Kumar
2026-07-24 10:24 ` [PATCH v3 03/10] mpi3mr: Add early timestamp synchronization after driver load Ranjan Kumar
2026-07-24 10:24 ` [PATCH v3 04/10] mpi3mr: Fix NVMe page size caching for non-operational devices Ranjan Kumar
2026-07-24 10:44 ` sashiko-bot
2026-07-24 10:25 ` [PATCH v3 05/10] mpi3mr: Fix performance regression caused by extended IRQ poll sleep Ranjan Kumar
2026-07-24 10:50 ` sashiko-bot
2026-07-24 10:25 ` [PATCH v3 06/10] mpi3mr: Fix memory leak on operational queue creation failure Ranjan Kumar
2026-07-24 10:51 ` sashiko-bot
2026-07-24 10:25 ` Ranjan Kumar [this message]
2026-07-24 10:25 ` [PATCH v3 08/10] mpi3mr: Fix SAS port allocation and registration error handling Ranjan Kumar
2026-07-24 10:25 ` [PATCH v3 09/10] mpi3mr: Fix SAS PHY cleanup in host addition error paths Ranjan Kumar
2026-07-24 10:25 ` [PATCH v3 10/10] mpi3mr: Driver version update to 8.18.0.8.50 Ranjan Kumar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260724102505.115136-8-ranjan.kumar@broadcom.com \
--to=ranjan.kumar@broadcom.com \
--cc=chandrakanth.patil@broadcom.com \
--cc=ipylypiv@google.com \
--cc=linux-scsi@vger.kernel.org \
--cc=martin.petersen@oracle.com \
--cc=sashiko-bot@kernel.org \
--cc=sathya.prakash@broadcom.com \
--cc=vishakhavc@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox