diff mbox series

watchdog: set cdev owner before adding

Message ID 20231205190522.55153-1-curtis.klein@hpe.com (mailing list archive)
State New
Headers show
Series watchdog: set cdev owner before adding | expand

Commit Message

Curtis Klein Dec. 5, 2023, 7:05 p.m. UTC
When the new watchdog character device is registered, it becomes
available for opening. This creates a race where userspace may open the
device before the character device's owner is set. This results in an
imbalance in module_get calls as the cdev_get in cdev_open will not
increment the reference count on the watchdog driver module.

This causes problems when the watchdog character device is released as
the module loader's reference will also be released. This makes it
impossible to open the watchdog device later on as it now appears that
the module is being unloaded. The open will fail with -ENXIO from
chrdev_open.

The legacy watchdog device will fail with -EBUSY from the try_module_get
in watchdog_open because it's module owner is the watchdog core module
so it can still be opened but it will fail to get a refcount on the
underlying watchdog device driver.

Fixes: 72139dfa2464 ("watchdog: Fix the race between the release of watchdog_core_data and cdev")
Signed-off-by: Curtis Klein <curtis.klein@hpe.com>
---
 drivers/watchdog/watchdog_dev.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

Comments

Guenter Roeck Dec. 5, 2023, 7:13 p.m. UTC | #1
On 12/5/23 11:05, Curtis Klein wrote:
> When the new watchdog character device is registered, it becomes
> available for opening. This creates a race where userspace may open the
> device before the character device's owner is set. This results in an
> imbalance in module_get calls as the cdev_get in cdev_open will not
> increment the reference count on the watchdog driver module.
> 
> This causes problems when the watchdog character device is released as
> the module loader's reference will also be released. This makes it
> impossible to open the watchdog device later on as it now appears that
> the module is being unloaded. The open will fail with -ENXIO from
> chrdev_open.
> 
> The legacy watchdog device will fail with -EBUSY from the try_module_get
> in watchdog_open because it's module owner is the watchdog core module
> so it can still be opened but it will fail to get a refcount on the
> underlying watchdog device driver.
> 
> Fixes: 72139dfa2464 ("watchdog: Fix the race between the release of watchdog_core_data and cdev")
> Signed-off-by: Curtis Klein <curtis.klein@hpe.com>

Reviewed-by: Guenter Roeck <linux@roeck-us.net>

> ---
>   drivers/watchdog/watchdog_dev.c | 3 +--
>   1 file changed, 1 insertion(+), 2 deletions(-)
> 
> diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
> index 15df74e11a59..e2bd266b1b5b 100644
> --- a/drivers/watchdog/watchdog_dev.c
> +++ b/drivers/watchdog/watchdog_dev.c
> @@ -1073,6 +1073,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
>   
>   	/* Fill in the data structures */
>   	cdev_init(&wd_data->cdev, &watchdog_fops);
> +	wd_data->cdev.owner = wdd->ops->owner;
>   
>   	/* Add the device */
>   	err = cdev_device_add(&wd_data->cdev, &wd_data->dev);
> @@ -1087,8 +1088,6 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
>   		return err;
>   	}
>   
> -	wd_data->cdev.owner = wdd->ops->owner;
> -
>   	/* Record time of most recent heartbeat as 'just before now'. */
>   	wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1);
>   	watchdog_set_open_deadline(wd_data);
diff mbox series

Patch

diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 15df74e11a59..e2bd266b1b5b 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -1073,6 +1073,7 @@  static int watchdog_cdev_register(struct watchdog_device *wdd)
 
 	/* Fill in the data structures */
 	cdev_init(&wd_data->cdev, &watchdog_fops);
+	wd_data->cdev.owner = wdd->ops->owner;
 
 	/* Add the device */
 	err = cdev_device_add(&wd_data->cdev, &wd_data->dev);
@@ -1087,8 +1088,6 @@  static int watchdog_cdev_register(struct watchdog_device *wdd)
 		return err;
 	}
 
-	wd_data->cdev.owner = wdd->ops->owner;
-
 	/* Record time of most recent heartbeat as 'just before now'. */
 	wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1);
 	watchdog_set_open_deadline(wd_data);