Skip to content

Commit 84265cb

Browse files
d-k-cgregkh
authored andcommitted
greybus: raw: fix use-after-free if write is called after disconnect
If a user writes to the chardev after disconnect has been called, the kernel panics with the following trace (with CONFIG_INIT_ON_FREE_DEFAULT_ON=y): BUG: kernel NULL pointer dereference, address: 0000000000000218 ... Call Trace: <TASK> gb_operation_create_common+0x61/0x180 gb_operation_create_flags+0x28/0xa0 gb_operation_sync_timeout+0x6f/0x100 raw_write+0x7b/0xc7 [gb_raw] vfs_write+0xcf/0x420 ? task_mm_cid_work+0x136/0x220 ksys_write+0x63/0xe0 do_syscall_64+0xa4/0x290 entry_SYSCALL_64_after_hwframe+0x77/0x7f Disconnect calls gb_connection_destroy, which ends up freeing the connection object. When gb_operation_sync is called in the write file operations, its gets a freed connection as parameter and the kernel panics. The gb_connection_destroy cannot be moved out of the disconnect function, as the Greybus subsystem expect all connections belonging to a bundle to be destroyed when disconnect returns. To prevent this bug, use a rw lock to synchronize access between write and disconnect. This guarantees that the write function doesn't try to use a disconnected connection. Fixes: e806c7f ("greybus: raw: add raw greybus kernel driver") Reviewed-by: Johan Hovold <johan@kernel.org> Signed-off-by: Damien Riégel <damien.riegel@silabs.com> Link: https://patch.msgid.link/20260324140039.40001-2-damien.riegel@silabs.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 983cc2c commit 84265cb

1 file changed

Lines changed: 21 additions & 2 deletions

File tree

  • drivers/staging/greybus

drivers/staging/greybus/raw.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ struct gb_raw {
2121
struct list_head list;
2222
int list_data;
2323
struct mutex list_lock;
24+
struct rw_semaphore disconnect_lock;
25+
bool disconnected;
2426
struct cdev cdev;
2527
struct device dev;
2628
};
@@ -200,6 +202,7 @@ static int gb_raw_probe(struct gb_bundle *bundle,
200202

201203
INIT_LIST_HEAD(&raw->list);
202204
mutex_init(&raw->list_lock);
205+
init_rwsem(&raw->disconnect_lock);
203206

204207
raw->connection = connection;
205208
greybus_set_drvdata(bundle, raw);
@@ -235,6 +238,11 @@ static void gb_raw_disconnect(struct gb_bundle *bundle)
235238
struct raw_data *temp;
236239

237240
cdev_device_del(&raw->cdev, &raw->dev);
241+
242+
down_write(&raw->disconnect_lock);
243+
raw->disconnected = true;
244+
up_write(&raw->disconnect_lock);
245+
238246
gb_connection_disable(connection);
239247
gb_connection_destroy(connection);
240248

@@ -277,11 +285,22 @@ static ssize_t raw_write(struct file *file, const char __user *buf,
277285
if (count > MAX_PACKET_SIZE)
278286
return -E2BIG;
279287

288+
down_read(&raw->disconnect_lock);
289+
290+
if (raw->disconnected) {
291+
retval = -ENODEV;
292+
goto exit;
293+
}
294+
280295
retval = gb_raw_send(raw, count, buf);
281296
if (retval)
282-
return retval;
297+
goto exit;
283298

284-
return count;
299+
retval = count;
300+
exit:
301+
up_read(&raw->disconnect_lock);
302+
303+
return retval;
285304
}
286305

287306
static ssize_t raw_read(struct file *file, char __user *buf, size_t count,

0 commit comments

Comments
 (0)