[PATCH] PCI: switchtec: Fix use-after-free in mrpc_timeout_work

Pei Xiao <[email protected]> Thu, 6 Aug 2026 19:55:15 +0800
Newsgroups org.kernel.vger.linux-pci,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <e640c79072065c1a347ed25f7ce0fe1ba565f4fe.1786017236.git.xiaopei01@kylinos.cn>
stdev_kill() cancels the works before clearing stdev->alive under
mrpc_mutex. Every path that arms mrpc_timeout holds that mutex, so a
write that already passed the alive check can still schedule
mrpc_timeout after cancel_delayed_work_sync() returns:

CPU0                                      CPU1

                                          | switchtec_dev_write()
                                          | lock_mutex_and_test_alive()
cancel_delayed_work_sync(mrpc_timeout)    |
                                          | mrpc_cmd_submit()
                                          | schedule_delayed_work(mrpc_timeout)
                                          | mutex_unlock(&stdev->mrpc_mutex)
scoped_guard(mutex) { alive = false }     |
put_device() -> kfree(stdev)              |
                                          | mrpc_timeout_work

Fix it by clearing alive under mrpc_mutex before canceling the works, so no
new mrpc_timeout can be armed once alive is cleared.

Fixes: 080b47def5e5 ("MicroSemi Switchtec management interface driver")
Cc: [email protected]
Assisted-by: Codex:deepseek-v4-flash
Signed-off-by: Pei Xiao <[email protected]>
---
 drivers/pci/switch/switchtec.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c
index 235ca1877b6c..9127841b2bf8 100644
--- a/drivers/pci/switch/switchtec.c
+++ b/drivers/pci/switch/switchtec.c
@@ -1323,10 +1323,6 @@ static void stdev_kill(struct switchtec_dev *stdev)
 	if (stdev->dma_mrpc_irq >= 0)
 		devm_free_irq(&stdev->pdev->dev, stdev->dma_mrpc_irq, stdev);
 
-	cancel_work_sync(&stdev->mrpc_work);
-	cancel_work_sync(&stdev->link_event_work);
-	cancel_delayed_work_sync(&stdev->mrpc_timeout);
-
 	/* Mark the hardware as unavailable and complete all completions */
 	scoped_guard (mutex, &stdev->mrpc_mutex) {
 		stdev->alive = false;
@@ -1341,6 +1337,10 @@ static void stdev_kill(struct switchtec_dev *stdev)
 
 	}
 
+	cancel_work_sync(&stdev->mrpc_work);
+	cancel_work_sync(&stdev->link_event_work);
+	cancel_delayed_work_sync(&stdev->mrpc_timeout);
+
 	/* Wake up any users waiting on event_wq */
 	wake_up_interruptible(&stdev->event_wq);
 }
-- 
2.25.1