From: Sujit Konapur <hello@sujit.dev>
To: yun.zhou@windriver.com
Cc: mchehab@kernel.org, kees@kernel.org, linma@zju.edu.cn,
linux-media@vger.kernel.org, linux-kernel@vger.kernel.org,
Sujit Konapur <hello@sujit.dev>
Subject: Re: [PATCH v3] media: dvb-core: fix use-after-free in dvb_frontend_open()
Date: Mon, 2 Nov 2026 22:26:06 -0800 [thread overview]
Message-ID: <20261103062606.30322-1-hello@sujit.dev> (raw)
In-Reply-To: <20260610083055.3976083-1-yun.zhou@windriver.com>
On Wed, Jun 10, 2026 at 04:30:55PM +0800, Yun Zhou wrote:
> dvb_frontend_open() calls dvb_generic_release() in its error path after
> dvb_generic_open() succeeds. dvb_generic_release() drops the device
> reference via dvb_device_put(), and then dvb_device_open() drops it again
> in its error handling, causing a use-after-free and refcount underflow.
[...]
> @@ -2887,6 +2887,7 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
> mutex_unlock(&fe->dvb->mdev_lock);
> err2:
> #endif
> + dvb_device_get(dvbdev);
> dvb_generic_release(inode, file);
> err1:
> if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl)
I wrote a standalone C reproducer for this syzbot report (below) and
used it to test v3.
Each failed open past dvb_generic_open() is net -1 on the dvb_device
refcount (get in dvb_device_open(), put in dvb_generic_release(), put
again in dvb_device_open()'s error path). The free only happens inside
open() if another holder closes while the opener sleeps in
kthread_create(). The reproducer forces that deterministically: it holds
an O_RDONLY fd, does three O_RDWR opens from a higher-priority SCHED_FIFO
child that gets SIGKILLed while blocked on kthreadd (so
dvb_frontend_start() fails with -EINTR), and closes the holder fd just
before the last kill. Everything is pinned to CPU 0.
Tested on 3d6d817622b0 (the syzbot commit), syzbot's .config, gcc 14.2,
QEMU with vidtv as the frontend:
without the patch: KASAN slab-use-after-free Read in
dvb_frontend_open+0x1344/0x16d0, freed by dvb_generic_release(),
on both -smp 1 and -smp 2 (same as the syzbot report)
with the patch: the same three "dvb_frontend_start: failed to start
kthread (-4)" failures occur, but no KASAN report and no refcount
warning (panic_on_warn=1); 4 runs on -smp 1, 1 run on -smp 2
Tested-by: Sujit Konapur <hello@sujit.dev>
Reproducer:
// Reproducer: KASAN slab-use-after-free Read in dvb_frontend_open
// syzbot: https://syzkaller.appspot.com/bug?extid=40339ea82afa8184ad5d
//
// When dvb_frontend_open() fails after dvb_generic_open() succeeded (here
// dvb_frontend_start() -> kthread_run() returns -EINTR because the opener got
// SIGKILL), its error path calls dvb_generic_release(), which drops a
// dvb_device reference, and dvb_device_open() then drops the reference it took
// for the open again. Every such failed open is net -1 on the refcount.
//
// Refcount walk (dvb_device of /dev/dvb/adapter0/frontend0, vidtv):
// 2 after registration (kref_init + dvb_minors[])
// 3 holder A opens O_RDONLY and keeps the fd (O_RDONLY does not start the
// frontend thread)
// 2 failed O_RDWR open #1
// 1 failed O_RDWR open #2
// failed O_RDWR open #3: B takes a ref (2) and sleeps in kthread_create();
// A closes its fd (1) and SIGKILLs B; B's dvb_generic_release() drops the
// last ref and frees the dvb_device, then dvb_frontend_open() reads
// dvbdev->users at err1 -> UAF.
//
// Deterministic: A is SCHED_FIFO 5, B is SCHED_FIFO 10, and A, B and kthreadd
// are pinned to CPU 0, so A runs only while B waits for kthreadd to create the
// frontend thread. Needs root (RT scheduling, kthreadd affinity) and a DVB
// frontend (CONFIG_DVB_VIDTV=y is enough). Build: cc -static -O1 repro.c
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <unistd.h>
static const char *path = "/dev/dvb/adapter0/frontend0";
static void set_fifo(pid_t pid, int prio)
{
struct sched_param sp = { .sched_priority = prio };
if (syscall(SYS_sched_setscheduler, pid, SCHED_FIFO, &sp))
perror("sched_setscheduler");
}
// One O_RDWR open by a higher-priority child, killed as soon as it blocks.
// If close_holder, the holder fd is closed in that window first.
static void failed_open(int holder, int close_holder)
{
pid_t pid = fork();
if (pid == 0) {
// Drop the inherited copy so the parent's close() is the final
// fput of the holder file.
close(holder);
int fd = open(path, O_RDWR);
// Only reached if we were not killed in time.
fprintf(stderr, "child: open returned %d (%s)\n", fd,
fd < 0 ? strerror(errno) : "ok");
_exit(0);
}
// Child preempts us here and runs until it sleeps inside open().
set_fifo(pid, 10);
if (close_holder)
close(holder);
kill(pid, SIGKILL);
waitpid(pid, NULL, 0);
}
int main(int argc, char **argv)
{
if (argc > 1)
path = argv[1];
// Keep everything on one CPU so the priority ordering below holds even
// if the VM has more than one.
cpu_set_t cpus;
CPU_ZERO(&cpus);
CPU_SET(0, &cpus);
syscall(SYS_sched_setaffinity, 0, sizeof(cpus), &cpus);
// kthreadd too, so it can't create the frontend thread on another CPU
// before we get to send the SIGKILL.
syscall(SYS_sched_setaffinity, 2, sizeof(cpus), &cpus);
set_fifo(0, 5);
int holder = open(path, O_RDONLY);
if (holder < 0) {
perror("open holder");
return 1;
}
failed_open(holder, 0);
failed_open(holder, 0);
failed_open(holder, 1);
// Nothing should get here on a vulnerable kernel with oops=panic.
sleep(2);
printf("no crash\n");
return 0;
}
next prev parent reply other threads:[~2026-11-03 6:26 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-10 8:30 Yun Zhou
2026-06-17 0:29 ` Zhou, Yun
2026-06-25 11:24 ` Zhou, Yun
2026-07-02 10:01 ` Zhou, Yun
2026-07-07 6:55 ` Zhou, Yun
2026-07-14 5:39 ` Zhou, Yun
2026-11-03 6:26 ` Sujit Konapur [this message]
-- strict thread matches above, loose matches on Subject: below --
2026-05-22 3:02 Yun Zhou
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261103062606.30322-1-hello@sujit.dev \
--to=hello@sujit.dev \
--cc=kees@kernel.org \
--cc=linma@zju.edu.cn \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=mchehab@kernel.org \
--cc=yun.zhou@windriver.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®