syzbot
KASAN: slab-use-after-free Read in v4l2_device_disconnect
Status:
upstream: reported syz repro on 2026/10/31 03:29
Subsystems:
media
usb
Labels:
prio:high
[Documentation on labels]
Reported-by: syzbot+1e71765881a6fc43185f@syzkaller.appspotmail.com
First crash: 1d00h, last: 1d00h
✨ AI-generated root cause summary:
In the em28xx driver, if initialization fails after video device nodes have been registered, em28xx_v4l2_init() unregisters the nodes and drops its initial reference on v4l2_dev without clearing dev->v4l2 to NULL. If a userspace process has opened the video device node in the interim, the v4l2 instance remains allocated until the file descriptor is closed. When the USB device is disconnected or the extension is closed, em28xx_v4l2_fini() reads the non-NULL dev->v4l2 pointer and concurrently attempts to disconnect and release the already-destroyed or currently freeing v4l2_device, leading to a slab-use-after-free in v4l2_device_disconnect().
▶
▼
✨ Full AI root cause analysis
Technical Analysis of the Root Cause
1. Functions, Structs, and Files Involved
- Source File:
drivers/media/usb/em28xx/em28xx-video.c
- Functions:
- Structs and Fields:
struct em28xx: dev->v4l2, dev->lock
struct em28xx_v4l2: v4l2->v4l2_dev, v4l2->vdev
struct v4l2_device: v4l2_dev.ref, v4l2_dev.release, v4l2_dev.dev
2. Root Cause and Mechanism
The bug is a use-after-free race condition caused by improper cleanup in em28xx_v4l2_init() combined with unsynchronized pointer dereferencing in em28xx_v4l2_fini().
-
Initialization Failure:
During probe, em28xx_v4l2_init() allocates struct em28xx_v4l2 and stores it into dev->v4l2 (dev->v4l2 = v4l2;). It initializes the embedded v4l2_dev with an initial refcount of 1 and sets its release callback to em28xx_free_v4l2. It then calls video_register_device(&v4l2->vdev, ...) which increments the v4l2_dev refcount to 2.
-
Race Window on Error:
As soon as video_register_device() succeeds, the /dev/video* character device node is exposed to userspace. A userspace process (e.g., udev daemon or monitoring tool) opens the device file, acquiring an active file reference on vdev->dev (via video_get()).
If a subsequent initialization step in em28xx_v4l2_init() fails (such as registering vbi_dev, radio_dev, or media graph creation), execution jumps to unregister_dev:.
In unregister_dev:, the driver unregisters the video device nodes and calls:
mutex_unlock(&dev->lock);
v4l2_device_put(&v4l2->v4l2_dev);
return ret;
Crucially, unregister_dev: drops the driver's initial reference on v4l2_dev, but does not clear dev->v4l2 = NULL. The driver relies on the release callback (em28xx_free_v4l2) to set dev->v4l2 = NULL when the refcount hits zero. However, because userspace still holds the /dev/video* file open, the refcount remains at 1 and em28xx_free_v4l2() is deferred until userspace closes the file descriptor.
-
Dangling Pointer and Use-After-Free:
em28xx_init_extension() ignores the error return value from ops->init(dev), leaving the device attached. When the USB device is disconnected (or the extension is shut down), em28xx_close_extension() calls em28xx_v4l2_fini().
In em28xx_v4l2_fini():
struct em28xx_v4l2 *v4l2 = dev->v4l2;
...
if (!v4l2)
return 0;
...
mutex_lock(&dev->lock);
v4l2_device_disconnect(&v4l2->v4l2_dev);
em28xx_v4l2_fini() reads dev->v4l2 before acquiring dev->lock. When userspace closes the file descriptor concurrently or right before mutex_lock(&dev->lock), the final reference to vdev is dropped, triggering v4l2_device_release() -> v4l2_device_put() -> em28xx_free_v4l2().
em28xx_free_v4l2() frees v4l2 via kfree(v4l2). When em28xx_v4l2_fini() acquires the lock and calls v4l2_device_disconnect(&v4l2->v4l2_dev), it dereferences v4l2_dev->dev at offset 8 within the already-freed em28xx_v4l2 object, producing the slab-use-after-free detected by KASAN.
3. Runtime Consequences in Production Kernels
- Memory Corruption:
true. In a production build without KASAN, accessing freed heap memory (struct em28xx_v4l2) allows reading or writing stale/reallocated slab objects. Furthermore, em28xx_v4l2_fini() continues into subsequent teardown steps and calls v4l2_device_put(&v4l2->v4l2_dev) again, which causes refcount underflows and double-free/corrupted slub freelists.
- Denial of Service:
true. Dereferencing freed/overwritten memory in v4l2_device_disconnect() results in a page fault / general protection fault, panicking the kernel.
- Information Leak:
none. The defect does not leak kernel or heap contents to userspace or network packets.
- Networking / KVM Candidate:
false. The defect resides entirely in the USB video capture subsystem (V4L2 / em28xx).
Sample crash report:
* Struck through repros no longer work on HEAD.