Skip to content

Commit 5b28dde

Browse files
Shuah Khanmchehab
Shuah Khan
authored andcommitted
[media] media: fix use-after-free in cdev_put() when app exits after driver unbind
When driver unbinds while media_ioctl is in progress, cdev_put() fails with when app exits after driver unbinds. Add devnode struct device kobj as the cdev parent kobject. cdev_add() gets a reference to it and releases it in cdev_del() ensuring that the devnode is not deallocated as long as the application has the device file open. media_devnode_register() initializes the struct device kobj before calling cdev_add(). media_devnode_unregister() does cdev_del() and then deletes the device. devnode is released when the last reference to the struct device is gone. This problem is found on uvcvideo, em28xx, and au0828 drivers and fix has been tested on all three. kernel: [ 193.599736] BUG: KASAN: use-after-free in cdev_put+0x4e/0x50 kernel: [ 193.599745] Read of size 8 by task media_device_te/1851 kernel: [ 193.599792] INFO: Allocated in __media_device_register+0x54 kernel: [ 193.599951] INFO: Freed in media_devnode_release+0xa4/0xc0 kernel: [ 193.601083] Call Trace: kernel: [ 193.601093] [<ffffffff81aecac3>] dump_stack+0x67/0x94 kernel: [ 193.601102] [<ffffffff815359b2>] print_trailer+0x112/0x1a0 kernel: [ 193.601111] [<ffffffff8153b5e4>] object_err+0x34/0x40 kernel: [ 193.601119] [<ffffffff8153d9d4>] kasan_report_error+0x224/0x530 kernel: [ 193.601128] [<ffffffff814a2c3d>] ? kzfree+0x2d/0x40 kernel: [ 193.601137] [<ffffffff81539d72>] ? kfree+0x1d2/0x1f0 kernel: [ 193.601154] [<ffffffff8157ca7e>] ? cdev_put+0x4e/0x50 kernel: [ 193.601162] [<ffffffff8157ca7e>] cdev_put+0x4e/0x50 kernel: [ 193.601170] [<ffffffff815767eb>] __fput+0x52b/0x6c0 kernel: [ 193.601179] [<ffffffff8117743a>] ? switch_task_namespaces+0x2a kernel: [ 193.601188] [<ffffffff815769ee>] ____fput+0xe/0x10 kernel: [ 193.601196] [<ffffffff81170023>] task_work_run+0x133/0x1f0 kernel: [ 193.601204] [<ffffffff8117746e>] ? switch_task_namespaces+0x5e kernel: [ 193.601213] [<ffffffff8111b50c>] do_exit+0x72c/0x2c20 kernel: [ 193.601224] [<ffffffff8111ade0>] ? release_task+0x1250/0x1250 - - - kernel: [ 193.601360] [<ffffffff81003587>] ? exit_to_usermode_loop+0xe7 kernel: [ 193.601368] [<ffffffff810035c0>] exit_to_usermode_loop+0x120 kernel: [ 193.601376] [<ffffffff810061da>] syscall_return_slowpath+0x16a kernel: [ 193.601386] [<ffffffff82848b33>] entry_SYSCALL_64_fastpath+0xa6 Signed-off-by: Shuah Khan <[email protected]> Tested-by: Mauro Carvalho Chehab <[email protected]> Signed-off-by: Mauro Carvalho Chehab <[email protected]>
1 parent a087ce7 commit 5b28dde

File tree

2 files changed

+33
-21
lines changed

2 files changed

+33
-21
lines changed

drivers/media/media-device.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -723,16 +723,16 @@ int __must_check __media_device_register(struct media_device *mdev,
723723

724724
ret = media_devnode_register(mdev, devnode, owner);
725725
if (ret < 0) {
726+
/* devnode free is handled in media_devnode_*() */
726727
mdev->devnode = NULL;
727-
kfree(devnode);
728728
return ret;
729729
}
730730

731731
ret = device_create_file(&devnode->dev, &dev_attr_model);
732732
if (ret < 0) {
733+
/* devnode free is handled in media_devnode_*() */
733734
mdev->devnode = NULL;
734735
media_devnode_unregister(devnode);
735-
kfree(devnode);
736736
return ret;
737737
}
738738

@@ -812,6 +812,8 @@ void media_device_unregister(struct media_device *mdev)
812812
if (media_devnode_is_registered(mdev->devnode)) {
813813
device_remove_file(&mdev->devnode->dev, &dev_attr_model);
814814
media_devnode_unregister(mdev->devnode);
815+
/* devnode free is handled in media_devnode_*() */
816+
mdev->devnode = NULL;
815817
}
816818
}
817819
EXPORT_SYMBOL_GPL(media_device_unregister);

drivers/media/media-devnode.c

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,16 @@ static void media_devnode_release(struct device *cd)
6363
struct media_devnode *devnode = to_media_devnode(cd);
6464

6565
mutex_lock(&media_devnode_lock);
66-
67-
/* Delete the cdev on this minor as well */
68-
cdev_del(&devnode->cdev);
69-
7066
/* Mark device node number as free */
7167
clear_bit(devnode->minor, media_devnode_nums);
72-
7368
mutex_unlock(&media_devnode_lock);
7469

7570
/* Release media_devnode and perform other cleanups as needed. */
7671
if (devnode->release)
7772
devnode->release(devnode);
7873

7974
kfree(devnode);
75+
pr_debug("%s: Media Devnode Deallocated\n", __func__);
8076
}
8177

8278
static struct bus_type media_bus_type = {
@@ -205,6 +201,8 @@ static int media_release(struct inode *inode, struct file *filp)
205201
/* decrease the refcount unconditionally since the release()
206202
return value is ignored. */
207203
put_device(&devnode->dev);
204+
205+
pr_debug("%s: Media Release\n", __func__);
208206
return 0;
209207
}
210208

@@ -235,6 +233,7 @@ int __must_check media_devnode_register(struct media_device *mdev,
235233
if (minor == MEDIA_NUM_DEVICES) {
236234
mutex_unlock(&media_devnode_lock);
237235
pr_err("could not get a free minor\n");
236+
kfree(devnode);
238237
return -ENFILE;
239238
}
240239

@@ -244,40 +243,47 @@ int __must_check media_devnode_register(struct media_device *mdev,
244243
devnode->minor = minor;
245244
devnode->media_dev = mdev;
246245

246+
/* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */
247+
devnode->dev.bus = &media_bus_type;
248+
devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
249+
devnode->dev.release = media_devnode_release;
250+
if (devnode->parent)
251+
devnode->dev.parent = devnode->parent;
252+
dev_set_name(&devnode->dev, "media%d", devnode->minor);
253+
device_initialize(&devnode->dev);
254+
247255
/* Part 2: Initialize and register the character device */
248256
cdev_init(&devnode->cdev, &media_devnode_fops);
249257
devnode->cdev.owner = owner;
258+
devnode->cdev.kobj.parent = &devnode->dev.kobj;
250259

251260
ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1);
252261
if (ret < 0) {
253262
pr_err("%s: cdev_add failed\n", __func__);
254-
goto error;
263+
goto cdev_add_error;
255264
}
256265

257-
/* Part 3: Register the media device */
258-
devnode->dev.bus = &media_bus_type;
259-
devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
260-
devnode->dev.release = media_devnode_release;
261-
if (devnode->parent)
262-
devnode->dev.parent = devnode->parent;
263-
dev_set_name(&devnode->dev, "media%d", devnode->minor);
264-
ret = device_register(&devnode->dev);
266+
/* Part 3: Add the media device */
267+
ret = device_add(&devnode->dev);
265268
if (ret < 0) {
266-
pr_err("%s: device_register failed\n", __func__);
267-
goto error;
269+
pr_err("%s: device_add failed\n", __func__);
270+
goto device_add_error;
268271
}
269272

270273
/* Part 4: Activate this minor. The char device can now be used. */
271274
set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
272275

273276
return 0;
274277

275-
error:
276-
mutex_lock(&media_devnode_lock);
278+
device_add_error:
277279
cdev_del(&devnode->cdev);
280+
cdev_add_error:
281+
mutex_lock(&media_devnode_lock);
278282
clear_bit(devnode->minor, media_devnode_nums);
283+
devnode->media_dev = NULL;
279284
mutex_unlock(&media_devnode_lock);
280285

286+
put_device(&devnode->dev);
281287
return ret;
282288
}
283289

@@ -289,8 +295,12 @@ void media_devnode_unregister(struct media_devnode *devnode)
289295

290296
mutex_lock(&media_devnode_lock);
291297
clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
298+
/* Delete the cdev on this minor as well */
299+
cdev_del(&devnode->cdev);
292300
mutex_unlock(&media_devnode_lock);
293-
device_unregister(&devnode->dev);
301+
device_del(&devnode->dev);
302+
devnode->media_dev = NULL;
303+
put_device(&devnode->dev);
294304
}
295305

296306
/*

0 commit comments

Comments
 (0)