[PATCH 1/8] watchdog: core: Clear wd_data pointer on errors
From: Guenter Roeck
Date: Tue Sep 29 2026 - 09:52:03 EST
In watchdog_cdev_register(), if device registration fails after the core
watchdog_core_data structure is allocated and assigned to the persistent
watchdog_device structure, the function frees the data but leaves a
dangling pointer.
During device registration, watchdog_cdev_register() links the newly
allocated wd_data to wdd->wd_data. If a subsequent initialization step
fails, such as the watchdog_kworker validation, dev_set_name(),
misc_register(), or cdev_device_add(), the function cleans up by freeing
wd_data via kfree() or put_device(). However, it fails to clear the
wdd->wd_data pointer before returning. Furthermore, if cdev_device_add()
fails after misc_register() exposed /dev/watchdog to userspace, a
concurrent open may hold a reference to wd_data while the caller frees wdd,
leaving wd_data->wdd dangling.
Fix the problem by clearing wdd->wd_data on all error paths during
watchdog registration, and clearing wd_data->wdd under wd_data->lock if
cdev_device_add() fails.
Fixes: b4ffb1909843 ("watchdog: Separate and maintain variables based on variable lifetime")
Assisted-by: LLM
Signed-off-by: Guenter Roeck <linux@xxxxxxxxxxxx>
---
drivers/watchdog/watchdog_dev.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index e8c1fcb53c56..ed5b35184271 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -1033,6 +1033,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
wdd->wd_data = wd_data;
if (IS_ERR_OR_NULL(watchdog_kworker)) {
+ wdd->wd_data = NULL;
kfree(wd_data);
return -ENODEV;
}
@@ -1046,6 +1047,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
dev_set_drvdata(&wd_data->dev, wdd);
err = dev_set_name(&wd_data->dev, "watchdog%d", wdd->id);
if (err) {
+ wdd->wd_data = NULL;
put_device(&wd_data->dev);
return err;
}
@@ -1066,6 +1068,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
pr_err("%s: a legacy watchdog module is probably present.\n",
wdd->info->identity);
old_wd_data = NULL;
+ wdd->wd_data = NULL;
put_device(&wd_data->dev);
return err;
}
@@ -1084,6 +1087,10 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
misc_deregister(&watchdog_miscdev);
old_wd_data = NULL;
}
+ mutex_lock(&wd_data->lock);
+ wd_data->wdd = NULL;
+ wdd->wd_data = NULL;
+ mutex_unlock(&wd_data->lock);
put_device(&wd_data->dev);
return err;
}
--
2.45.2