media: em28xx: fix v4l2 use-after-free after failed init
Subsystems (4)
PATCH v2 1/1 media: em28xx: fix v4l2 use-after-free after failed init
em28xx_v4l2_init() publishes the new object in dev->v4l2 before the video device is registered. If a later initialization step fails -past v4l2_device_register-, the error path unregisters the device nodes and drops the initial v4l2_device reference, but previously left dev->v4l2 pointing at the freed object.
syzbot reported:
BUG: KASAN: slab-use-after-free in v4l2_device_disconnect+0xa4/0xc0 drivers/media/v4l2-core/v4l2-device.c:78
em28xx_v4l2_fini() is called later from USB disconnect. It reads dev->v4l2 before taking dev->lock, and on the failed-init path it owns no reference. The last close of the video node can therefore drop the final v4l2_device reference and free the object between that read and its use in v4l2_device_disconnect(), causing a use-after-free.
Fixing this by clearing dev->v4l2 under dev->lock in the init error path before dropping the initial reference, and read it under dev->lock in fini. This makes fini see NULL after a failed init, while the successful path continues to hold the initial reference until fini is done.
---
v2: added unlock in early return in em28xx_v4l2_fini v1:https://lore.kernel.org/all/[email protected]/T/
drivers/media/usb/em28xx/em28xx-video.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
drivers/media/usb/em28xx/em28xx-video.c +11 -4
@@ -2414,7 +2414,7 @@ static int em28xx_v4l2_open(struct file *filp)*/ static int em28xx_v4l2_fini (struct em28xx *dev) { -struct em28xx_v4l2 *v4l2 = dev->v4l2; +struct em28xx_v4l2 *v4l2;if (dev->is_audio_only) {/* Shouldn't initialize IR for this interface */
@@ -2426,13 +2426,17 @@ static int em28xx_v4l2_fini(struct em28xx *dev)return 0 ; } -if (!v4l2) -return 0 ; -dev_info (&dev->intf->dev,"Closing video extension\n" );mutex_lock (&dev->lock); +/* matches em28xx_v4l2_init error paths */ + v4l2 = dev->v4l2; +if (!v4l2){ +mutex_unlock (&dev->lock); +return 0 ; + } +v4l2_device_disconnect (&v4l2->v4l2_dev);em28xx_uninit_usb_xfer (dev,EM28XX_ANALOG_MODE );
@@ -3129,6 +3133,9 @@ static int em28xx_v4l2_init(struct em28xx *dev)vb2_video_unregister_device (&v4l2->vdev); } +/* matches check in em28xx_v4l2_fini (under the same lock) */ + dev->v4l2 =NULL ; +mutex_unlock (&dev->lock);/*
Signature · 1 line
2.43.0
[email protected]
AI reviewSashiko has reviewed this patch and found no issues.
Quoted 22 lines
em28xx_v4l2_init()publishes the new object indev->v4l2before the video device is registered. If a later initialization step fails -past v4l2_device_register-, the error path unregisters the device nodes and drops the initialv4l2_devicereference, but previously leftdev->v4l2pointing at the freed object.syzbot reported:
BUG: KASAN: slab-use-after-free in v4l2_device_disconnect+0xa4/0xc0 drivers/media/v4l2-core/v4l2-device.c:78
em28xx_v4l2_fini()is called later from USB disconnect. It readsdev->v4l2before takingdev->lock, and on the failed-init path it owns no reference. The last close of the video node can therefore drop the finalv4l2_devicereference and free the object between that read and its use inv4l2_device_disconnect(), causing a use-after-free.Fixing this by clearing
dev->v4l2underdev->lockin the init error path before dropping the initial reference, and read it underdev->lockin fini. This makes fini see NULL after a failed init, while the successful path continues to hold the initial reference until fini is done.
Quoted 5 lines
Fixes 288254383674 ("media: em28xx: use v4l2_device release callback")Reported-by [email protected]Signed-off-by Drif Abdelmalek Mohamed Said <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Hello,
Just following up on this patch to see if anyone has had a chance to review it or if any changes are needed.
Thanks,
Drif Abdelmalek Mohamed Said
Quoted 81 lines · Drif Abdelmalek Mohamed Said
em28xx_v4l2_init()publishes the new object indev->v4l2before the video device is registered. If a later initialization step fails -past v4l2_device_register-, the error path unregisters the device nodes and drops the initialv4l2_devicereference, but previously leftdev->v4l2pointing at the freed object.syzbot reported:
BUG: KASAN: slab-use-after-free in v4l2_device_disconnect+0xa4/0xc0 drivers/media/v4l2-core/v4l2-device.c:78
em28xx_v4l2_fini()is called later from USB disconnect. It readsdev->v4l2before takingdev->lock, and on the failed-init path it owns no reference. The last close of the video node can therefore drop the finalv4l2_devicereference and free the object between that read and its use inv4l2_device_disconnect(), causing a use-after-free.Fixing this by clearing
dev->v4l2underdev->lockin the init error path before dropping the initial reference, and read it underdev->lockin fini. This makes fini see NULL after a failed init, while the successful path continues to hold the initial reference until fini is done.Fixes 288254383674 ("media: em28xx: use v4l2_device release callback")Reported-by [email protected]Signed-off-by Drif Abdelmalek Mohamed Said <[email protected]>---
v2: added unlock in early return inem28xx_v4l2_finiv1:https://lore.kernel.org/all/[email protected]/T/drivers/media/usb/em28xx/em28xx-video.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)drivers/media/usb/em28xx/em28xx-video.c +11
-5@@ -2414,7 +2414,7 @@ static int em28xx_v4l2_open(struct file *filp)*/ static int em28xx_v4l2_fini (struct em28xx *dev) { -struct em28xx_v4l2 *v4l2 = dev->v4l2; +struct em28xx_v4l2 *v4l2;if (dev->is_audio_only) {/* Shouldn't initialize IR for this interface */ @@ -2426,13 +2426,17 @@ static int em28xx_v4l2_fini(struct em28xx *dev)return 0 ; } -if (!v4l2) -return 0 ; -dev_info (&dev->intf->dev,"Closing video extension\n" );mutex_lock (&dev->lock); +/* matches em28xx_v4l2_init error paths */ + v4l2 = dev->v4l2; +if (!v4l2){ +mutex_unlock (&dev->lock); +return 0 ; + } +v4l2_device_disconnect (&v4l2->v4l2_dev);em28xx_uninit_usb_xfer (dev,EM28XX_ANALOG_MODE );@@ -3129,6 +3133,9 @@ static int em28xx_v4l2_init(struct em28xx *dev)vb2_video_unregister_device (&v4l2->vdev); } +/* matches check in em28xx_v4l2_fini (under the same lock) */ + dev->v4l2 =NULL ; +mutex_unlock (&dev->lock);/* -- 2.43.0