The raw protocol driver destroyed its character device and freed its private data even when userspace still had the device node open. This could cause use-after-free access through active file operations.
Fix this by marking the device as disconnected, returning -ENODEV from all file operations after disconnect, and disabling the Greybus connection before destroying the cdev. This provides safe teardown without redesigning the driver.
Remove the obsolete FIXME
Signed-off-by: Ayaan Mirza Baig ayaanmirzabaig85@gmail.com --- drivers/staging/greybus/raw.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index 71de6776739c..74818ca829c2 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -24,6 +24,7 @@ struct gb_raw { dev_t dev; struct cdev cdev; struct device *device; + bool disconnected; };
struct raw_data { @@ -231,10 +232,14 @@ static void gb_raw_disconnect(struct gb_bundle *bundle) struct raw_data *raw_data; struct raw_data *temp;
- // FIXME - handle removing a connection when the char device node is open. + /* Mark device as disconnected so file operations fail gracefully */ + raw->disconnected = true; + + /* Disable Greybus connection before destroying the chardev */ + gb_connection_disable(connection); + device_destroy(&raw_class, raw->dev); cdev_del(&raw->cdev); - gb_connection_disable(connection); ida_free(&minors, MINOR(raw->dev)); gb_connection_destroy(connection);
@@ -262,6 +267,9 @@ static int raw_open(struct inode *inode, struct file *file) struct cdev *cdev = inode->i_cdev; struct gb_raw *raw = container_of(cdev, struct gb_raw, cdev);
+ if (raw->disconnected) + return -ENODEV; + file->private_data = raw; return 0; } @@ -272,6 +280,9 @@ static ssize_t raw_write(struct file *file, const char __user *buf, struct gb_raw *raw = file->private_data; int retval;
+ if (raw->disconnected) + return -ENODEV; + if (!count) return 0;
@@ -292,6 +303,9 @@ static ssize_t raw_read(struct file *file, char __user *buf, size_t count, int retval = 0; struct raw_data *raw_data;
+ if (raw->disconnected) + return -ENODEV; + mutex_lock(&raw->list_lock); if (list_empty(&raw->list)) goto exit;