A race condition may occur if the user physically removes the uctrl device while calling open(). This is a race condition between uctrl_open() function and the uctrl_remove() function, which may lead to Use-After-Free. Therefore, add a kref when open() uctrl driver and decrement the kref when close() and uctrl_remove() so that the race condition is not occured. ---------------CPU 0--------------------CPU 1----------------- | p = dev_get_drvdata(&op->dev); | ... | kfree(p); -- (1) uctrl_get_event_status(global _driver); — (2) Signed-off-by: Yoochan Lee <yoochan1026@xxxxxxxxx> --- drivers/sbus/char/uctrl.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c index 05de0ce79cb9..17a8acdfc03a 100644 --- a/drivers/sbus/char/uctrl.c +++ b/drivers/sbus/char/uctrl.c @@ -189,6 +189,7 @@ static struct uctrl_driver { int irq; int pending; struct uctrl_status status; + struct kref *refcnt; } *global_driver; static void uctrl_get_event_status(struct uctrl_driver *); @@ -204,12 +205,28 @@ uctrl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return 0; } +static void uctrl_delete(struct kref *kref) +{ + struct uctrl_driver *p = container_of(kref, struct uctrl_driver, refcnt); + + misc_deregister(&uctrl_dev); + free_irq(p->irq, p); + of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0])); + kfree(p); +} + +static int uctrl_close(struct inode *inode, struct file *file) +{ + kref_put(&global_driver->refcnt, uctrl_delete); +} + static int uctrl_open(struct inode *inode, struct file *file) { mutex_lock(&uctrl_mutex); uctrl_get_event_status(global_driver); uctrl_get_external_status(global_driver); + kref_get(&global_driver->refcnt); mutex_unlock(&uctrl_mutex); return 0; } @@ -224,6 +241,7 @@ static const struct file_operations uctrl_fops = { .llseek = no_llseek, .unlocked_ioctl = uctrl_ioctl, .open = uctrl_open, + .release = uctrl_close, }; static struct miscdevice uctrl_dev = { @@ -404,10 +422,7 @@ static int uctrl_remove(struct platform_device *op) struct uctrl_driver *p = dev_get_drvdata(&op->dev); if (p) { - misc_deregister(&uctrl_dev); - free_irq(p->irq, p); - of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0])); - kfree(p); + kref_put(&p->refcnt, uctrl_delete); } return 0; } -- 2.39.0