[BUG] WARNING: refcount bug in dvb_device_open
Jaeyoung Chung <[email protected]>
| Newsgroups | org.kernel.vger.linux-media,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Hello,
We found a "WARNING: refcount bug in dvb_device_open" on Linux v7.2.
The issue was found by our own race fuzzer. We have not analyzed the root cause,
so we do not have a proposed fix to offer.
To reproduce the race reliably, we applied the delay patch below to the
kernel and ran the C reproducer as root inside an x86_64 QEMU guest. The
crash log we observed, the delay patch and the reproducer are all included
below.
The following kernel config options are required to reproduce the issue:
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
CONFIG_DVB_CORE=y
CONFIG_DVB_VIDTV=y
# CONFIG_DVB_DYNAMIC_MINORS is not set
CONFIG_DVB_TEST_DRIVERS=y
CONFIG_KASAN=y
We hope this report is useful. Please let us know if any further
information would help.
Reported-by: Eulgyu Kim <[email protected]>
Reported-by: Jaeyoung Chung <[email protected]>
Kernel delay patch:
==================================================================
--- a/drivers/media/dvb-core/dvbdev.c 2026-08-21 00:29:05.580120458 +0900
+++ b/drivers/media/dvb-core/dvbdev.c 2026-08-21 00:29:05.652121379 +0900
@@ -21,6 +21,8 @@
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
#include <media/dvbdev.h>
/* Due to enum tuner_pad_index */
@@ -105,6 +111,9 @@
goto fail;
file->private_data = dvb_device_get(dvbdev);
replace_fops(file, new_fops);
+ if (strncmp(current->comm, "syzrepro1", 9) == 0) {
+ mdelay(20);
+ }
if (file->f_op->open)
err = file->f_op->open(inode, file);
up_read(&minor_rwsem);
@@ -613,6 +625,9 @@
{
struct dvb_device *dvbdev = container_of(ref, struct dvb_device, ref);
+ if (strncmp(current->comm, "syzrepro", 8) == 0) {
+ mdelay(60);
+ }
kfree(dvbdev);
}
==================================================================
C reproducer:
==================================================================
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#define SYSCHK(x) ({ long __r = (long)(x); if (__r == -1L) { perror(#x); exit(1); } __r; })
#define DVB_MAJOR 212
#define FE_MINOR(a, id) (((a) << 6) | ((id) << 4) | 3)
struct k_sigevent {
uint64_t sigev_value;
int sigev_signo, sigev_notify, sigev_tid, _pad[12];
};
static char g_path[256];
static pid_t g_pid;
static volatile pid_t g_t1;
static volatile int g_stop;
static int usable(const char *p)
{
int fd = open(p, O_RDONLY | O_NONBLOCK);
if (fd >= 0) {
close(fd);
return 1;
}
return errno == EBUSY;
}
static int find_frontend(void)
{
int a, id;
for (a = 0; a < 4; a++) {
for (id = 0; id < 4; id++) {
snprintf(g_path, sizeof(g_path),
"/dev/dvb/adapter%d/frontend%d", a, id);
if (usable(g_path))
return 0;
snprintf(g_path, sizeof(g_path), "/tmp/fe%d", FE_MINOR(a, id));
unlink(g_path);
if (mknod(g_path, S_IFCHR | 0600,
makedev(DVB_MAJOR, FE_MINOR(a, id))) == 0 && usable(g_path))
return 0;
unlink(g_path);
}
}
return -1;
}
static void noop(int sig) { (void)sig; }
static void *opener(void *a)
{
(void)a;
prctl(PR_SET_NAME, "syzrepro0", 0, 0, 0);
while (!g_stop) {
int fd = open(g_path, O_RDONLY | O_NONBLOCK);
if (fd >= 0)
close(fd);
}
return NULL;
}
/* T1: takes a signal inside dvb_device_open(), unwinding the open midway */
static void *victim(void *a)
{
struct k_sigevent sev = { .sigev_signo = SIGUSR1, .sigev_notify = 4 };
struct itimerspec its = { { 0, 3000000 }, { 0, 3000000 } };
int timerid = 0;
long i;
(void)a;
prctl(PR_SET_NAME, "syzrepro1", 0, 0, 0);
g_t1 = sev.sigev_tid = syscall(__NR_gettid);
if (syscall(__NR_timer_create, CLOCK_MONOTONIC, &sev, &timerid) == 0)
syscall(__NR_timer_settime, timerid, 0, &its, NULL);
for (i = 0; i < 40 && !g_stop; i++) {
int fd = open(g_path, O_RDWR | O_NOATIME);
if (fd >= 0)
close(fd);
}
g_stop = 1;
return NULL;
}
static void *signaller(void *a)
{
(void)a;
prctl(PR_SET_NAME, "syzrepro2", 0, 0, 0);
while (!g_stop) {
if (g_t1)
syscall(__NR_tgkill, g_pid, g_t1, SIGUSR1);
usleep(300);
}
return NULL;
}
int main(void)
{
struct sigaction sa = { .sa_handler = noop };
pthread_t a, b, c;
prctl(PR_SET_NAME, "syzrepro3", 0, 0, 0);
g_pid = getpid();
if (find_frontend() != 0) {
fprintf(stderr, "no dvb frontend\n");
return 1;
}
SYSCHK(sigaction(SIGUSR1, &sa, NULL));
pthread_create(&b, NULL, victim, NULL);
pthread_create(&c, NULL, signaller, NULL);
pthread_create(&a, NULL, opener, NULL);
pthread_join(b, NULL);
g_stop = 1;
pthread_join(c, NULL);
pthread_join(a, NULL);
return 0;
}
==================================================================
Crash log:
==================================================================
refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x76/0xd0 lib/refcount.c:25, CPU#2: syzrepro0/411
Modules linked in:
CPU: 2 UID: 0 PID: 411 Comm: syzrepro0 Not tainted 7.2.0-dirty #3 PREEMPT
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
RIP: 0010:refcount_warn_saturate+0x76/0xd0 lib/refcount.c:25
Code: 3d ff af bd 04 67 48 0f b9 3a eb 4d 85 db 74 2f 83 fb 01 75 38 48 8d 3d f8 af bd 04 67 48 0f b9 3a eb 36 48 8d 3d fa af bd 04 <67> 48 0f b9 3a eb 28 48 8d 3d fc af bd 04 67 48 0f b9 3a eb 1a 48
RSP: 0018:ffff88810ab6f9e8 EFLAGS: 00010246
RAX: 0000000000000000 RBX: 0000000000000002 RCX: dffffc0000000000
RDX: 0000000000000001 RSI: 0000000000000004 RDI: ffffffffbce53340
RBP: 00000000ffffffed R08: ffff88810344ff13 R09: 1ffff11020689fe2
R10: dffffc0000000000 R11: ffffed1020689fe3 R12: ffff88810344ff10
R13: ffff88810409a000 R14: ffff88810344ff10 R15: dffffc0000000000
FS: 00007fb58863a6c0(0000) GS:ffff888153bf7000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fb588639f78 CR3: 000000010afa4000 CR4: 00000000000006f0
Call Trace:
<TASK>
__refcount_add drivers/media/dvb-core/dvbdev.c:-1 [inline]
__refcount_inc include/linux/refcount.h:366 [inline]
refcount_inc include/linux/refcount.h:383 [inline]
kref_get include/linux/kref.h:45 [inline]
dvb_device_get drivers/media/dvb-core/dvbdev.c:629 [inline]
dvb_device_open+0x340/0x370 drivers/media/dvb-core/dvbdev.c:108
chrdev_open+0x3b8/0x490 fs/char_dev.c:411
do_dentry_open+0x3f0/0x1120 fs/open.c:947
vfs_open+0x38/0x2b0 fs/open.c:1052
do_open fs/namei.c:4700 [inline]
path_openat+0x21a0/0x29b0 fs/namei.c:4863
do_file_open+0x19d/0x360 fs/namei.c:4892
do_sys_openat2+0x9a/0x100 fs/open.c:1368
do_sys_open fs/open.c:1374 [inline]
__do_sys_openat fs/open.c:1390 [inline]
__se_sys_openat fs/open.c:1385 [inline]
__x64_sys_openat+0xf8/0x130 fs/open.c:1385
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xf7/0x370 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x76/0x7e
RIP: 0033:0x7fb589738090
Code: 48 89 44 24 20 75 93 44 89 54 24 0c e8 a9 d7 f8 ff 44 8b 54 24 0c 89 da 48 89 ee 41 89 c0 bf 9c ff ff ff b8 01 01 00 00 0f 05 <48> 3d 00 f0 ff ff 77 38 44 89 c7 89 44 24 0c e8 fc d7 f8 ff 8b 44
RSP: 002b:00007fb588639e50 EFLAGS: 00000293 ORIG_RAX: 0000000000000101
RAX: ffffffffffffffda RBX: 0000000000000800 RCX: 00007fb589738090
RDX: 0000000000000800 RSI: 00005c341bf5c060 RDI: 00000000ffffff9c
RBP: 00005c341bf5c060 R08: 0000000000000000 R09: 00007ffece858747
R10: 0000000000000000 R11: 0000000000000293 R12: ffffffffffffff80
R13: 0000000000000000 R14: 00007ffece858650 R15: 00007fb587e3a000
</TASK>
---[ end trace 0000000000000000 ]---
----------------
Code disassembly (best guess):
0: 3d ff af bd 04 cmp $0x4bdafff,%eax
5: 67 48 0f b9 3a ud1 (%edx),%rdi
a: eb 4d jmp 0x59
c: 85 db test %ebx,%ebx
e: 74 2f je 0x3f
10: 83 fb 01 cmp $0x1,%ebx
13: 75 38 jne 0x4d
15: 48 8d 3d f8 af bd 04 lea 0x4bdaff8(%rip),%rdi # 0x4bdb014
1c: 67 48 0f b9 3a ud1 (%edx),%rdi
21: eb 36 jmp 0x59
23: 48 8d 3d fa af bd 04 lea 0x4bdaffa(%rip),%rdi # 0x4bdb024
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: eb 28 jmp 0x59
31: 48 8d 3d fc af bd 04 lea 0x4bdaffc(%rip),%rdi # 0x4bdb034
38: 67 48 0f b9 3a ud1 (%edx),%rdi
3d: eb 1a jmp 0x59
3f: 48 rex.W
==================================================================