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

Guangshuo Li posted 1 patch 2 months ago
drivers/usb/host/sl811_cs.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
[PATCH] usb: sl811_cs: fix failed platform device registration cleanup
Posted by Guangshuo Li 2 months ago
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@vger.kernel.org
Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
---
 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