Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: wdat_wdt: Fix ACPI table leak in probe function

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>

authored by

Haotian Zhang and committed by
Wim Van Sebroeck
25c0b472 e0c50cdd

+43 -21
+43 -21
drivers/watchdog/wdat_wdt.c
··· 326 326 return -ENODEV; 327 327 328 328 wdat = devm_kzalloc(dev, sizeof(*wdat), GFP_KERNEL); 329 - if (!wdat) 330 - return -ENOMEM; 329 + if (!wdat) { 330 + ret = -ENOMEM; 331 + goto out_put_table; 332 + } 331 333 332 334 regs = devm_kcalloc(dev, pdev->num_resources, sizeof(*regs), 333 335 GFP_KERNEL); 334 - if (!regs) 335 - return -ENOMEM; 336 + if (!regs) { 337 + ret = -ENOMEM; 338 + goto out_put_table; 339 + } 336 340 337 341 /* WDAT specification wants to have >= 1ms period */ 338 - if (tbl->timer_period < 1) 339 - return -EINVAL; 340 - if (tbl->min_count > tbl->max_count) 341 - return -EINVAL; 342 + if (tbl->timer_period < 1) { 343 + ret = -EINVAL; 344 + goto out_put_table; 345 + } 346 + if (tbl->min_count > tbl->max_count) { 347 + ret = -EINVAL; 348 + goto out_put_table; 349 + } 342 350 343 351 wdat->period = tbl->timer_period; 344 352 wdat->wdd.min_timeout = DIV_ROUND_UP(wdat->period * tbl->min_count, 1000); ··· 363 355 res = &pdev->resource[i]; 364 356 if (resource_type(res) == IORESOURCE_MEM) { 365 357 reg = devm_ioremap_resource(dev, res); 366 - if (IS_ERR(reg)) 367 - return PTR_ERR(reg); 358 + if (IS_ERR(reg)) { 359 + ret = PTR_ERR(reg); 360 + goto out_put_table; 361 + } 368 362 } else if (resource_type(res) == IORESOURCE_IO) { 369 363 reg = devm_ioport_map(dev, res->start, 1); 370 - if (!reg) 371 - return -ENOMEM; 364 + if (!reg) { 365 + ret = -ENOMEM; 366 + goto out_put_table; 367 + } 372 368 } else { 373 369 dev_err(dev, "Unsupported resource\n"); 374 - return -EINVAL; 370 + ret = -EINVAL; 371 + goto out_put_table; 375 372 } 376 373 377 374 regs[i] = reg; ··· 398 385 } 399 386 400 387 instr = devm_kzalloc(dev, sizeof(*instr), GFP_KERNEL); 401 - if (!instr) 402 - return -ENOMEM; 388 + if (!instr) { 389 + ret = -ENOMEM; 390 + goto out_put_table; 391 + } 403 392 404 393 INIT_LIST_HEAD(&instr->node); 405 394 instr->entry = entries[i]; ··· 432 417 433 418 if (!instr->reg) { 434 419 dev_err(dev, "I/O resource not found\n"); 435 - return -EINVAL; 420 + ret = -EINVAL; 421 + goto out_put_table; 436 422 } 437 423 438 424 instructions = wdat->instructions[action]; ··· 441 425 instructions = devm_kzalloc(dev, 442 426 sizeof(*instructions), 443 427 GFP_KERNEL); 444 - if (!instructions) 445 - return -ENOMEM; 428 + if (!instructions) { 429 + ret = -ENOMEM; 430 + goto out_put_table; 431 + } 446 432 447 433 INIT_LIST_HEAD(instructions); 448 434 wdat->instructions[action] = instructions; ··· 461 443 462 444 ret = wdat_wdt_enable_reboot(wdat); 463 445 if (ret) 464 - return ret; 446 + goto out_put_table; 465 447 466 448 platform_set_drvdata(pdev, wdat); 467 449 ··· 478 460 479 461 ret = wdat_wdt_set_timeout(&wdat->wdd, timeout); 480 462 if (ret) 481 - return ret; 463 + goto out_put_table; 482 464 483 465 watchdog_set_nowayout(&wdat->wdd, nowayout); 484 466 watchdog_stop_on_reboot(&wdat->wdd); 485 467 watchdog_stop_on_unregister(&wdat->wdd); 486 - return devm_watchdog_register_device(dev, &wdat->wdd); 468 + ret = devm_watchdog_register_device(dev, &wdat->wdd); 469 + 470 + out_put_table: 471 + acpi_put_table((struct acpi_table_header *)tbl); 472 + return ret; 487 473 } 488 474 489 475 static int wdat_wdt_suspend_noirq(struct device *dev)