]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
watchdog: wdat_wdt: Fix ACPI table leak in probe function
authorHaotian Zhang <vulab@iscas.ac.cn>
Thu, 13 Nov 2025 02:30:32 +0000 (10:30 +0800)
committerWim Van Sebroeck <wim@linux-watchdog.org>
Sat, 15 Nov 2025 14:28:10 +0000 (15:28 +0100)
wdat_wdt_probe() calls acpi_get_table() to obtain the WDAT ACPI table but
never calls acpi_put_table() on any paths. This causes a permanent ACPI
table memory leak.

Add a single cleanup path which calls acpi_put_table() to ensure
the ACPI table is always released.

Fixes: 058dfc767008 ("ACPI / watchdog: Add support for WDAT hardware watchdog")
Suggested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Haotian Zhang <vulab@iscas.ac.cn>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
drivers/watchdog/wdat_wdt.c

index 650fdc7996e1c28c537b97e152c97b50b79a7f76..dd3c2d69c9df12e814fa34060d613fe710322ad8 100644 (file)
@@ -326,19 +326,27 @@ static int wdat_wdt_probe(struct platform_device *pdev)
                return -ENODEV;
 
        wdat = devm_kzalloc(dev, sizeof(*wdat), GFP_KERNEL);
-       if (!wdat)
-               return -ENOMEM;
+       if (!wdat) {
+               ret = -ENOMEM;
+               goto out_put_table;
+       }
 
        regs = devm_kcalloc(dev, pdev->num_resources, sizeof(*regs),
                            GFP_KERNEL);
-       if (!regs)
-               return -ENOMEM;
+       if (!regs) {
+               ret = -ENOMEM;
+               goto out_put_table;
+       }
 
        /* WDAT specification wants to have >= 1ms period */
-       if (tbl->timer_period < 1)
-               return -EINVAL;
-       if (tbl->min_count > tbl->max_count)
-               return -EINVAL;
+       if (tbl->timer_period < 1) {
+               ret = -EINVAL;
+               goto out_put_table;
+       }
+       if (tbl->min_count > tbl->max_count) {
+               ret = -EINVAL;
+               goto out_put_table;
+       }
 
        wdat->period = tbl->timer_period;
        wdat->wdd.min_timeout = DIV_ROUND_UP(wdat->period * tbl->min_count, 1000);
@@ -355,15 +363,20 @@ static int wdat_wdt_probe(struct platform_device *pdev)
                res = &pdev->resource[i];
                if (resource_type(res) == IORESOURCE_MEM) {
                        reg = devm_ioremap_resource(dev, res);
-                       if (IS_ERR(reg))
-                               return PTR_ERR(reg);
+                       if (IS_ERR(reg)) {
+                               ret = PTR_ERR(reg);
+                               goto out_put_table;
+                       }
                } else if (resource_type(res) == IORESOURCE_IO) {
                        reg = devm_ioport_map(dev, res->start, 1);
-                       if (!reg)
-                               return -ENOMEM;
+                       if (!reg) {
+                               ret = -ENOMEM;
+                               goto out_put_table;
+                       }
                } else {
                        dev_err(dev, "Unsupported resource\n");
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto out_put_table;
                }
 
                regs[i] = reg;
@@ -385,8 +398,10 @@ static int wdat_wdt_probe(struct platform_device *pdev)
                }
 
                instr = devm_kzalloc(dev, sizeof(*instr), GFP_KERNEL);
-               if (!instr)
-                       return -ENOMEM;
+               if (!instr) {
+                       ret = -ENOMEM;
+                       goto out_put_table;
+               }
 
                INIT_LIST_HEAD(&instr->node);
                instr->entry = entries[i];
@@ -417,7 +432,8 @@ static int wdat_wdt_probe(struct platform_device *pdev)
 
                if (!instr->reg) {
                        dev_err(dev, "I/O resource not found\n");
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto out_put_table;
                }
 
                instructions = wdat->instructions[action];
@@ -425,8 +441,10 @@ static int wdat_wdt_probe(struct platform_device *pdev)
                        instructions = devm_kzalloc(dev,
                                                    sizeof(*instructions),
                                                    GFP_KERNEL);
-                       if (!instructions)
-                               return -ENOMEM;
+                       if (!instructions) {
+                               ret = -ENOMEM;
+                               goto out_put_table;
+                       }
 
                        INIT_LIST_HEAD(instructions);
                        wdat->instructions[action] = instructions;
@@ -443,7 +461,7 @@ static int wdat_wdt_probe(struct platform_device *pdev)
 
        ret = wdat_wdt_enable_reboot(wdat);
        if (ret)
-               return ret;
+               goto out_put_table;
 
        platform_set_drvdata(pdev, wdat);
 
@@ -460,12 +478,16 @@ static int wdat_wdt_probe(struct platform_device *pdev)
 
        ret = wdat_wdt_set_timeout(&wdat->wdd, timeout);
        if (ret)
-               return ret;
+               goto out_put_table;
 
        watchdog_set_nowayout(&wdat->wdd, nowayout);
        watchdog_stop_on_reboot(&wdat->wdd);
        watchdog_stop_on_unregister(&wdat->wdd);
-       return devm_watchdog_register_device(dev, &wdat->wdd);
+       ret = devm_watchdog_register_device(dev, &wdat->wdd);
+
+out_put_table:
+       acpi_put_table((struct acpi_table_header *)tbl);
+       return ret;
 }
 
 static int wdat_wdt_suspend_noirq(struct device *dev)