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
Based on kernel commit 4dd16e31135e:

Technical Analysis of the Root Cause

1. Functions, Structs, and Files Involved


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().

  1. 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.

  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.

  3. 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).
▶ ▼ ✨ AI Jobs (1)
ID Workflow Result Correct Bug Created Started Finished Revision
651f2679 assessment-security DenialOfService: ✅ Exploitable: ✅ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ✅ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ✅ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ KASAN: slab-use-after-free Read in v4l2_device_disconnect 2026/10/30 11:09 2026/10/30 11:09 2026/10/30 11:53 b498adee
▶ ▼ Discussions (1)
Title Replies (including bot) Last reply
[syzbot] [media?] [usb?] KASAN: slab-use-after-free Read in v4l2_device_disconnect 0 (1) 2026/10/31 03:29

Sample crash report:
em28xx 2-1:0.191: Closing video extension
==================================================================
BUG: KASAN: slab-use-after-free in v4l2_device_disconnect+0xa4/0xc0 drivers/media/v4l2-core/v4l2-device.c:78
Read of size 8 at addr ffff88807de14008 by task kworker/1:1/29

CPU: 1 UID: 0 PID: 29 Comm: kworker/1:1 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 08/05/2026
Workqueue: usb_hub_wq hub_event
Call Trace:
 <TASK>
 __dump_stack lib/dump_stack.c:94 [inline]
 dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
 print_address_description mm/kasan/report.c:378 [inline]
 print_report+0x13d/0x4b0 mm/kasan/report.c:482
 kasan_report+0xdf/0x1c0 mm/kasan/report.c:595
 v4l2_device_disconnect+0xa4/0xc0 drivers/media/v4l2-core/v4l2-device.c:78
 em28xx_v4l2_fini.cold+0x65/0x312 drivers/media/usb/em28xx/em28xx-video.c:2436
 em28xx_close_extension+0xd2/0x460 drivers/media/usb/em28xx/em28xx-core.c:1262
 em28xx_usb_disconnect.cold+0x13d/0x253 drivers/media/usb/em28xx/em28xx-cards.c:4489
 usb_unbind_interface+0x1dd/0x9b0 drivers/usb/core/driver.c:460
 device_remove drivers/base/dd.c:619 [inline]
 device_remove+0x12a/0x180 drivers/base/dd.c:611
 __device_release_driver drivers/base/dd.c:1357 [inline]
 device_release_driver_internal+0x441/0x610 drivers/base/dd.c:1380
 bus_remove_device+0x2bc/0x560 drivers/base/bus.c:681
 device_del+0x376/0x9b0 drivers/base/core.c:3965
 usb_disable_device+0x367/0x810 drivers/usb/core/message.c:1484
 usb_disconnect+0x2e2/0x9a0 drivers/usb/core/hub.c:2355
 hub_port_connect drivers/usb/core/hub.c:5425 [inline]
 hub_port_connect_change drivers/usb/core/hub.c:5725 [inline]
 port_event drivers/usb/core/hub.c:5889 [inline]
 hub_event+0x1bb1/0x4420 drivers/usb/core/hub.c:5971
 process_one_work+0xac7/0x1b10 kernel/workqueue.c:3495
 process_scheduled_works kernel/workqueue.c:3578 [inline]
 worker_thread+0x5ef/0xe50 kernel/workqueue.c:3659
 kthread+0x373/0x450 kernel/kthread.c:436
 ret_from_fork+0x730/0xd60 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
 </TASK>

Allocated by task 5952:
 kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
 kasan_save_track+0x14/0x30 mm/kasan/common.c:78
 poison_kmalloc_redzone mm/kasan/common.c:409 [inline]
 __kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:426
 kasan_kmalloc include/linux/kasan.h:263 [inline]
 __kmalloc_cache_noprof+0x2f8/0x6d0 mm/slub.c:5680
 _kmalloc_noprof include/linux/slab.h:991 [inline]
 _kzalloc_noprof include/linux/slab.h:1312 [inline]
 em28xx_v4l2_init.cold+0x94/0x3a7f drivers/media/usb/em28xx/em28xx-video.c:2751
 em28xx_init_extension+0x13a/0x200 drivers/media/usb/em28xx/em28xx-core.c:1245
 request_module_async+0x1e/0x30 drivers/media/usb/em28xx/em28xx-cards.c:3723
 process_one_work+0xac7/0x1b10 kernel/workqueue.c:3495
 process_scheduled_works kernel/workqueue.c:3578 [inline]
 worker_thread+0x5ef/0xe50 kernel/workqueue.c:3659
 kthread+0x373/0x450 kernel/kthread.c:436
 ret_from_fork+0x730/0xd60 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245

Freed by task 7419:
 kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
 kasan_save_track+0x14/0x30 mm/kasan/common.c:78
 kasan_save_free_info+0x3b/0x70 mm/kasan/generic.c:584
 poison_slab_object mm/kasan/common.c:264 [inline]
 __kasan_slab_free+0x5f/0x80 mm/kasan/common.c:296
 kasan_slab_free include/linux/kasan.h:235 [inline]
 slab_free_hook mm/slub.c:2780 [inline]
 slab_free mm/slub.c:6630 [inline]
 kfree+0x22b/0x6d0 mm/slub.c:6923
 em28xx_free_v4l2+0x7b/0x110 drivers/media/usb/em28xx/em28xx-video.c:2337
 v4l2_device_release drivers/media/v4l2-core/v4l2-device.c:51 [inline]
 kref_put include/linux/kref.h:65 [inline]
 v4l2_device_put+0xa3/0xd0 drivers/media/v4l2-core/v4l2-device.c:56
 v4l2_device_release+0x2cf/0x480 drivers/media/v4l2-core/v4l2-dev.c:229
 device_release+0xd2/0x270 drivers/base/core.c:2640
 kobject_cleanup lib/kobject.c:689 [inline]
 kobject_release lib/kobject.c:720 [inline]
 kref_put include/linux/kref.h:65 [inline]
 kobject_put+0x1f7/0x640 lib/kobject.c:737
 put_device+0x1f/0x30 drivers/base/core.c:3884
 video_put drivers/media/v4l2-core/v4l2-dev.c:173 [inline]
 v4l2_release+0x2da/0x390 drivers/media/v4l2-core/v4l2-dev.c:480
 __fput+0x3fb/0xb50 fs/file_table.c:512
 fput_close_sync+0x118/0x250 fs/file_table.c:618
 filp_close_sync+0x75/0xa0 fs/open.c:1573
 __do_sys_close fs/open.c:1596 [inline]
 __se_sys_close fs/open.c:1583 [inline]
 __x64_sys_close+0x81/0x110 fs/open.c:1583
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x123/0x790 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

The buggy address belongs to the object at ffff88807de14000
 which belongs to the cache kmalloc-8k of size 8192
The buggy address is located 8 bytes inside of
 freed 8192-byte region [ffff88807de14000, ffff88807de16000)

The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x7de10
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0xfff00000000040(head|node=0|zone=1|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 00fff00000000040 ffff88813ff0d280 dead000000000100 dead000000000122
raw: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000
head: 00fff00000000040 ffff88813ff0d280 dead000000000100 dead000000000122
head: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000
head: 00fff00000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd2040(__GFP_IO|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5287, tgid 5287 (dhcpcd-run-hook), ts 43652285249
 set_page_owner include/linux/page_owner.h:33 [inline]
 post_alloc_hook+0xfd/0x120 mm/page_alloc.c:1856
 prep_new_page mm/page_alloc.c:1864 [inline]
 get_page_from_freelist+0x1113/0x35e0 mm/page_alloc.c:3935
 __alloc_frozen_pages_noprof+0x2bd/0x3280 mm/page_alloc.c:5444
 alloc_slab_page mm/slub.c:3384 [inline]
 allocate_slab mm/slub.c:3499 [inline]
 new_slab+0xb1/0x6d0 mm/slub.c:3550
 refill_objects+0xe3/0x410 mm/slub.c:7539
 refill_sheaf mm/slub.c:2917 [inline]
 __pcs_replace_empty_main+0x35e/0x650 mm/slub.c:4811
 alloc_from_pcs mm/slub.c:4887 [inline]
 slab_alloc_node mm/slub.c:5021 [inline]
 __kmalloc_cache_noprof+0x4ef/0x6d0 mm/slub.c:5676
 _kmalloc_noprof include/linux/slab.h:991 [inline]
 _kzalloc_noprof include/linux/slab.h:1312 [inline]
 tomoyo_print_bprm security/tomoyo/audit.c:26 [inline]
 tomoyo_init_log+0xb78/0x1f80 security/tomoyo/audit.c:262
 tomoyo_supervisor+0x506/0x1340 security/tomoyo/common.c:2232
 tomoyo_audit_path_log security/tomoyo/file.c:169 [inline]
 tomoyo_execute_permission+0x38e/0x4b0 security/tomoyo/file.c:624
 tomoyo_find_next_domain+0x38d/0x2010 security/tomoyo/domain.c:763
 tomoyo_bprm_check_security security/tomoyo/tomoyo.c:102 [inline]
 tomoyo_bprm_check_security+0x12d/0x1d0 security/tomoyo/tomoyo.c:92
 security_bprm_check+0x87/0x1e0 security/security.c:834
 search_binary_handler fs/exec.c:1767 [inline]
 exec_binprm fs/exec.c:1809 [inline]
 bprm_execve fs/exec.c:1865 [inline]
 bprm_execve+0x80b/0x1940 fs/exec.c:1841
 do_execveat_common.isra.0+0x49d/0x580 fs/exec.c:1963
 __do_sys_execve fs/exec.c:2036 [inline]
 __se_sys_execve fs/exec.c:2030 [inline]
 __x64_sys_execve+0x93/0xd0 fs/exec.c:2030
page last free pid 5281 tgid 5281 ts 43568219751 stack trace:
 reset_page_owner include/linux/page_owner.h:26 [inline]
 __free_pages_prepare mm/page_alloc.c:1403 [inline]
 __free_frozen_pages+0x7b4/0x1110 mm/page_alloc.c:2954
 qlink_free mm/kasan/quarantine.c:163 [inline]
 qlist_free_all+0x47/0xf0 mm/kasan/quarantine.c:179
 kasan_quarantine_reduce+0x1a0/0x1f0 mm/kasan/quarantine.c:286
 __kasan_slab_alloc+0x69/0x90 mm/kasan/common.c:361
 kasan_slab_alloc include/linux/kasan.h:253 [inline]
 slab_post_alloc_hook mm/slub.c:4720 [inline]
 slab_alloc_node mm/slub.c:5033 [inline]
 __do_kmalloc_node mm/slub.c:5530 [inline]
 __kvmalloc_node_noprof+0x2f6/0x9b0 mm/slub.c:7136
 proc_sys_call_handler+0x2c7/0x5a0 fs/proc/proc_sysctl.c:583
 new_sync_write fs/read_write.c:595 [inline]
 vfs_write+0x6af/0x1050 fs/read_write.c:687
 ksys_write+0x12a/0x250 fs/read_write.c:739
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x123/0x790 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Memory state around the buggy address:
 ffff88807de13f00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
 ffff88807de13f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff88807de14000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
                      ^
 ffff88807de14080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
 ffff88807de14100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/10/30 11:09 upstream 4dd16e31135e b498adee .config console log report syz / log [disk image] [vmlinux] [kernel image] ci-upstream-kasan-gce-selinux-root KASAN: slab-use-after-free Read in v4l2_device_disconnect
* Struck through repros no longer work on HEAD.