[PATCH] \sbus: char: uctrl: Fix use-after-free in uctrl_open

From: Yoochan Lee
Date: Fri Dec 30 2022 - 23:59:23 EST


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