media: dmxdev: fix UAF when dvb_register_device() fails
authorWang Hai <wanghai38@huawei.com>
Fri, 15 Oct 2021 08:57:41 +0000 (16:57 +0800)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Fri, 19 Nov 2021 15:17:57 +0000 (15:17 +0000)
I got a use-after-free report:

dvbdev: dvb_register_device: failed to create device dvb1.dvr0 (-12)
...
==================================================================
BUG: KASAN: use-after-free in dvb_dmxdev_release+0xce/0x2f0
...
Call Trace:
 dump_stack_lvl+0x6c/0x8b
 print_address_description.constprop.0+0x48/0x70
 kasan_report.cold+0x82/0xdb
 __asan_load4+0x6b/0x90
 dvb_dmxdev_release+0xce/0x2f0
...
Allocated by task 7666:
 kasan_save_stack+0x23/0x50
 __kasan_kmalloc+0x83/0xa0
 kmem_cache_alloc_trace+0x22e/0x470
 dvb_register_device+0x12f/0x980
 dvb_dmxdev_init+0x1f3/0x230
...
Freed by task 7666:
 kasan_save_stack+0x23/0x50
 kasan_set_track+0x20/0x30
 kasan_set_free_info+0x24/0x40
 __kasan_slab_free+0xf2/0x130
 kfree+0xd1/0x5c0
 dvb_register_device.cold+0x1ac/0x1fa
 dvb_dmxdev_init+0x1f3/0x230
...

When dvb_register_device() in dvb_dmxdev_init() fails, dvb_dmxdev_init()
does not return a failure, and the memory pointed to by dvbdev or
dvr_dvbdev is invalid at this point. If they are used subsequently, it
will result in UFA or null-ptr-deref.

If dvb_register_device() in dvb_dmxdev_init() fails, fix the bug by making
dvb_dmxdev_init() return an error as well.

Link: https://lore.kernel.org/linux-media/20211015085741.1203283-1-wanghai38@huawei.com
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: Hulk Robot <hulkci@huawei.com>
Signed-off-by: Wang Hai <wanghai38@huawei.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
drivers/media/dvb-core/dmxdev.c

index 5d5a484..01f288f 100644 (file)
@@ -1413,7 +1413,7 @@ static const struct dvb_device dvbdev_dvr = {
 };
 int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
 {
-       int i;
+       int i, ret;
 
        if (dmxdev->demux->open(dmxdev->demux) < 0)
                return -EUSERS;
@@ -1432,14 +1432,26 @@ int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
                                            DMXDEV_STATE_FREE);
        }
 
-       dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev,
+       ret = dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev,
                            DVB_DEVICE_DEMUX, dmxdev->filternum);
-       dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr,
+       if (ret < 0)
+               goto err_register_dvbdev;
+
+       ret = dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr,
                            dmxdev, DVB_DEVICE_DVR, dmxdev->filternum);
+       if (ret < 0)
+               goto err_register_dvr_dvbdev;
 
        dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
 
        return 0;
+
+err_register_dvr_dvbdev:
+       dvb_unregister_device(dmxdev->dvbdev);
+err_register_dvbdev:
+       vfree(dmxdev->filter);
+       dmxdev->filter = NULL;
+       return ret;
 }
 
 EXPORT_SYMBOL(dvb_dmxdev_init);