[PATCH 1/2] media: dvbdev: Prevent a dangling pointer in dvb_minors
From: Petr Pavlu
Date: Fri Feb 13 2026 - 08:47:33 EST
Syzbot reports a slab-out-of-bounds write in try_module_get():
==================================================================
BUG: KASAN: slab-out-of-bounds in instrument_atomic_read_write include/linux/instrumented.h:96 [inline]
BUG: KASAN: slab-out-of-bounds in atomic_inc_not_zero include/linux/atomic/atomic-instrumented.h:1536 [inline]
BUG: KASAN: slab-out-of-bounds in try_module_get+0x46/0xc0 kernel/module/main.c:913
Write of size 4 at addr ffff888142f67108 by task syz.3.2706/18556
CPU: 1 UID: 0 PID: 18556 Comm: syz.3.2706 Tainted: G L syzkaller #0 PREEMPT(full)-
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/24/2026
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:378 [inline]
print_report+0xba/0x230 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
check_region_inline mm/kasan/generic.c:-1 [inline]
kasan_check_range+0x264/0x2c0 mm/kasan/generic.c:200
instrument_atomic_read_write include/linux/instrumented.h:96 [inline]
atomic_inc_not_zero include/linux/atomic/atomic-instrumented.h:1536 [inline]
try_module_get+0x46/0xc0 kernel/module/main.c:913
dvb_device_open+0xef/0x350 drivers/media/dvb-core/dvbdev.c:103
chrdev_open+0x4cd/0x5e0 fs/char_dev.c:414
do_dentry_open+0x7ce/0x1420 fs/open.c:962
vfs_open+0x3b/0x340 fs/open.c:1094
do_open fs/namei.c:4637 [inline]
path_openat+0x3486/0x3e20 fs/namei.c:4796
do_filp_open+0x22d/0x490 fs/namei.c:4823
do_sys_openat2+0x12f/0x220 fs/open.c:1430
do_sys_open fs/open.c:1436 [inline]
__do_sys_openat fs/open.c:1452 [inline]
__se_sys_openat fs/open.c:1447 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1447
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xe2/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7feefad5b78e
Code: 08 0f 85 a5 a8 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 80 00 00 00 00 48 83 ec 08
RSP: 002b:00007feefbc79b28 EFLAGS: 00000246 ORIG_RAX: 0000000000000101
RAX: ffffffffffffffda RBX: 00007feefbc7a6c0 RCX: 00007feefad5b78e
RDX: 0000000000000002 RSI: 00007feefbc79c00 RDI: ffffffffffffff9c
RBP: 00007feefbc79c00 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: cccccccccccccccd
R13: 00007feefb016128 R14: 00007feefb016090 R15: 00007ffc341cef58
</TASK>
==================================================================
The dvb_device_open() function performs 'dvbdev = dvb_minors[minor]' and
then calls fops_get(dvbdev->fops). The code in fops_get() invokes
try_module_get(fops->owner) but this operation fails because the module is
apparently gone.
The issue seems to stem from dvb_register_device() which assigns
dvb_minors[minor] and then continues with the device creation. However, if
a subsequent step fails, the value in dvb_minors[minor] is not cleared,
leaving a dangling dvbdev pointer in the array.
Fix the issue by extending the duration for which minor_rwsem is held in
dvb_register_device() and assign dvb_minors[minor] only after the device
creation is fully completed. This is similar to the approach taken in
usb_register_dev(), where usb_minors and its minor_rwsem are handled.
Reported-by: syzbot+e993e01b15c8eefd9cd4@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=71d5d861d9adc6905054
Fixes: 5dd3f3071070 ("V4L/DVB (9361): Dynamic DVB minor allocation")
Signed-off-by: Petr Pavlu <petr.pavlu@xxxxxxxx>
---
drivers/media/dvb-core/dvbdev.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index 8b980d371a45..c067bbeb9461 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -548,8 +548,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
}
dvbdev->minor = minor;
- dvb_minors[minor] = dvb_device_get(dvbdev);
- up_write(&minor_rwsem);
+
ret = dvb_register_media_device(dvbdev, type, minor, demux_sink_pads);
if (ret) {
pr_err("%s: dvb_register_media_device failed to create the mediagraph\n",
@@ -563,6 +562,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
list_del(&dvbdev->list_head);
kfree(dvbdev);
*pdvbdev = NULL;
+ up_write(&minor_rwsem);
mutex_unlock(&dvbdev_register_lock);
return ret;
}
@@ -582,10 +582,14 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
list_del(&dvbdev->list_head);
kfree(dvbdev);
*pdvbdev = NULL;
+ up_write(&minor_rwsem);
mutex_unlock(&dvbdev_register_lock);
return PTR_ERR(clsdev);
}
+ dvb_minors[minor] = dvb_device_get(dvbdev);
+ up_write(&minor_rwsem);
+
dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
adap->num, dnames[type], id, minor, minor);
--
2.52.0