[PATCH] usb: sl811_cs: fix failed platform device registration cleanup

From: Guangshuo Li

Date: Wed Apr 15 2026 - 15:03:47 EST


When platform_device_register() fails in sl811_hc_init(), the embedded
struct device in platform_dev has already been initialized by
device_initialize(), but the failure path returns the error without
dropping the device reference for the current platform device:

sl811_hc_init()
-> platform_device_register(&platform_dev)
-> device_initialize(&platform_dev.dev)
-> setup_pdev_dma_masks(&platform_dev)
-> platform_device_add(&platform_dev)

This leads to a reference leak when platform_device_register() fails.

Manual review also shows that sl811_hc_init() sets platform_dev.dev.parent
before calling platform_device_register(), but does not clear it on
failure. As a result, later calls may incorrectly see the device as busy.

Fix this by calling platform_device_put() and clearing
platform_dev.dev.parent when platform_device_register() fails. Also make
sl811_cs_release() only unregister the platform device when it belongs
to the current pcmcia device.

The reference leak was identified by a static analysis tool I developed
and confirmed by manual review.

Fixes: c6de2b64eb575 ("[PATCH] USB: add sl811_cs support")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Guangshuo Li <lgs201920130244@xxxxxxxxx>
---
drivers/usb/host/sl811_cs.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c
index ada91ca33f65..096f5fff0bfb 100644
--- a/drivers/usb/host/sl811_cs.c
+++ b/drivers/usb/host/sl811_cs.c
@@ -90,6 +90,8 @@ static struct platform_device platform_dev = {
static int sl811_hc_init(struct device *parent, resource_size_t base_addr,
int irq)
{
+ int ret;
+
if (platform_dev.dev.parent)
return -EBUSY;
platform_dev.dev.parent = parent;
@@ -108,7 +110,13 @@ static int sl811_hc_init(struct device *parent, resource_size_t base_addr,
* by referencing "sl811h_driver".
*/
platform_dev.name = sl811h_driver.driver.name;
- return platform_device_register(&platform_dev);
+ ret = platform_device_register(&platform_dev);
+ if (ret) {
+ platform_device_put(&platform_dev);
+ platform_dev.dev.parent = NULL;
+ }
+
+ return ret;
}

/*====================================================================*/
@@ -128,7 +136,10 @@ static void sl811_cs_release(struct pcmcia_device * link)
dev_dbg(&link->dev, "sl811_cs_release\n");

pcmcia_disable_device(link);
- platform_device_unregister(&platform_dev);
+ if (platform_dev.dev.parent == &link->dev)
+ platform_device_unregister(&platform_dev);
+ platform_dev.dev.parent = NULL;
+
}

static int sl811_cs_config_check(struct pcmcia_device *p_dev, void *priv_data)
--
2.43.0