* [PATCH 0/2] media: dvbdev: Prevent a dangling pointer in dvb_minors
@ 2026-02-13 13:44 Petr Pavlu
2026-02-13 13:44 ` [PATCH 1/2] " Petr Pavlu
2026-02-13 13:44 ` [PATCH 2/2] media: dvbdev: Simplify error paths in dvb_register_device() Petr Pavlu
0 siblings, 2 replies; 3+ messages in thread
From: Petr Pavlu @ 2026-02-13 13:44 UTC (permalink / raw)
To: Mauro Carvalho Chehab; +Cc: linux-media, linux-kernel, Petr Pavlu
Prevent a dangling pointer in dvb_minors and simplify error paths in
dvb_register_device().
The main patch attempts to address a syzbot report in try_module_get() [1].
The report provides limited information so I'm not entirely sure if the
patch resolves the specific issue. Nonetheless, the bug fixed in
dvb_register_device() is valid and can be reproduced by manually injecting
errors into the function.
Note that I don't have any DVB device available. I tested the code only by
writing a dummy module that calls dvb_register_adapter() and
dvb_register_device(), and by injecting errors into the latter function.
[1] https://syzkaller.appspot.com/bug?extid=71d5d861d9adc6905054
Petr Pavlu (2):
media: dvbdev: Prevent a dangling pointer in dvb_minors
media: dvbdev: Simplify error paths in dvb_register_device()
drivers/media/dvb-core/dvbdev.c | 83 ++++++++++++++-------------------
1 file changed, 35 insertions(+), 48 deletions(-)
base-commit: cee73b1e840c154f64ace682cb477c1ae2e29cc4
--
2.52.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] media: dvbdev: Prevent a dangling pointer in dvb_minors
2026-02-13 13:44 [PATCH 0/2] media: dvbdev: Prevent a dangling pointer in dvb_minors Petr Pavlu
@ 2026-02-13 13:44 ` Petr Pavlu
2026-02-13 13:44 ` [PATCH 2/2] media: dvbdev: Simplify error paths in dvb_register_device() Petr Pavlu
1 sibling, 0 replies; 3+ messages in thread
From: Petr Pavlu @ 2026-02-13 13:44 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: linux-media, linux-kernel, Petr Pavlu,
syzbot+e993e01b15c8eefd9cd4
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@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=71d5d861d9adc6905054
Fixes: 5dd3f3071070 ("V4L/DVB (9361): Dynamic DVB minor allocation")
Signed-off-by: Petr Pavlu <petr.pavlu@suse.com>
---
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
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 2/2] media: dvbdev: Simplify error paths in dvb_register_device()
2026-02-13 13:44 [PATCH 0/2] media: dvbdev: Prevent a dangling pointer in dvb_minors Petr Pavlu
2026-02-13 13:44 ` [PATCH 1/2] " Petr Pavlu
@ 2026-02-13 13:44 ` Petr Pavlu
1 sibling, 0 replies; 3+ messages in thread
From: Petr Pavlu @ 2026-02-13 13:44 UTC (permalink / raw)
To: Mauro Carvalho Chehab; +Cc: linux-media, linux-kernel, Petr Pavlu
The error paths in dvb_register_device() each handle necessary cleanup
operations individually. Since the function performs a number of
operations, this leads to duplicated and lengthy code.
Unify the cleanup process under error labels.
Signed-off-by: Petr Pavlu <petr.pavlu@suse.com>
---
drivers/media/dvb-core/dvbdev.c | 79 +++++++++++++--------------------
1 file changed, 31 insertions(+), 48 deletions(-)
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index c067bbeb9461..94591f8a7afa 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -466,16 +466,15 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
id = dvbdev_get_free_id(adap, type);
if (id < 0) {
- mutex_unlock(&dvbdev_register_lock);
- *pdvbdev = NULL;
pr_err("%s: couldn't find free device id\n", __func__);
- return -ENFILE;
+ ret = -ENFILE;
+ goto error_set_pdvbdev;
}
*pdvbdev = dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL);
if (!dvbdev) {
- mutex_unlock(&dvbdev_register_lock);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto error_unlock;
}
/*
@@ -494,19 +493,15 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
if (!dvbdevfops) {
dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL);
if (!dvbdevfops) {
- kfree(dvbdev);
- *pdvbdev = NULL;
- mutex_unlock(&dvbdev_register_lock);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto error_free_dvbdev;
}
new_node = kzalloc(sizeof(*new_node), GFP_KERNEL);
if (!new_node) {
kfree(dvbdevfops);
- kfree(dvbdev);
- *pdvbdev = NULL;
- mutex_unlock(&dvbdev_register_lock);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto error_free_dvbdev;
}
new_node->fops = dvbdevfops;
@@ -534,17 +529,8 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
minor = nums2minor(adap->num, type, id);
#endif
if (minor >= MAX_DVB_MINORS) {
- if (new_node) {
- list_del(&new_node->list_head);
- kfree(dvbdevfops);
- kfree(new_node);
- }
- list_del(&dvbdev->list_head);
- kfree(dvbdev);
- *pdvbdev = NULL;
- up_write(&minor_rwsem);
- mutex_unlock(&dvbdev_register_lock);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error_dvbdev_cleanup;
}
dvbdev->minor = minor;
@@ -553,18 +539,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
if (ret) {
pr_err("%s: dvb_register_media_device failed to create the mediagraph\n",
__func__);
- if (new_node) {
- list_del(&new_node->list_head);
- kfree(dvbdevfops);
- kfree(new_node);
- }
- dvb_media_device_free(dvbdev);
- list_del(&dvbdev->list_head);
- kfree(dvbdev);
- *pdvbdev = NULL;
- up_write(&minor_rwsem);
- mutex_unlock(&dvbdev_register_lock);
- return ret;
+ goto error_free_media;
}
clsdev = device_create(dvb_class, adap->device,
@@ -573,18 +548,8 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
if (IS_ERR(clsdev)) {
pr_err("%s: failed to create device dvb%d.%s%d (%pe)\n",
__func__, adap->num, dnames[type], id, clsdev);
- if (new_node) {
- list_del(&new_node->list_head);
- kfree(dvbdevfops);
- kfree(new_node);
- }
- dvb_media_device_free(dvbdev);
- list_del(&dvbdev->list_head);
- kfree(dvbdev);
- *pdvbdev = NULL;
- up_write(&minor_rwsem);
- mutex_unlock(&dvbdev_register_lock);
- return PTR_ERR(clsdev);
+ ret = PTR_ERR(clsdev);
+ goto error_free_media;
}
dvb_minors[minor] = dvb_device_get(dvbdev);
@@ -595,6 +560,24 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
mutex_unlock(&dvbdev_register_lock);
return 0;
+
+error_free_media:
+ dvb_media_device_free(dvbdev);
+error_dvbdev_cleanup:
+ if (new_node) {
+ list_del(&new_node->list_head);
+ kfree(dvbdevfops);
+ kfree(new_node);
+ }
+ list_del(&dvbdev->list_head);
+ up_write(&minor_rwsem);
+error_free_dvbdev:
+ kfree(dvbdev);
+error_set_pdvbdev:
+ *pdvbdev = NULL;
+error_unlock:
+ mutex_unlock(&dvbdev_register_lock);
+ return ret;
}
EXPORT_SYMBOL(dvb_register_device);
--
2.52.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-02-13 13:46 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-13 13:44 [PATCH 0/2] media: dvbdev: Prevent a dangling pointer in dvb_minors Petr Pavlu
2026-02-13 13:44 ` [PATCH 1/2] " Petr Pavlu
2026-02-13 13:44 ` [PATCH 2/2] media: dvbdev: Simplify error paths in dvb_register_device() Petr Pavlu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox