aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShuah Khan <shuahkh@osg.samsung.com>2016-05-04 15:48:28 -0400
committerMauro Carvalho Chehab <mchehab@s-opensource.com>2016-06-15 16:58:06 -0400
commit5b28dde51d0ccc54cee70756e1800d70bed7114a (patch)
treeb0d673d732fabaddb33e8283ba39c07a72223e57
parenta087ce704b802becbb4b0f2a20f2cb3f6911802e (diff)
[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 <shuahkh@osg.samsung.com> Tested-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
-rw-r--r--drivers/media/media-device.c6
-rw-r--r--drivers/media/media-devnode.c48
2 files changed, 33 insertions, 21 deletions
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index e61fa66d95df..33a99524216a 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -723,16 +723,16 @@ int __must_check __media_device_register(struct media_device *mdev,
723 723
724 ret = media_devnode_register(mdev, devnode, owner); 724 ret = media_devnode_register(mdev, devnode, owner);
725 if (ret < 0) { 725 if (ret < 0) {
726 /* devnode free is handled in media_devnode_*() */
726 mdev->devnode = NULL; 727 mdev->devnode = NULL;
727 kfree(devnode);
728 return ret; 728 return ret;
729 } 729 }
730 730
731 ret = device_create_file(&devnode->dev, &dev_attr_model); 731 ret = device_create_file(&devnode->dev, &dev_attr_model);
732 if (ret < 0) { 732 if (ret < 0) {
733 /* devnode free is handled in media_devnode_*() */
733 mdev->devnode = NULL; 734 mdev->devnode = NULL;
734 media_devnode_unregister(devnode); 735 media_devnode_unregister(devnode);
735 kfree(devnode);
736 return ret; 736 return ret;
737 } 737 }
738 738
@@ -812,6 +812,8 @@ void media_device_unregister(struct media_device *mdev)
812 if (media_devnode_is_registered(mdev->devnode)) { 812 if (media_devnode_is_registered(mdev->devnode)) {
813 device_remove_file(&mdev->devnode->dev, &dev_attr_model); 813 device_remove_file(&mdev->devnode->dev, &dev_attr_model);
814 media_devnode_unregister(mdev->devnode); 814 media_devnode_unregister(mdev->devnode);
815 /* devnode free is handled in media_devnode_*() */
816 mdev->devnode = NULL;
815 } 817 }
816} 818}
817EXPORT_SYMBOL_GPL(media_device_unregister); 819EXPORT_SYMBOL_GPL(media_device_unregister);
diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
index ecdc02d6ed83..5b605ff38daf 100644
--- a/drivers/media/media-devnode.c
+++ b/drivers/media/media-devnode.c
@@ -63,13 +63,8 @@ static void media_devnode_release(struct device *cd)
63 struct media_devnode *devnode = to_media_devnode(cd); 63 struct media_devnode *devnode = to_media_devnode(cd);
64 64
65 mutex_lock(&media_devnode_lock); 65 mutex_lock(&media_devnode_lock);
66
67 /* Delete the cdev on this minor as well */
68 cdev_del(&devnode->cdev);
69
70 /* Mark device node number as free */ 66 /* Mark device node number as free */
71 clear_bit(devnode->minor, media_devnode_nums); 67 clear_bit(devnode->minor, media_devnode_nums);
72
73 mutex_unlock(&media_devnode_lock); 68 mutex_unlock(&media_devnode_lock);
74 69
75 /* Release media_devnode and perform other cleanups as needed. */ 70 /* Release media_devnode and perform other cleanups as needed. */
@@ -77,6 +72,7 @@ static void media_devnode_release(struct device *cd)
77 devnode->release(devnode); 72 devnode->release(devnode);
78 73
79 kfree(devnode); 74 kfree(devnode);
75 pr_debug("%s: Media Devnode Deallocated\n", __func__);
80} 76}
81 77
82static struct bus_type media_bus_type = { 78static struct bus_type media_bus_type = {
@@ -205,6 +201,8 @@ static int media_release(struct inode *inode, struct file *filp)
205 /* decrease the refcount unconditionally since the release() 201 /* decrease the refcount unconditionally since the release()
206 return value is ignored. */ 202 return value is ignored. */
207 put_device(&devnode->dev); 203 put_device(&devnode->dev);
204
205 pr_debug("%s: Media Release\n", __func__);
208 return 0; 206 return 0;
209} 207}
210 208
@@ -235,6 +233,7 @@ int __must_check media_devnode_register(struct media_device *mdev,
235 if (minor == MEDIA_NUM_DEVICES) { 233 if (minor == MEDIA_NUM_DEVICES) {
236 mutex_unlock(&media_devnode_lock); 234 mutex_unlock(&media_devnode_lock);
237 pr_err("could not get a free minor\n"); 235 pr_err("could not get a free minor\n");
236 kfree(devnode);
238 return -ENFILE; 237 return -ENFILE;
239 } 238 }
240 239
@@ -244,27 +243,31 @@ int __must_check media_devnode_register(struct media_device *mdev,
244 devnode->minor = minor; 243 devnode->minor = minor;
245 devnode->media_dev = mdev; 244 devnode->media_dev = mdev;
246 245
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
247 /* Part 2: Initialize and register the character device */ 255 /* Part 2: Initialize and register the character device */
248 cdev_init(&devnode->cdev, &media_devnode_fops); 256 cdev_init(&devnode->cdev, &media_devnode_fops);
249 devnode->cdev.owner = owner; 257 devnode->cdev.owner = owner;
258 devnode->cdev.kobj.parent = &devnode->dev.kobj;
250 259
251 ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1); 260 ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1);
252 if (ret < 0) { 261 if (ret < 0) {
253 pr_err("%s: cdev_add failed\n", __func__); 262 pr_err("%s: cdev_add failed\n", __func__);
254 goto error; 263 goto cdev_add_error;
255 } 264 }
256 265
257 /* Part 3: Register the media device */ 266 /* Part 3: Add the media device */
258 devnode->dev.bus = &media_bus_type; 267 ret = device_add(&devnode->dev);
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);
265 if (ret < 0) { 268 if (ret < 0) {
266 pr_err("%s: device_register failed\n", __func__); 269 pr_err("%s: device_add failed\n", __func__);
267 goto error; 270 goto device_add_error;
268 } 271 }
269 272
270 /* Part 4: Activate this minor. The char device can now be used. */ 273 /* Part 4: Activate this minor. The char device can now be used. */
@@ -272,12 +275,15 @@ int __must_check media_devnode_register(struct media_device *mdev,
272 275
273 return 0; 276 return 0;
274 277
275error: 278device_add_error:
276 mutex_lock(&media_devnode_lock);
277 cdev_del(&devnode->cdev); 279 cdev_del(&devnode->cdev);
280cdev_add_error:
281 mutex_lock(&media_devnode_lock);
278 clear_bit(devnode->minor, media_devnode_nums); 282 clear_bit(devnode->minor, media_devnode_nums);
283 devnode->media_dev = NULL;
279 mutex_unlock(&media_devnode_lock); 284 mutex_unlock(&media_devnode_lock);
280 285
286 put_device(&devnode->dev);
281 return ret; 287 return ret;
282} 288}
283 289
@@ -289,8 +295,12 @@ void media_devnode_unregister(struct media_devnode *devnode)
289 295
290 mutex_lock(&media_devnode_lock); 296 mutex_lock(&media_devnode_lock);
291 clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); 297 clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
298 /* Delete the cdev on this minor as well */
299 cdev_del(&devnode->cdev);
292 mutex_unlock(&media_devnode_lock); 300 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);
294} 304}
295 305
296/* 306/*