general protection fault in ec168_streaming_ctrl

Status: moderation: reported on 2026/11/15 00:14
Subsystems: media
Labels: prio:low
[Documentation on labels]
Reported-by: syzbot+1bd3211e00623dae5bdb@syzkaller.appspotmail.com
First crash: 1h28m, last: 1h28m
✨ AI-generated root cause summary:

In dvb_usb_start_feed(), the driver fails to synchronize against concurrent frontend sleep (dvb_usb_fe_sleep()) and ignores the error return value from usb_urb_submitv2(). Because dvb_usb_start_feed() does not hold a lock or cache adap->active_fe locally, dvb_usb_fe_sleep() can concurrently power down the hardware and reset adap->active_fe to -1. When dvb_usb_start_feed()随后 invokes d->props->streaming_ctrl(adap->fe[adap->active_fe], 1), it evaluates adap->fe[-1], which indexes out of bounds into the adjacent struct dvb_net (reading NULL). Passing fe = NULL to the driver's streaming_ctrl callback (ec168_streaming_ctrl) triggers a NULL pointer dereference when fe_to_d(fe) accesses fe->dvb at offset 0x308.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit aacb77975041:

Bug Analysis and Technical Mechanism

1. Functions, Structs, and Files Involved


2. Root Cause and Trigger Sequence

  1. Missing Synchronization in dvb_usb_start_feed(): When a user process issues an ioctl like DMX_SET_PES_FILTER (0x40146f2c) on /dev/dvb/adapterX/demuxY, the demux subsystem triggers dvb_usb_start_feed(). At entry, dvb_usb_start_feed() waits only for the ADAP_INIT status bit:

    wait_on_bit(&adap->state_bits, ADAP_INIT, TASK_UNINTERRUPTIBLE);
    if (adap->active_fe == -1)
        return -EINVAL;
    

    Critically, dvb_usb_start_feed() does not wait for ADAP_SLEEP, does not acquire a lock protecting adap->active_fe, and does not cache adap->active_fe into a local variable.

  2. Race with Concurrent Frontend Sleep (dvb_usb_fe_sleep()): If the frontend device is concurrently closed or put to sleep, dvb_usb_fe_sleep() is called:

    if (!adap->suspend_resume_active) {
        set_bit(ADAP_SLEEP, &adap->state_bits);
        wait_on_bit(&adap->state_bits, ADAP_STREAMING, TASK_UNINTERRUPTIBLE);
    }
    

    If dvb_usb_start_feed() has passed the adap->active_fe == -1 check (where active_fe was still 0) but has not yet called set_bit(ADAP_STREAMING, ...), wait_on_bit in dvb_usb_fe_sleep() immediately proceeds without blocking. dvb_usb_fe_sleep() powers off the device via dvb_usbv2_device_power_ctrl(d, 0) and executes line 623:

    adap->active_fe = -1;
    
  3. Unchecked Return Value from usb_urb_submitv2(): Concurrently, dvb_usb_start_feed() sets ADAP_STREAMING and attempts to submit URBs:

    usb_urb_submitv2(&adap->stream, &stream_props);
    

    Because the device was powered down or transitioning to sleep, usb_submit_urb() fails on URB 0, printing: "dvb_usb_v2: could not submit urb no. 0 - get them all back" usb_urb_submitv2() kills the stream and returns an error code (-ESHUTDOWN / -EPROTO / -ENODEV). However, dvb_usb_start_feed() ignores the return value of usb_urb_submitv2() and proceeds directly to line 308.

  4. Out-of-Bounds Indexing adap->fe[-1]: At line 308 in dvb_usb_core.c:

    if (d->props->streaming_ctrl) {
        ret = d->props->streaming_ctrl(adap->fe[adap->active_fe], 1);
    

    Since adap->active_fe was re-read from memory after dvb_usb_fe_sleep() updated it, adap->active_fe is now -1. adap->fe[adap->active_fe] evaluates to adap->fe[-1]. In struct dvb_usb_adapter:

    struct dvb_adapter   dvb_adap;
    struct dmxdev        dmxdev;
    struct dvb_demux     demux;
    struct dvb_net       dvb_net;
    struct dvb_frontend *fe[MAX_NO_OF_FE_PER_ADAP];
    

    The field preceding adap->fe[0] is adap->dvb_net. When CONFIG_DVB_NET is disabled, struct dvb_net is defined as:

    struct dvb_net {
        struct dvb_device *dvbdev;
    };
    

    sizeof(struct dvb_net) is exactly 8 bytes (sizeof(void *)), and its only member dvbdev is initialized to NULL. Consequently, adap->fe[-1] directly indexes adap->dvb_net.dvbdev and yields NULL (fe = 0x0).

  5. NULL Pointer Dereference in ec168_streaming_ctrl(): d->props->streaming_ctrl (ec168_streaming_ctrl) is invoked with fe = NULL:

    static int ec168_streaming_ctrl(struct dvb_frontend *fe, int onoff)
    {
        struct dvb_usb_device *d = fe_to_d(fe);
    

    Where fe_to_d(fe) expands to adap_to_d((struct dvb_usb_adapter *) ((fe)->dvb->priv)). In struct dvb_frontend, the dvb pointer member lies at byte offset 776 (0x308). Dereferencing fe->dvb with fe == NULL attempts to read memory at address 0x0000000000000308. Under KASAN, checking shadow memory for 0x308 (0xdffffc0000000061) triggers a general protection fault (KASAN: null-ptr-deref in range [0x0000000000000308-0x000000000000030f]).

▶ ▼ ✨ AI Jobs (1)
ID Workflow Result Correct Bug Created Started Finished Revision
fe5b525a assessment-security DenialOfService: ✅ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ general protection fault in ec168_streaming_ctrl 2026/11/14 23:29 2026/11/14 23:29 2026/11/15 00:12 819ebe34

Sample crash report:
usb 3-1: dvb_usb_v2: could not submit urb no. 0 - get them all back
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000061: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000308-0x000000000000030f]
CPU: 1 UID: 0 PID: 15743 Comm: syz.7.3121 Tainted: G             L      syzkaller #0 PREEMPT(full) 
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 11/06/2026
RIP: 0010:ec168_streaming_ctrl+0x73/0x210 drivers/media/usb/dvb-usb-v2/ec168.c:312
Code: c7 40 04 00 00 f3 f3 65 48 8b 05 b0 63 60 0d 48 89 44 24 58 31 c0 e8 9c a4 b3 f9 49 8d bc 24 08 03 00 00 48 89 f8 48 c1 e8 03 <42> 80 3c 28 00 0f 85 77 01 00 00 4d 8b a4 24 08 03 00 00 48 b8 00
RSP: 0018:ffffc9000adafac0 EFLAGS: 00010202
RAX: 0000000000000061 RBX: 0000000000000001 RCX: ffffc90010c03000
RDX: 0000000000080000 RSI: ffffffff885adc64 RDI: 0000000000000308
RBP: 1ffff920015b5f58 R08: 0000000000000000 R09: 0000000000000000
R10: ffffc9000adaf68f R11: 0000000000000001 R12: 0000000000000000
R13: dffffc0000000000 R14: 1ffff920015b5f6e R15: ffff888035c84000
FS:  00007f1b121ad6c0(0000) GS:ffff88812256c000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000000110c49919d CR3: 000000007e721000 CR4: 00000000003526f0
DR0: 0000000000000010 DR1: 00000000fffffffd DR2: 400000000001001d
DR3: fffffffffffffff6 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Call Trace:
 <TASK>
 dvb_usb_start_feed+0xa03/0xef0 drivers/media/usb/dvb-usb-v2/dvb_usb_core.c:308
 dmx_ts_feed_start_filtering+0xf6/0x220 drivers/media/dvb-core/dvb_demux.c:747
 dvb_dmxdev_start_feed+0x273/0x3f0 drivers/media/dvb-core/dmxdev.c:658
 dvb_dmxdev_filter_start+0x1b6/0xdd0 drivers/media/dvb-core/dmxdev.c:769
 dvb_dmxdev_pes_filter_set drivers/media/dvb-core/dmxdev.c:985 [inline]
 dvb_demux_do_ioctl+0xe64/0x1200 drivers/media/dvb-core/dmxdev.c:1099
 dvb_usercopy+0x167/0x340 drivers/media/dvb-core/dvbdev.c:996
 dvb_demux_ioctl+0x29/0x40 drivers/media/dvb-core/dmxdev.c:1208
 vfs_ioctl fs/ioctl.c:52 [inline]
 __do_sys_ioctl fs/ioctl.c:598 [inline]
 __se_sys_ioctl fs/ioctl.c:584 [inline]
 __x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:584
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x123/0x7b0 arch/x86/entry/syscall_64.c:89
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f1b113a94d9
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f1b121ad028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007f1b11625fa0 RCX: 00007f1b113a94d9
RDX: 0000200000000100 RSI: 0000000040146f2c RDI: 0000000000000004
RBP: 00007f1b1143804e R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f1b11626038 R14: 00007f1b11625fa0 R15: 00007ffd186b9f98
 </TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:ec168_streaming_ctrl+0x73/0x210 drivers/media/usb/dvb-usb-v2/ec168.c:312
Code: c7 40 04 00 00 f3 f3 65 48 8b 05 b0 63 60 0d 48 89 44 24 58 31 c0 e8 9c a4 b3 f9 49 8d bc 24 08 03 00 00 48 89 f8 48 c1 e8 03 <42> 80 3c 28 00 0f 85 77 01 00 00 4d 8b a4 24 08 03 00 00 48 b8 00
RSP: 0018:ffffc9000adafac0 EFLAGS: 00010202
RAX: 0000000000000061 RBX: 0000000000000001 RCX: ffffc90010c03000
RDX: 0000000000080000 RSI: ffffffff885adc64 RDI: 0000000000000308
RBP: 1ffff920015b5f58 R08: 0000000000000000 R09: 0000000000000000
R10: ffffc9000adaf68f R11: 0000000000000001 R12: 0000000000000000
R13: dffffc0000000000 R14: 1ffff920015b5f6e R15: ffff888035c84000
FS:  00007f1b121ad6c0(0000) GS:ffff88812256c000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000000110c49919d CR3: 000000007e721000 CR4: 00000000003526f0
DR0: 0000000000000010 DR1: 00000000fffffffd DR2: 400000000001001d
DR3: fffffffffffffff6 DR6: 00000000ffff0ff0 DR7: 0000000000000400
----------------
Code disassembly (best guess):
   0:	c7 40 04 00 00 f3 f3 	movl   $0xf3f30000,0x4(%rax)
   7:	65 48 8b 05 b0 63 60 	mov    %gs:0xd6063b0(%rip),%rax        # 0xd6063bf
   e:	0d
   f:	48 89 44 24 58       	mov    %rax,0x58(%rsp)
  14:	31 c0                	xor    %eax,%eax
  16:	e8 9c a4 b3 f9       	call   0xf9b3a4b7
  1b:	49 8d bc 24 08 03 00 	lea    0x308(%r12),%rdi
  22:	00
  23:	48 89 f8             	mov    %rdi,%rax
  26:	48 c1 e8 03          	shr    $0x3,%rax
* 2a:	42 80 3c 28 00       	cmpb   $0x0,(%rax,%r13,1) <-- trapping instruction
  2f:	0f 85 77 01 00 00    	jne    0x1ac
  35:	4d 8b a4 24 08 03 00 	mov    0x308(%r12),%r12
  3c:	00
  3d:	48                   	rex.W
  3e:	b8                   	.byte 0xb8

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/14 23:29 upstream aacb77975041 819ebe34 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-kasan-gce-selinux-root general protection fault in ec168_streaming_ctrl
* Struck through repros no longer work on HEAD.