From b38af5386a74416e898955443f2e26547b358cb2 Mon Sep 17 00:00:00 2001 From: Yibo Tan Date: Tue, 15 Sep 2026 22:44:57 +0800 Subject: [PATCH 1/6] mmc: mtk-sd: Cancel request timeout work on remove The driver queues req_timeout while a request is active. msdc_request_done() uses cancel_delayed_work(), which does not wait for a timeout callback that has already started. The timeout callback calls mmc_request_done(), which wakes the request waiter, and then continues to use host->dev_comp and check the SDIO IRQ. During unbind, msdc_drv_remove() can return and the managed mmc_host can be freed before the callback finishes. KASAN reported use-after-free accesses in msdc_request_done() and msdc_recheck_sdio_irq() in each of three unbind tests. The same tests completed without a kernel diagnostic after this change. Call cancel_delayed_work_sync() after mmc_remove_host() has stopped new requests and before the driver releases the host resources. Fixes: 208489032bdd ("mmc: mediatek: Add Mediatek MMC driver") Cc: stable@vger.kernel.org Assisted-by: Codex:GPT-5 Signed-off-by: Yibo Tan Signed-off-by: Ulf Hansson --- drivers/mmc/host/mtk-sd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 01ea3adbdf3b..5b160fdad3f0 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -3216,6 +3216,7 @@ static void msdc_drv_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); mmc_remove_host(mmc); + cancel_delayed_work_sync(&host->req_timeout); msdc_deinit_hw(host); msdc_gate_clock(host); From e1c6c10a6a70a69e29b04fcc1ebe4b9bcbc7a49a Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 16 Sep 2026 00:19:47 +0800 Subject: [PATCH 2/6] mmc: sdhci-sprd: disable runtime PM on remove sdhci_sprd_probe() enables runtime PM, while sdhci_sprd_remove() does not perform the corresponding runtime PM cleanup. The probe failure path disables runtime PM before disabling the clocks, but the normal remove path directly disables clocks that are also managed by the runtime PM callbacks. If the device is runtime suspended, those clocks may already be disabled. Resume the device before removal, disable runtime PM and drop the temporary runtime PM reference before disabling the clocks. This issue was found by manual code inspection. Fixes: fb8bd90f83c4 ("mmc: sdhci-sprd: Add Spreadtrum's initial host controller") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Reviewed-by: Baolin Wang Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-sprd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c index 3584a2b314a9..0c8f389fe584 100644 --- a/drivers/mmc/host/sdhci-sprd.c +++ b/drivers/mmc/host/sdhci-sprd.c @@ -890,6 +890,10 @@ static void sdhci_sprd_remove(struct platform_device *pdev) struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); + pm_runtime_get_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + sdhci_remove_host(host, 0); clk_disable_unprepare(sprd_host->clk_sdio); From 0f4d4424fc1d651ec3fa63a0a8060ba9087eb0ec Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Tue, 15 Sep 2026 23:57:24 +0800 Subject: [PATCH 3/6] mmc: cavium-octeon: destroy slot platform devices on remove octeon_mmc_probe() creates host->slot_pdev[i] with of_platform_device_create(), while octeon_mmc_remove() does not destroy the child platform devices. The probe failure path removes each MMC slot and destroys its associated platform device, but the normal remove path only removes the MMC slot. As a result, the child platform devices remain registered after the Octeon MMC controller is removed. Destroy each slot platform device during removal after cleaning up the corresponding MMC slot, matching the probe failure cleanup path. This issue was found by manual code inspection. Fixes: 01d95843335c ("mmc: cavium: Add MMC support for Octeon SOCs.") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Ulf Hansson --- drivers/mmc/host/cavium-octeon.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/cavium-octeon.c b/drivers/mmc/host/cavium-octeon.c index 1bfc1a4f9fe9..671dd1a36a74 100644 --- a/drivers/mmc/host/cavium-octeon.c +++ b/drivers/mmc/host/cavium-octeon.c @@ -295,9 +295,12 @@ static void octeon_mmc_remove(struct platform_device *pdev) u64 dma_cfg; int i; - for (i = 0; i < CAVIUM_MAX_MMC; i++) + for (i = 0; i < CAVIUM_MAX_MMC; i++) { if (host->slot[i]) cvm_mmc_of_slot_remove(host->slot[i]); + if (host->slot_pdev[i]) + of_platform_device_destroy(&host->slot_pdev[i]->dev, NULL); + } dma_cfg = readq(host->dma_base + MIO_EMM_DMA_CFG(host)); dma_cfg &= ~MIO_EMM_DMA_CFG_EN; From 29ee8cf18a21b02cd0b2e35f741b61cd1fe3e14e Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 16 Sep 2026 00:13:10 +0800 Subject: [PATCH 4/6] mmc: cavium-thunderx: destroy slot platform devices on remove thunder_mmc_probe() creates host->slot_pdev[i] with of_platform_device_create(), while thunder_mmc_remove() does not destroy the child platform devices. The probe failure path removes each MMC slot and destroys its associated platform device, but the normal remove path only removes the MMC slot. As a result, the child platform devices remain registered after the ThunderX MMC controller is removed. Destroy each slot platform device during removal after cleaning up the corresponding MMC slot, matching the probe failure cleanup path. Keep an extra device reference around of_platform_device_destroy() as done in the existing error path. This issue was found by manual code inspection. Fixes: 166bac38c3c56 ("mmc: cavium: Add MMC PCI driver for ThunderX SOCs") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Ulf Hansson --- drivers/mmc/host/cavium-thunderx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/cavium-thunderx.c b/drivers/mmc/host/cavium-thunderx.c index 84ff6d82ae3c..2635ac94ec5d 100644 --- a/drivers/mmc/host/cavium-thunderx.c +++ b/drivers/mmc/host/cavium-thunderx.c @@ -173,9 +173,12 @@ static void thunder_mmc_remove(struct pci_dev *pdev) u64 dma_cfg; int i; - for (i = 0; i < CAVIUM_MAX_MMC; i++) + for (i = 0; i < CAVIUM_MAX_MMC; i++) { if (host->slot[i]) cvm_mmc_of_slot_remove(host->slot[i]); + if (host->slot_pdev[i]) + of_platform_device_destroy(&host->slot_pdev[i]->dev, NULL); + } dma_cfg = readq(host->dma_base + MIO_EMM_DMA_CFG(host)); dma_cfg &= ~MIO_EMM_DMA_CFG_EN; From aa0a37b5024d921e96ac4f8f487c8bfbf1f1a582 Mon Sep 17 00:00:00 2001 From: Raj Ojha Date: Fri, 25 Sep 2026 00:59:55 +0530 Subject: [PATCH 5/6] memstick: core: wait for request completion before freeing card In memstick_alloc_card() and memstick_set_rw_addr(), wait_for_completion_timeout() is used to wait for device identification and address configuration requests to complete. The memstick subsystem does not provide an abort or cancellation callback in struct memstick_host for in-flight requests. If wait_for_completion_timeout() expires (for instance, when a USB transfer or command retry is delayed), memstick_alloc_card() returns an error and frees the memstick_dev structure via kfree(card). However, the host controller driver (such as rtsx_usb_ms) is still running the request asynchronously in worker context. When the transfer eventually finishes, the driver writes into host->req->data, which points to memory inside the already freed card structure. This leads to a KASAN slab-use-after-free write: BUG: KASAN: slab-use-after-free in ms_read_bytes+0x8fe/0x990 Write of size 1 at addr ffff88813c188079 by task kworker/1:5/5239 Workqueue: events rtsx_usb_ms_handle_req Call Trace: ms_read_bytes+0x8fe/0x990 drivers/memstick/host/rtsx_usb_ms.c:450 rtsx_usb_ms_issue_cmd drivers/memstick/host/rtsx_usb_ms.c:477 [inline] rtsx_usb_ms_handle_req+0x557/0x1a00 drivers/memstick/host/rtsx_usb_ms.c:531 process_one_work+0xac7/0x1b10 kernel/workqueue.c:3396 worker_thread+0x5ef/0xe50 kernel/workqueue.c:3560 kthread+0x373/0x450 kernel/kthread.c:436 Allocated by task 1662: memstick_alloc_card drivers/memstick/core/memstick.c:383 [inline] memstick_check+0x2c1/0x10d0 drivers/memstick/core/memstick.c:452 process_one_work+0xac7/0x1b10 kernel/workqueue.c:3396 Freed by task 1662: kfree+0x20c/0x650 mm/slub.c:6792 memstick_alloc_card drivers/memstick/core/memstick.c:420 [inline] memstick_check+0x72b/0x10d0 drivers/memstick/core/memstick.c:452 process_one_work+0xac7/0x1b10 kernel/workqueue.c:3396 Host controller drivers (rtsx_usb_ms, jmb38x_ms, tifm_ms, r592) already implement their own hardware and bus timeouts, and callers in mspro_block and ms_block already rely on wait_for_completion() unconditionally. Replace wait_for_completion_timeout() with wait_for_completion() in memstick_set_rw_addr() and memstick_alloc_card() so that the card structure is never freed while an asynchronous request is in flight. Fixes: baf8532a147d ("memstick: initial commit for Sony MemoryStick support") Reported-by: syzbot+ccffdf05833ebdaeae70@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=ccffdf05833ebdaeae70 Cc: stable@vger.kernel.org Signed-off-by: Raj Ojha Signed-off-by: Ulf Hansson --- drivers/memstick/core/memstick.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index e03989c4e99e..0d15f48ef51d 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -370,9 +370,7 @@ int memstick_set_rw_addr(struct memstick_dev *card) { card->next_request = h_memstick_set_rw_addr; memstick_new_req(card->host); - if (!wait_for_completion_timeout(&card->mrq_complete, - msecs_to_jiffies(500))) - card->current_mrq.error = -ETIMEDOUT; + wait_for_completion(&card->mrq_complete); return card->current_mrq.error; } @@ -405,9 +403,7 @@ static struct memstick_dev *memstick_alloc_card(struct memstick_host *host) card->next_request = h_memstick_read_dev_id; memstick_new_req(host); - if (!wait_for_completion_timeout(&card->mrq_complete, - msecs_to_jiffies(500))) - card->current_mrq.error = -ETIMEDOUT; + wait_for_completion(&card->mrq_complete); if (card->current_mrq.error) goto err_out; From c96ca94425b076dc398d929366e378d1e22ec794 Mon Sep 17 00:00:00 2001 From: Nguyen Ngoc Thang Date: Wed, 30 Sep 2026 21:07:48 +0700 Subject: [PATCH 6/6] memstick: rtsx_usb_ms: complete requests after eject instead of dropping them memstick_check() can pass its host->removing check just before rtsx_usb_ms_drv_remove() sets eject/removing. Its next request is then silently dropped: rtsx_usb_ms_request() skips schedule_work() once eject is set, and drv_remove's cancel_work_sync() can also cancel a queued handle_req before it picks the request up. Nobody completes card->mrq_complete. Once memstick core waits for requests without a timeout ("memstick: core: wait for request completion before freeing card"), this hangs removal: memstick_check() never returns and memstick_remove_host() blocks in flush_workqueue(): INFO: task kworker/u10:3:65 blocked for more than 20 seconds. Workqueue: kmemstick memstick_check __wait_for_common memstick_check INFO: task kworker/1:1:33 blocked for more than 20 seconds. Workqueue: usb_hub_wq hub_event __flush_workqueue memstick_remove_host rtsx_usb_ms_drv_remove Never drop a request. rtsx_usb_ms_request() always schedules handle_req, and handle_req fails requests with -ENOMEDIUM once eject is set, without touching the device. drv_remove flushes handle_req instead of cancelling it, and cancels it only after memstick_remove_host(), when no new request can arrive, so it cannot run on a freed host. The host_mutex drain in drv_remove is removed: handle_req always leaves host->req NULL when it finishes, so the drain never had anything to do, and it would now race with handle_req. Fixes: 99451dceeb5f ("memstick: Add realtek USB memstick host driver") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/20260924204142.607-1-rajojha047@gmail.com/ Signed-off-by: Nguyen Ngoc Thang Signed-off-by: Ulf Hansson --- drivers/memstick/host/rtsx_usb_ms.c | 31 ++++++++++------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/drivers/memstick/host/rtsx_usb_ms.c b/drivers/memstick/host/rtsx_usb_ms.c index beadc389f15f..d5b3a96fc609 100644 --- a/drivers/memstick/host/rtsx_usb_ms.c +++ b/drivers/memstick/host/rtsx_usb_ms.c @@ -27,7 +27,6 @@ struct rtsx_usb_ms { struct memstick_host *msh; struct memstick_request *req; - struct mutex host_mutex; struct work_struct handle_req; struct delayed_work poll_card; @@ -514,6 +513,13 @@ static void rtsx_usb_ms_handle_req(struct work_struct *work) struct memstick_host *msh = host->msh; int rc; + /* Fail requests after eject so their waiters are released. */ + if (host->eject) { + while (!memstick_next_req(msh, &host->req)) + host->req->error = -ENOMEDIUM; + return; + } + if (!host->req) { pm_runtime_get_sync(ms_dev(host)); do { @@ -547,8 +553,7 @@ static void rtsx_usb_ms_request(struct memstick_host *msh) dev_dbg(ms_dev(host), "--> %s\n", __func__); - if (!host->eject) - schedule_work(&host->handle_req); + schedule_work(&host->handle_req); } static int rtsx_usb_ms_set_param(struct memstick_host *msh, @@ -781,7 +786,6 @@ static int rtsx_usb_ms_drv_probe(struct platform_device *pdev) host->power_mode = MEMSTICK_POWER_OFF; platform_set_drvdata(pdev, host); - mutex_init(&host->host_mutex); INIT_WORK(&host->handle_req, rtsx_usb_ms_handle_req); INIT_DELAYED_WORK(&host->poll_card, rtsx_usb_ms_poll_card); @@ -812,27 +816,12 @@ static void rtsx_usb_ms_drv_remove(struct platform_device *pdev) { struct rtsx_usb_ms *host = platform_get_drvdata(pdev); struct memstick_host *msh = host->msh; - int err; host->eject = true; msh->removing = true; - cancel_work_sync(&host->handle_req); + flush_work(&host->handle_req); cancel_delayed_work_sync(&host->poll_card); - mutex_lock(&host->host_mutex); - if (host->req) { - dev_dbg(ms_dev(host), - "%s: Controller removed during transfer\n", - dev_name(&msh->dev)); - host->req->error = -ENOMEDIUM; - do { - err = memstick_next_req(msh, &host->req); - if (!err) - host->req->error = -ENOMEDIUM; - } while (!err); - } - mutex_unlock(&host->host_mutex); - /* Balance possible unbalanced usage count * e.g. unconditional module removal */ @@ -841,6 +830,8 @@ static void rtsx_usb_ms_drv_remove(struct platform_device *pdev) pm_runtime_disable(ms_dev(host)); memstick_remove_host(msh); + /* No card, no new requests; wait for the last failed one to finish. */ + cancel_work_sync(&host->handle_req); dev_dbg(ms_dev(host), ": Realtek USB Memstick controller has been removed\n"); memstick_free_host(msh);