syzbot
KASAN: slab-use-after-free Read in dev_dbg_obj
Status:
upstream: reported syz repro on 2026/11/01 18:26
Subsystems:
media
usb
Labels:
prio:high
[Documentation on labels]
Reported-by: syzbot+54648a6c1187a7ef46a6@syzkaller.appspotmail.com
First crash: 12h30m, last: 12h30m
✨ AI-generated root cause summary:
In drivers/media/usb/em28xx/em28xx-video.c, if em28xx_v4l2_init() fails after registering video devices (e.g. during v4l2_mc_create_media_graph()) while userspace concurrently holds an open file handle to a video device node, unregister_dev drops the initial v4l2_dev reference but leaves dev->v4l2 non-NULL. When the device disconnects, em28xx_v4l2_fini() is called and incorrectly drops the initial reference a second time via v4l2_device_put(&v4l2->v4l2_dev). This refcount underflow causes em28xx_free_v4l2() to free struct em28xx_v4l2 prematurely, resulting in a slab use-after-free when userspace closes the file descriptor and v4l2_device_release() accesses embedded media entities.
▶
▼
✨ Full AI root cause analysis
Root Cause Analysis
1. Relevant Components and Involved Code
- Files:
- Structs and Fields:
struct em28xx_v4l2: Contains struct v4l2_device v4l2_dev, struct video_device vdev, and struct media_pad video_pad.
v4l2->v4l2_dev.ref: Reference counter for the V4L2 device and surrounding em28xx_v4l2 container.
dev->v4l2: Pointer in struct em28xx referencing the allocated struct em28xx_v4l2.
2. Trigger Mechanism and Lifecycle Invariant Violation
-
Device Initialization and Failure:
- A USB device matching an em28xx board with a non-webcam profile and external decoder (such as USB ID
1b80:e309, EM2860_BOARD_EASYCAP with decoder EM28XX_SAA711X) is attached.
- Task
request_module_async() invokes em28xx_init_extension(), calling em28xx_v4l2_init().
em28xx_v4l2_init() allocates struct em28xx_v4l2 via kzalloc_obj(*v4l2) (at em28xx-video.c:2751) and sets dev->v4l2 = v4l2. It registers the V4L2 device (v4l2_device_register()), which initializes v4l2->v4l2_dev.ref to 1 and sets .release = em28xx_free_v4l2.
- Because the decoder subdevice fails to initialize (e.g., I2C communication fails on the virtual device), no
MEDIA_ENT_F_ATV_DECODER entity is registered.
- Prior to entity verification,
em28xx_v4l2_init() calls video_register_device(&v4l2->vdev, ...) (at line 3022), which registers /dev/videoX and increments v4l2->v4l2_dev.ref via v4l2_device_get().
- Next,
em28xx_v4l2_create_entities() links v4l2->vdev.entity with &v4l2->video_pad (embedded in struct em28xx_v4l2).
em28xx_v4l2_init() then calls v4l2_mc_create_media_graph(dev->media_dev) (at line 3085), which fails with -EINVAL because no decoder entity is present (drivers/media/v4l2-core/v4l2-mc.c:100), jumping to unregister_dev:.
-
Concurrent Open and Refcount Mismanagement:
- As soon as
video_register_device() exposes /dev/videoX, userspace (v4l_id) opens the device node. v4l2_open() increments the device reference count (v4l2->vdev.dev) via video_get().
- In
unregister_dev:, vb2_video_unregister_device(&v4l2->vdev) calls device_unregister(&vdev->dev). Because v4l_id still holds an open file handle, vdev->dev's refcount does not reach zero, deferring v4l2_device_release(). Consequently, vdev still retains its reference on v4l2->v4l2_dev.
em28xx_v4l2_init() then drops the initial reference taken at v4l2_device_register() time via v4l2_device_put(&v4l2->v4l2_dev) (line 3139) and returns the error code. Because em28xx_init_extension() ignores the return value of ops->init(dev) and unregister_dev: fails to reset dev->v4l2 = NULL, dev->v4l2 remains pointing to the still-allocated struct em28xx_v4l2.
-
Premature Free on Disconnect:
- When the USB device is disconnected,
em28xx_usb_disconnect() runs and calls em28xx_close_extension(dev) -> em28xx_v4l2_fini(dev).
- In
em28xx_v4l2_fini(), because dev->v4l2 is non-NULL, it proceeds through cleanup and calls v4l2_device_put(&v4l2->v4l2_dev) (line 2465) a second time to drop what it assumes is the initial registration reference.
- This causes an underflow of
v4l2->v4l2_dev.ref, reducing it to zero while userspace still has /dev/videoX open.
em28xx_free_v4l2() is immediately triggered, which calls kfree(v4l2) (line 2337), freeing struct em28xx_v4l2 (including v4l2->video_pad).
-
Use-After-Free on Close:
- Userspace task
v4l_id closes its file descriptor, invoking v4l2_release() -> video_put() -> device_release() -> v4l2_device_release().
- In
v4l2_device_release() (line 208), media_device_unregister_entity(&vdev->entity) is called. It iterates over the entity pads and executes media_gobj_destroy(&iter->graph_obj).
iter points to v4l2->video_pad inside the freed struct em28xx_v4l2. When media_gobj_destroy() calls dev_dbg_obj() -> media_type(gobj) (drivers/media/mc/mc-entity.c:96), it reads gobj->id at offset 7120 inside the freed kmalloc-8k slab object, triggering the KASAN slab-use-after-free report.
▶
▼
Last patch testing requests (1)
Sample crash report:
* Struck through repros no longer work on HEAD.