| 0/0 |
2026/05/24 21:04 |
flow |
assessment-security |
39m
Results: map[DenialOfService:true Explanation:**Analysis of the Bug**
The bug is a memory leak in the Linux kernel's DVB (Digital Video Broadcasting)
demux subsystem, specifically in `drivers/media/dvb-core/dmxdev.c`.
**Root Cause:**
When a user configures a demux filter as a PES (Packetized Elementary Stream)
filter using `ioctl(DVB_DEMUX_DMX_SET_PES_FILTER)`, the driver sets the filter
type to `DMXDEV_TYPE_PES`. It then allocates a `struct dmxdev_feed` object and
adds it to the `filter->feed.ts` linked list.
If the user subsequently reconfigures the exact same filter as a SEC (Section)
filter using `ioctl(DVB_DEMUX_DMX_SET_FILTER)`, the `dvb_dmxdev_filter_set()`
function changes the filter type to `DMXDEV_TYPE_SEC`. However, it fails to call
`dvb_dmxdev_filter_reset()` or otherwise free the existing `feed.ts` list before
doing so.
In `struct dmxdev_filter`, the `feed` member is a union:
```c
union {
struct list_head ts;
struct dmx_section_feed *sec;
} feed;
```
Because they share the same memory, when the filter is later started as a SEC
filter, the driver initializes `filter->feed.sec` (often setting it to `NULL`
before allocating a new section feed). This overwrites the `feed.ts` list head,
causing the kernel to lose all references to the previously allocated
`dmxdev_feed` objects, resulting in a permanent memory leak.
**Security Impact:**
* **Exploitable:** No. This is a pure memory leak. The leaked `dmxdev_feed`
objects are never accessed again. The union's memory overlap does not lead to a
use-after-free or wild pointer dereference because the driver unconditionally
overwrites the `feed.sec` pointer with a valid allocated section feed (or
`NULL`) before it is ever dereferenced as a SEC feed.
* **Denial of Service:** Yes. A local attacker with access to the device node
can repeatedly add PIDs to a PES filter and then switch it to a SEC filter,
leaking kernel memory (32 bytes per feed) until the system runs out of memory
(OOM), causing a denial of service.
* **Unprivileged Access:** No. Triggering this bug requires opening the DVB
demux device node (e.g., `/dev/dvb/adapter0/demux0`). By default on standard
Linux distributions, these device nodes are restricted to `root` and the `video`
group (permissions `0660`). Therefore, a completely unprivileged user cannot
reach this code.
* **Other Dimensions:** The bug is not reachable via network traffic, malicious
filesystems, or from within a standard user namespace (as device nodes are not
exposed/accessible by default). It is also not triggerable from a VM guest/host
context. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/24 21:04 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
static long syz_open_dev(volatile long a0, volatile long a1, volatile long a2)
{
if (a0 == 0xc || a0 == 0xb) {
char buf[128];
sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, (uint8_t)a2);
return open(buf, O_RDWR, 0);
} else {
unsigned long nb = a1;
char buf[1024];
char* hash;
strncpy(buf, (char*)a0, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = 0;
while ((hash = strchr(buf, '#'))) {
*hash = '0' + (char)(nb % 10);
nb /= 10;
}
return open(buf, a2 & ~O_CREAT, 0);
}
}
uint64_t r[1] = {0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// syz_open_dev$dvb_demux arguments: [
// dev: ptr[in, buffer] {
// buffer: {2f 64 65 76 2f 64 76 62 2f 61 64 61 70 74 65 72 23 2f 64 65 6d 75 78 23 00} (length 0x19)
// }
// id: intptr = 0x0 (8 bytes)
// flags: open_flags = 0x41 (8 bytes)
// ]
// returns fd_dvb_demux
memcpy((void*)0x200000000080, "/dev/dvb/adapter#/demux#\000", 25);
res = -1;
res = syz_open_dev(/*dev=*/0x200000000080, /*id=*/0, /*flags=O_CREAT|O_WRONLY*/0x41);
if (res != -1)
r[0] = res;
// ioctl$DVB_DEMUX_DMX_SET_PES_FILTER arguments: [
// fd: fd_dvb_demux (resource)
// cmd: const = 0x40146f2c (4 bytes)
// arg: ptr[in, dmx_pes_filter_params] {
// dmx_pes_filter_params {
// pid: int16 = 0x2 (2 bytes)
// pad = 0x0 (2 bytes)
// input: dmx_input = 0x1 (4 bytes)
// output: dmx_output = 0x3 (4 bytes)
// pes_type: dmx_ts_pes = 0x14 (4 bytes)
// flags: dmx_pes_filter_flags = 0x4 (4 bytes)
// }
// }
// ]
*(uint16_t*)0x200000000100 = 2;
*(uint32_t*)0x200000000104 = 1;
*(uint32_t*)0x200000000108 = 3;
*(uint32_t*)0x20000000010c = 0x14;
*(uint32_t*)0x200000000110 = 4;
syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0x40146f2c, /*arg=*/0x200000000100ul);
// ioctl$DVB_DEMUX_DMX_SET_FILTER arguments: [
// fd: fd_dvb_demux (resource)
// cmd: const = 0x403c6f2b (4 bytes)
// arg: ptr[in, dmx_sct_filter_params] {
// dmx_sct_filter_params {
// pid: int16 = 0x8 (2 bytes)
// filter: dmx_filter {
// filter: buffer: {c2 3b 58 52 72 ac a2 8d 9c ad 01 a3 cd d1 e3 a9} (length 0x10)
// mask: buffer: {d4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x10)
// mode: buffer: {b0 e9 53 ee 24 cb ee 87 39 aa 9f 2a 42 3b 34 29} (length 0x10)
// }
// pad = 0x0 (2 bytes)
// timeout: int32 = 0x6 (4 bytes)
// flags: dmx_sct_filter_flags = 0x6 (4 bytes)
// }
// }
// ]
*(uint16_t*)0x2000000003c0 = 8;
memcpy((void*)0x2000000003c2, "\xc2\x3b\x58\x52\x72\xac\xa2\x8d\x9c\xad\x01\xa3\xcd\xd1\xe3\xa9", 16);
memcpy((void*)0x2000000003d2, "\xd4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
memcpy((void*)0x2000000003e2, "\xb0\xe9\x53\xee\x24\xcb\xee\x87\x39\xaa\x9f\x2a\x42\x3b\x34\x29", 16);
*(uint32_t*)0x2000000003f4 = 6;
*(uint32_t*)0x2000000003f8 = 6;
syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0x403c6f2b, /*arg=*/0x2000000003c0ul);
return 0;
}
]
|
| 2/1 |
2026/05/24 21:04 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/8912ecf9ca00a9ea88c66924b7c9f926dcb5d760]
|
| 3/1 |
2026/05/24 21:04 |
action |
kernel-builder |
20m
Results: map[KernelObj:/app/workdir/cache/build/50c27233618ab34c5a28f6bd6f088dfa76fe05c8]
|
| 4/1 |
2026/05/24 21:24 |
action |
codesearch-prepare |
16m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/24 21:40 |
agent |
expert |
3m
Model:
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction:
You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by greeping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption or elevated privileges.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completly wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control tranfers, that's also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitabability depends on how exactly the uninit value
is used in the code, and what it affects.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidetial Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
BUG: memory leak
unreferenced object 0xffff88810b771800 (size 32):
comm "syz.0.17", pid 5923, jiffies 4294942824
hex dump (first 32 bytes):
02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08 00 56 05 00 c9 ff ff 08 00 56 05 00 c9 ff ff ..V.......V.....
backtrace (crc 1a56a8d7):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4574 [inline]
slab_alloc_node mm/slub.c:4898 [inline]
__kmalloc_cache_noprof+0x371/0x480 mm/slub.c:5410
kmalloc_noprof include/linux/slab.h:950 [inline]
kzalloc_noprof include/linux/slab.h:1188 [inline]
dvb_dmxdev_add_pid+0x9c/0x160 drivers/media/dvb-core/dmxdev.c:897
dvb_dmxdev_pes_filter_set drivers/media/dvb-core/dmxdev.c:972 [inline]
dvb_demux_do_ioctl+0x3c6/0x7d0 drivers/media/dvb-core/dmxdev.c:1092
dvb_usercopy+0x116/0x2d0 drivers/media/dvb-core/dvbdev.c:996
dvb_demux_ioctl+0x29/0x40 drivers/media/dvb-core/dmxdev.c:1201
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl fs/ioctl.c:583 [inline]
__x64_sys_ioctl+0xf4/0x140 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xee/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
BUG: memory leak
unreferenced object 0xffff8881297f65e0 (size 32):
comm "syz.0.18", pid 5934, jiffies 4294942829
hex dump (first 32 bytes):
02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08 00 56 05 00 c9 ff ff 08 00 56 05 00 c9 ff ff ..V.......V.....
backtrace (crc 1a56a8d7):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4574 [inline]
slab_alloc_node mm/slub.c:4898 [inline]
__kmalloc_cache_noprof+0x371/0x480 mm/slub.c:5410
kmalloc_noprof include/linux/slab.h:950 [inline]
kzalloc_noprof include/linux/slab.h:1188 [inline]
dvb_dmxdev_add_pid+0x9c/0x160 drivers/media/dvb-core/dmxdev.c:897
dvb_dmxdev_pes_filter_set drivers/media/dvb-core/dmxdev.c:972 [inline]
dvb_demux_do_ioctl+0x3c6/0x7d0 drivers/media/dvb-core/dmxdev.c:1092
dvb_usercopy+0x116/0x2d0 drivers/media/dvb-core/dvbdev.c:996
dvb_demux_ioctl+0x29/0x40 drivers/media/dvb-core/dmxdev.c:1201
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl fs/ioctl.c:583 [inline]
__x64_sys_ioctl+0xf4/0x140 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xee/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
connection error: failed to recv *flatrpc.ExecutorMessageRawT: EOF
It is reproducible with the followint program.
Keep in mind that it may lack the precise threading, sandboxing, and some arguments of a working reproducer.
But it should give an idea of the involved syscalls.
// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
static long syz_open_dev(volatile long a0, volatile long a1, volatile long a2)
{
if (a0 == 0xc || a0 == 0xb) {
char buf[128];
sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, (uint8_t)a2);
return open(buf, O_RDWR, 0);
} else {
unsigned long nb = a1;
char buf[1024];
char* hash;
strncpy(buf, (char*)a0, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = 0;
while ((hash = strchr(buf, '#'))) {
*hash = '0' + (char)(nb % 10);
nb /= 10;
}
return open(buf, a2 & ~O_CREAT, 0);
}
}
uint64_t r[1] = {0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// syz_open_dev$dvb_demux arguments: [
// dev: ptr[in, buffer] {
// buffer: {2f 64 65 76 2f 64 76 62 2f 61 64 61 70 74 65 72 23 2f 64 65 6d 75 78 23 00} (length 0x19)
// }
// id: intptr = 0x0 (8 bytes)
// flags: open_flags = 0x41 (8 bytes)
// ]
// returns fd_dvb_demux
memcpy((void*)0x200000000080, "/dev/dvb/adapter#/demux#\000", 25);
res = -1;
res = syz_open_dev(/*dev=*/0x200000000080, /*id=*/0, /*flags=O_CREAT|O_WRONLY*/0x41);
if (res != -1)
r[0] = res;
// ioctl$DVB_DEMUX_DMX_SET_PES_FILTER arguments: [
// fd: fd_dvb_demux (resource)
// cmd: const = 0x40146f2c (4 bytes)
// arg: ptr[in, dmx_pes_filter_params] {
// dmx_pes_filter_params {
// pid: int16 = 0x2 (2 bytes)
// pad = 0x0 (2 bytes)
// input: dmx_input = 0x1 (4 bytes)
// output: dmx_output = 0x3 (4 bytes)
// pes_type: dmx_ts_pes = 0x14 (4 bytes)
// flags: dmx_pes_filter_flags = 0x4 (4 bytes)
// }
// }
// ]
*(uint16_t*)0x200000000100 = 2;
*(uint32_t*)0x200000000104 = 1;
*(uint32_t*)0x200000000108 = 3;
*(uint32_t*)0x20000000010c = 0x14;
*(uint32_t*)0x200000000110 = 4;
syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0x40146f2c, /*arg=*/0x200000000100ul);
// ioctl$DVB_DEMUX_DMX_SET_FILTER arguments: [
// fd: fd_dvb_demux (resource)
// cmd: const = 0x403c6f2b (4 bytes)
// arg: ptr[in, dmx_sct_filter_params] {
// dmx_sct_filter_params {
// pid: int16 = 0x8 (2 bytes)
// filter: dmx_filter {
// filter: buffer: {c2 3b 58 52 72 ac a2 8d 9c ad 01 a3 cd d1 e3 a9} (length 0x10)
// mask: buffer: {d4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x10)
// mode: buffer: {b0 e9 53 ee 24 cb ee 87 39 aa 9f 2a 42 3b 34 29} (length 0x10)
// }
// pad = 0x0 (2 bytes)
// timeout: int32 = 0x6 (4 bytes)
// flags: dmx_sct_filter_flags = 0x6 (4 bytes)
// }
// }
// ]
*(uint16_t*)0x2000000003c0 = 8;
memcpy((void*)0x2000000003c2, "\xc2\x3b\x58\x52\x72\xac\xa2\x8d\x9c\xad\x01\xa3\xcd\xd1\xe3\xa9", 16);
memcpy((void*)0x2000000003d2, "\xd4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
memcpy((void*)0x2000000003e2, "\xb0\xe9\x53\xee\x24\xcb\xee\x87\x39\xaa\x9f\x2a\x42\x3b\x34\x29", 16);
*(uint32_t*)0x2000000003f4 = 6;
*(uint32_t*)0x2000000003f8 = 6;
syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0x403c6f2b, /*arg=*/0x2000000003c0ul);
return 0;
}
Reply: **Analysis of the Bug**
The bug is a memory leak in the Linux kernel's DVB (Digital Video Broadcasting) demux subsystem, specifically in `drivers/media/dvb-core/dmxdev.c`.
**Root Cause:**
When a user configures a demux filter as a PES (Packetized Elementary Stream) filter using `ioctl(DVB_DEMUX_DMX_SET_PES_FILTER)`, the driver sets the filter type to `DMXDEV_TYPE_PES`. It then allocates a `struct dmxdev_feed` object and adds it to the `filter->feed.ts` linked list.
If the user subsequently reconfigures the exact same filter as a SEC (Section) filter using `ioctl(DVB_DEMUX_DMX_SET_FILTER)`, the `dvb_dmxdev_filter_set()` function changes the filter type to `DMXDEV_TYPE_SEC`. However, it fails to call `dvb_dmxdev_filter_reset()` or otherwise free the existing `feed.ts` list before doing so.
In `struct dmxdev_filter`, the `feed` member is a union:
```c
union {
struct list_head ts;
struct dmx_section_feed *sec;
} feed;
```
Because they share the same memory, when the filter is later started as a SEC filter, the driver initializes `filter->feed.sec` (often setting it to `NULL` before allocating a new section feed). This overwrites the `feed.ts` list head, causing the kernel to lose all references to the previously allocated `dmxdev_feed` objects, resulting in a permanent memory leak.
**Security Impact:**
* **Exploitable:** No. This is a pure memory leak. The leaked `dmxdev_feed` objects are never accessed again. The union's memory overlap does not lead to a use-after-free or wild pointer dereference because the driver unconditionally overwrites the `feed.sec` pointer with a valid allocated section feed (or `NULL`) before it is ever dereferenced as a SEC feed.
* **Denial of Service:** Yes. A local attacker with access to the device node can repeatedly add PIDs to a PES filter and then switch it to a SEC filter, leaking kernel memory (32 bytes per feed) until the system runs out of memory (OOM), causing a denial of service.
* **Unprivileged Access:** No. Triggering this bug requires opening the DVB demux device node (e.g., `/dev/dvb/adapter0/demux0`). By default on standard Linux distributions, these device nodes are restricted to `root` and the `video` group (permissions `0660`). Therefore, a completely unprivileged user cannot reach this code.
* **Other Dimensions:** The bug is not reachable via network traffic, malicious filesystems, or from within a standard user namespace (as device nodes are not exposed/accessible by default). It is also not triggerable from a VM guest/host context.
|
| 6/2 |
2026/05/24 21:40 |
llm |
expert |
0m
Model:
Tokens:
input: 9023
output: 53
thoughts: 154
Thoughts: **Analyzing a Linux Kernel Memory Leak**
Okay, so I've got a memory leak report coming in, specifically targeting the Linux kernel. It seems the issue stems from `dvb_dmxdev_add_pid`. I'm tracing the call stack, and it looks like it's triggered from `dvb_dmxdev_pes_filter_set`, which is called through `dvb_demux_do_ioctl` and eventually, `dvb_usercopy`. The reproducer is simple enough: open a `/dev/dvb/adapter#/demux#` device and then hit it with an `ioctl` call using `DVB_DEMUX_DMX_SET_PES_FILTER` or `DVB_DEMUX_DMX_SET_FILTER`.
Right, so the culprit is likely in `drivers/media/dvb-core/dmxdev.c`. Time to dig into `dvb_dmxdev_add_pid` and see what's going on in there. I'll need to scrutinize how it handles the filter settings and the data structures involved, looking for any potential allocation errors or missed deallocations. This smells like a classic issue with improper resource management in a kernel driver. Let's see if we can find the root cause.
|
| 7/2 |
2026/05/24 21:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_dmxdev_add_pid]
Results: map[SourceCode: 883: static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
884: struct dmxdev_filter *filter, u16 pid)
885: {
886: struct dmxdev_feed *feed;
887:
888: if ((filter->type != DMXDEV_TYPE_PES) ||
889: (filter->state < DMXDEV_STATE_SET))
890: return -EINVAL;
891:
892: /* only TS packet filters may have multiple PIDs */
893: if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) &&
894: (!list_empty(&filter->feed.ts)))
895: return -EINVAL;
896:
897: feed = kzalloc_obj(struct dmxdev_feed);
898: if (feed == NULL)
899: return -ENOMEM;
900:
901: feed->pid = pid;
902: list_add(&feed->next, &filter->feed.ts);
903:
904: if (filter->state >= DMXDEV_STATE_GO)
905: return dvb_dmxdev_start_feed(dmxdev, filter, feed);
906:
907: return 0;
908: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 8/2 |
2026/05/24 21:40 |
llm |
expert |
0m
Model:
Tokens:
input: 9944
output: 55
thoughts: 52
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience:
**Analyzing a Potential Memory Leak**
Alright, I'm tracking down this memory leak. The key is `feed`, which was allocated at line 897. Now, it seems this `feed` is then incorporated into `filter->feed.ts` at line 902. Given that, I need to check out what `dvb_dmxdev_pes_filter_set` does with that data. It's likely that function is the culprit, so it needs closer inspection, and I need to determine how `feed` is handled throughout. I'll focus on where this memory gets freed, or if it ever does. I'll need to think about reference counting, and potential double-frees if this isn't handled correctly. It's time to dig into that function's code.
|
| 9/2 |
2026/05/24 21:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_dmxdev_pes_filter_set]
Results: map[SourceCode: 953: static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
954: struct dmxdev_filter *dmxdevfilter,
955: struct dmx_pes_filter_params *params)
956: {
957: int ret;
958:
959: dvb_dmxdev_filter_stop(dmxdevfilter);
960: dvb_dmxdev_filter_reset(dmxdevfilter);
961:
962: if ((unsigned int)params->pes_type > DMX_PES_OTHER)
963: return -EINVAL;
964:
965: dmxdevfilter->type = DMXDEV_TYPE_PES;
966: memcpy(&dmxdevfilter->params, params,
967: sizeof(struct dmx_pes_filter_params));
968: INIT_LIST_HEAD(&dmxdevfilter->feed.ts);
969:
970: dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
971:
972: ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter,
973: dmxdevfilter->params.pes.pid);
974: if (ret < 0)
975: return ret;
976:
977: if (params->flags & DMX_IMMEDIATE_START)
978: return dvb_dmxdev_filter_start(dmxdevfilter);
979:
980: return 0;
981: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 10/2 |
2026/05/24 21:40 |
llm |
expert |
0m
Model:
Tokens:
input: 10750
output: 53
thoughts: 135
Thoughts: **DVB PES Filter Setup: A Potential Leak**
Okay, so `dvb_dmxdev_pes_filter_set` is responsible for configuring a PES filter for our DVB device. It starts by stopping and resetting the filter using `dvb_dmxdev_filter_stop` and `dvb_dmxdev_filter_reset`, which seems sensible β clean slate, good. Then it explicitly sets the filter type to `DMXDEV_TYPE_PES` and copies over the necessary parameters. No surprises there.
But then... wait a minute. It calls `INIT_LIST_HEAD(&dmxdevfilter->feed.ts)`. This is where I get concerned. `INIT_LIST_HEAD` initializes a list head, which is fine if the list is empty. However, if the `ts` list already contained elements from a previous configuration, `INIT_LIST_HEAD` will just overwrite the head, effectively leaking those existing list elements! We could be losing track of buffers or other important data. This needs to be verified. Let me take a look at the `dvb_dmxdev_filter_reset` function to understand what's happening to the list within that function.
|
| 11/2 |
2026/05/24 21:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_dmxdev_filter_reset]
Results: map[SourceCode: 602: static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter)
603: {
604: if (dmxdevfilter->state < DMXDEV_STATE_SET)
605: return 0;
606:
607: if (dmxdevfilter->type == DMXDEV_TYPE_PES)
608: dvb_dmxdev_delete_pids(dmxdevfilter);
609:
610: dmxdevfilter->type = DMXDEV_TYPE_NONE;
611: dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
612: return 0;
613: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 12/2 |
2026/05/24 21:41 |
llm |
expert |
0m
Model:
Tokens:
input: 11444
output: 53
thoughts: 103
Thoughts: Okay, let's break this down.
**Diving Deep into DVB Demux Filter Reset and PID Handling**
Right, so `dvb_dmxdev_filter_reset` calls `dvb_dmxdev_delete_pids` β that makes perfect sense. But only if the `dmxdevfilter->type` is `DMXDEV_TYPE_PES`. Okay, I've got that. My question is, *what happens* if the `dmxdevfilter->type` is set to `DMXDEV_TYPE_SEC`? Does it skip the PID deletion entirely in that scenario? That seems a bit odd; usually, you'd expect *some* form of cleanup or reset regardless of the filter type, wouldn't you? There's a potential gap in logic there.
I need to see how the filter is *set* in the first place. I should look into `dvb_dmxdev_filter_set`. I'm assuming that function is likely the handler for the `DVB_DEMUX_DMX_SET_FILTER` ioctl. It will probably be revealing to understand if it's dependent on the `dmxdevfilter->type` during set-up. It's time to dig into that function's implementation. Knowing how the filter is *initialized* will provide the context I need to figure out this potential vulnerability/issue in the reset procedure.
|
| 13/2 |
2026/05/24 21:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_dmxdev_filter_set]
Results: map[SourceCode: 932: static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
933: struct dmxdev_filter *dmxdevfilter,
934: struct dmx_sct_filter_params *params)
935: {
936: dprintk("%s: PID=0x%04x, flags=%02x, timeout=%d\n",
937: __func__, params->pid, params->flags, params->timeout);
938:
939: dvb_dmxdev_filter_stop(dmxdevfilter);
940:
941: dmxdevfilter->type = DMXDEV_TYPE_SEC;
942: memcpy(&dmxdevfilter->params.sec,
943: params, sizeof(struct dmx_sct_filter_params));
944: invert_mode(&dmxdevfilter->params.sec.filter);
945: dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
946:
947: if (params->flags & DMX_IMMEDIATE_START)
948: return dvb_dmxdev_filter_start(dmxdevfilter);
949:
950: return 0;
951: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 14/2 |
2026/05/24 21:41 |
llm |
expert |
0m
Model:
Tokens:
input: 12306
output: 50
thoughts: 640
Thoughts: **Demux Filter Type Mishandling Leads to Feed Leaks**
Okay, here's what's bothering me. I've been digging into this `dvb_dmxdev_filter_set` function and it seems to have a problem. Specifically, it sets `dmxdevfilter->type` to `DMXDEV_TYPE_SEC` without doing any cleanup, like calling `dvb_dmxdev_filter_reset` or freeing the PES feeds. That's immediately suspect.
If we have a filter that was initially configured for PES, and then `dvb_dmxdev_filter_set` is called on it, its `type` is changed to `DMXDEV_TYPE_SEC`. The problem is that a subsequent call to `dvb_dmxdev_pes_filter_set` will then call `dvb_dmxdev_filter_reset`. This function checks if `type == DMXDEV_TYPE_PES` which it won't be, so it skips the call to `dvb_dmxdev_delete_pids`. This means the existing feeds aren't freed. Then, `dvb_dmxdev_pes_filter_set` then initializes a new `feed.ts` list, leaking the old, now orphaned, feeds!
Let's trace through a sequence to confirm this. We start with:
1. `ioctl(DVB_DEMUX_DMX_SET_PES_FILTER)`: This calls `dvb_dmxdev_pes_filter_set`. This sets `type` to `DMXDEV_TYPE_PES` and `dvb_dmxdev_add_pid` allocates a `feed` and adds it to `feed.ts`.
2. `ioctl(DVB_DEMUX_DMX_SET_FILTER)`: This calls `dvb_dmxdev_filter_set`. It calls `dvb_dmxdev_filter_stop(dmxdevfilter)`. Critically, it sets `dmxdevfilter->type` to `DMXDEV_TYPE_SEC`. It doesn't clean up the old feeds.
3. `ioctl(DVB_DEMUX_DMX_SET_PES_FILTER)` (or closing the file, or any other call that effectively re-configures the filter): If we set a PES filter again, `dvb_dmxdev_pes_filter_set` calls `dvb_dmxdev_filter_reset`. `dvb_dmxdev_filter_reset` checks `if (dmxdevfilter->type == DMXDEV_TYPE_PES)`, but it's not. Therefore, `dvb_dmxdev_delete_pids` is skipped. Then, `dvb_dmxdev_pes_filter_set` re-initializes `INIT_LIST_HEAD(&dmxdevfilter->feed.ts)`, leaking the original list of feeds.
Okay, now, the reproducer specifically does:
1. `ioctl(DVB_DEMUX_DMX_SET_PES_FILTER)`
2. `ioctl(DVB_DEMUX_DMX_SET_FILTER)`
3. Process exits.
So, when the process exits, the file is closed. This could be where the leak comes into play. I need to investigate the close path closely. It sounds like there's a good chance of a serious resource leak here if this isn't handled correctly during file closing.
|
| 15/2 |
2026/05/24 21:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_demux_release]
Results: map[SourceCode:1243: static int dvb_demux_release(struct inode *inode, struct file *file)
1244: {
1245: struct dmxdev_filter *dmxdevfilter = file->private_data;
1246: struct dmxdev *dmxdev = dmxdevfilter->dev;
1247: int ret;
1248:
1249: ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
1250:
1251: mutex_lock(&dmxdev->mutex);
1252: dmxdev->dvbdev->users--;
1253: if (dmxdev->dvbdev->users == 1 && dmxdev->exit == 1) {
1254: mutex_unlock(&dmxdev->mutex);
1255: wake_up(&dmxdev->dvbdev->wait_queue);
1256: } else
1257: mutex_unlock(&dmxdev->mutex);
1258:
1259: return ret;
1260: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 16/2 |
2026/05/24 21:41 |
llm |
expert |
0m
Model:
Tokens:
input: 14083
output: 53
thoughts: 16
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing `dvb_dmxdev_filter_free`**
Alright, `dvb_dmxdev_filter_free`. Time to assess this function. I'm immediately thinking about its role within the DVB subsystem, specifically the demux device. My brain is already mapping out the data structures involved: the `dvb_demux_filter` itself, the parent `dvb_demux` instance, and how they relate. Iβm thinking about the potential implications for resource management β what specifically needs to be cleaned up when a filter is freed?
I'm mentally running through the typical lifecycle of a demux filter: allocation (likely `dvb_dmxdev_filter_alloc` or a similar function), initialization, configuration, and then, finally, deallocation when no longer needed. So, `dvb_dmxdev_filter_free` would be the *final* step, the cleanup stage.
I'm probably going to need to look into how this function interacts with any associated buffers, memory allocations, or DMA settings. Does it handle unregistering the filter from the demux? De-allocating any internal data structures? Maybe even releasing locks to prevent memory corruption if there are other processes interacting with this device. I need to keep an eye on concurrency issues too. Are we safely unlinking it from any lists? Are there potential race conditions with other operations?
I anticipate that there will be error handling here, which is key. What happens if a resource can't be freed for some reason? Does it return an error code? How is that error handled upstream? More likely, this will be some kind of robust locking mechanism, with error-checking to be sure no resources are orphaned. I'm also wondering if it calls any callbacks or uses a work queue to ensure that no important tasks are missed when a filter is cleaned up. Finally, I'll need to check the surrounding code for how this function is *used* β where are the calls to `dvb_dmxdev_filter_free` made? Understanding the call sites is crucial to grasping the overall architecture and potential issues. Let's see what's what.
|
| 17/2 |
2026/05/24 21:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_dmxdev_filter_free]
Results: map[SourceCode: 846: static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev,
847: struct dmxdev_filter *dmxdevfilter)
848: {
849: mutex_lock(&dmxdev->mutex);
850: mutex_lock(&dmxdevfilter->mutex);
851: if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx))
852: dvb_vb2_stream_off(&dmxdevfilter->vb2_ctx);
853: dvb_vb2_release(&dmxdevfilter->vb2_ctx);
854:
855:
856: dvb_dmxdev_filter_stop(dmxdevfilter);
857: dvb_dmxdev_filter_reset(dmxdevfilter);
858:
859: if (dmxdevfilter->buffer.data) {
860: void *mem = dmxdevfilter->buffer.data;
861:
862: spin_lock_irq(&dmxdev->lock);
863: dmxdevfilter->buffer.data = NULL;
864: spin_unlock_irq(&dmxdev->lock);
865: vfree(mem);
866: }
867:
868: dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
869: wake_up(&dmxdevfilter->buffer.queue);
870: mutex_unlock(&dmxdevfilter->mutex);
871: mutex_unlock(&dmxdev->mutex);
872: return 0;
873: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 18/2 |
2026/05/24 21:41 |
llm |
expert |
0m
Model:
Tokens:
input: 15151
output: 53
thoughts: 718
Thoughts: **Analyzing a DVB Demux Memory Leak**
Okay, so here's what's going on. `dvb_dmxdev_filter_free` is calling `dvb_dmxdev_filter_reset`, and I need to figure out if we have a memory leak here. I know `dvb_dmxdev_filter_reset` only calls `dvb_dmxdev_delete_pids` if the `dmxdevfilter->type` is `DMXDEV_TYPE_PES`. The crucial part is that `dvb_dmxdev_filter_set` changes `type` to `DMXDEV_TYPE_SEC`, which means `dvb_dmxdev_delete_pids` *isn't* called. This means the `feed` allocated in `dvb_dmxdev_add_pid` is leaking.
Pure memory leak, that's what it looks like. Can this be weaponized, though? A memory leak could be a Denial of Service (DoS) vulnerability through out-of-memory. But is it even *accessible* to unprivileged processes? The reproducer uses `/dev/dvb/adapter#/demux#`, so let's check the device permissions. DVB devices are usually owned by `root:video` with `0660` permissions. So, an unprivileged user wouldn't normally have access unless they're in the `video` group, or if ACLs are in play (like logind granting access to the active user).
Wait, hang on. Are DVB devices *generally* accessible to unprivileged users? Let's figure out if this can be triggered by a typical user. If they're logged in locally, udev/logind ACLs might give them access. But, is the typical case the deciding factor? The prompt says "accessible from an unprivileged user that does NOT have special capabilities or access". Since `/dev/dvb/adapter...` is normally restricted to `root`, `video`, or a logged in user, it might be restricted. But if a user *does* have access to the device node, they can trigger it.
Okay, other impact? Besides a memory leak... nope, it's just a `kzalloc` that's never freed.
Wait a minute, what happens if we do something clever? What if we call `dvb_dmxdev_filter_set` (SEC) and *then* `dvb_dmxdev_pes_filter_set` (PES)? Let's break it down:
1. `PES` filter set -> allocates a `feed`, and adds it to the `feed.ts`.
2. `SEC` filter set -> changes the `type` to `SEC`.
3. `PES` filter set -> `dvb_dmxdev_filter_reset` does nothing because `type == SEC`. *Then* `INIT_LIST_HEAD(&dmxdevfilter->feed.ts)` is called, which leaks the old `feed`. Then `dvb_dmxdev_add_pid` allocates a new `feed` and adds it, so thatβs another memory leak.
Okay, but are there any use-after-free or other memory corruption risks? I need to know if the demux hardware/software is *still* using the `feed` if `type` is changed to `SEC`. I need to go check out `dvb_dmxdev_filter_stop` to be sure.
|
| 19/2 |
2026/05/24 21:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_dmxdev_filter_stop]
Results: map[SourceCode: 551: static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
552: {
553: struct dmxdev_feed *feed;
554: struct dmx_demux *demux;
555:
556: if (dmxdevfilter->state < DMXDEV_STATE_GO)
557: return 0;
558:
559: switch (dmxdevfilter->type) {
560: case DMXDEV_TYPE_SEC:
561: if (!dmxdevfilter->feed.sec)
562: break;
563: dvb_dmxdev_feed_stop(dmxdevfilter);
564: if (dmxdevfilter->filter.sec)
565: dmxdevfilter->feed.sec->
566: release_filter(dmxdevfilter->feed.sec,
567: dmxdevfilter->filter.sec);
568: dvb_dmxdev_feed_restart(dmxdevfilter);
569: dmxdevfilter->feed.sec = NULL;
570: break;
571: case DMXDEV_TYPE_PES:
572: dvb_dmxdev_feed_stop(dmxdevfilter);
573: demux = dmxdevfilter->dev->demux;
574: list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
575: demux->release_ts_feed(demux, feed->ts);
576: feed->ts = NULL;
577: }
578: break;
579: default:
580: if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED)
581: return 0;
582: return -EINVAL;
583: }
584:
585: dvb_ringbuffer_flush(&dmxdevfilter->buffer);
586: return 0;
587: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 20/2 |
2026/05/24 21:41 |
llm |
expert |
0m
Model:
Tokens:
input: 17295
output: 53
thoughts: 98
Thoughts: **Analyzing the DVB Demux Filter Lifecycle**
Right, so `dvb_dmxdev_filter_stop` is using `dmxdevfilter->type` as the crucial indicator for how to properly halt a filter operation. Makes sense. If we're working with a `PES` filter configuration, that `type` gets explicitly assigned as `PES`. And that's where things get interesting. We're also allocating a `feed` structure. Further, if the `DMX_IMMEDIATE_START` flag is enabled during that PES filter setup, it immediately calls `dvb_dmxdev_filter_start`. I need to dig into `dvb_dmxdev_filter_start` now to see what's actually happening at that point. Gotta understand the complete start-to-stop lifecycle for these DMX filters.
|
| 21/2 |
2026/05/24 21:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_dmxdev_filter_start]
Results: map[SourceCode: 667: static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
668: {
669: struct dmxdev *dmxdev = filter->dev;
670: struct dmxdev_feed *feed;
671: void *mem;
672: int ret, i;
673:
674: if (filter->state < DMXDEV_STATE_SET)
675: return -EINVAL;
676:
677: if (filter->state >= DMXDEV_STATE_GO)
678: dvb_dmxdev_filter_stop(filter);
679:
680: if (!filter->buffer.data) {
681: mem = vmalloc(filter->buffer.size);
682: if (!mem)
683: return -ENOMEM;
684: spin_lock_irq(&filter->dev->lock);
685: filter->buffer.data = mem;
686: spin_unlock_irq(&filter->dev->lock);
687: }
688:
689: dvb_ringbuffer_flush(&filter->buffer);
690:
691: switch (filter->type) {
692: case DMXDEV_TYPE_SEC:
693: {
694: struct dmx_sct_filter_params *para = &filter->params.sec;
695: struct dmx_section_filter **secfilter = &filter->filter.sec;
696: struct dmx_section_feed **secfeed = &filter->feed.sec;
697:
698: *secfilter = NULL;
699: *secfeed = NULL;
700:
701:
702: /* find active filter/feed with same PID */
703: for (i = 0; i < dmxdev->filternum; i++) {
704: if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
705: dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&
706: dmxdev->filter[i].params.sec.pid == para->pid) {
707: *secfeed = dmxdev->filter[i].feed.sec;
708: break;
709: }
710: }
711:
712: /* if no feed found, try to allocate new one */
713: if (!*secfeed) {
714: ret = dmxdev->demux->allocate_section_feed(dmxdev->demux,
715: secfeed,
716: dvb_dmxdev_section_callback);
717: if (!*secfeed) {
718: pr_err("DVB (%s): could not alloc feed\n",
719: __func__);
720: return ret;
721: }
722:
723: ret = (*secfeed)->set(*secfeed, para->pid,
724: (para->flags & DMX_CHECK_CRC) ? 1 : 0);
725: if (ret < 0) {
726: pr_err("DVB (%s): could not set feed\n",
727: __func__);
728: dvb_dmxdev_feed_restart(filter);
729: return ret;
730: }
731: } else {
732: dvb_dmxdev_feed_stop(filter);
733: }
734:
735: ret = (*secfeed)->allocate_filter(*secfeed, secfilter);
736: if (ret < 0) {
737: dvb_dmxdev_feed_restart(filter);
738: *secfeed = NULL;
739: dprintk("could not get filter\n");
740: return ret;
741: }
742:
743: (*secfilter)->priv = filter;
744:
745: memcpy(&((*secfilter)->filter_value[3]),
746: &(para->filter.filter[1]), DMX_FILTER_SIZE - 1);
747: memcpy(&(*secfilter)->filter_mask[3],
748: ¶->filter.mask[1], DMX_FILTER_SIZE - 1);
749: memcpy(&(*secfilter)->filter_mode[3],
750: ¶->filter.mode[1], DMX_FILTER_SIZE - 1);
751:
752: (*secfilter)->filter_value[0] = para->filter.filter[0];
753: (*secfilter)->filter_mask[0] = para->filter.mask[0];
754: (*secfilter)->filter_mode[0] = para->filter.mode[0];
755: (*secfilter)->filter_mask[1] = 0;
756: (*secfilter)->filter_mask[2] = 0;
757:
758: filter->todo = 0;
759:
760: ret = filter->feed.sec->start_filtering(filter->feed.sec);
761: if (ret < 0)
762: return ret;
763:
764: dvb_dmxdev_filter_timer(filter);
765: break;
766: }
767: case DMXDEV_TYPE_PES:
768: list_for_each_entry(feed, &filter->feed.ts, next) {
769: ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
770: if (ret < 0) {
771: dvb_dmxdev_filter_stop(filter);
772: return ret;
773: }
774: }
775: break;
776: default:
777: return -EINVAL;
778: }
779:
780: dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
781: return 0;
782: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 22/2 |
2026/05/24 21:41 |
llm |
expert |
0m
Model:
Tokens:
input: 19441
output: 48
thoughts: 198
Thoughts: **Analyzing the DVB Demux Filter Reset**
Okay, so I'm looking at how `dvb_dmxdev_filter_set` works, and I'm a bit concerned. Specifically, I see it does the following:
First, it calls `dvb_dmxdev_filter_stop`, which is good. That makes sure any existing operation is correctly terminated. My main concern right now is what happens to the allocated resources, though.
Then, it changes the filter `type` to `DMXDEV_TYPE_SEC` and copies the new parameters in place. That seems straightforward, copying the new `sec` parameters into the filter.
My concern is what happens when we switch from a running PES filter to a SEC one. `dvb_dmxdev_filter_stop` correctly stops the filter, since the `type` field still contains the original state. However, it *doesn't* free the `feed.ts` list. That's a potential memory leak if the PES filter was previously running and allocated data to this list.
Now, if we call `dvb_dmxdev_filter_start`, which *could* happen immediately if `DMX_IMMEDIATE_START` is set, the system will use the memory allocated for `filter->feed.sec`.
I just realized something: `filter->feed` is a *union*! So, the old `feed.ts` list is still there, but now we're interpreting the same memory as `feed.sec`. This could be a problem, depending on the sizes and usage of these.
|
| 23/2 |
2026/05/24 21:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dmxdev_filter]
Results: map[SourceCode: 89: /**
90: * struct dmxdev_filter - digital TV dmxdev filter
91: *
92: * @filter: a union describing a dmxdev filter.
93: * Currently used only for section filters.
94: * @filter.sec: a &struct dmx_section_filter pointer.
95: * For section filter only.
96: * @feed: a union describing a dmxdev feed.
97: * Depending on the filter type, it can be either
98: * @feed.ts or @feed.sec.
99: * @feed.ts: a &struct list_head list.
100: * For TS and PES feeds.
101: * @feed.sec: a &struct dmx_section_feed pointer.
102: * For section feed only.
103: * @params: a union describing dmxdev filter parameters.
104: * Depending on the filter type, it can be either
105: * @params.sec or @params.pes.
106: * @params.sec: a &struct dmx_sct_filter_params embedded struct.
107: * For section filter only.
108: * @params.pes: a &struct dmx_pes_filter_params embedded struct.
109: * For PES filter only.
110: * @type: type of the dmxdev filter, as defined by &enum dmxdev_type.
111: * @state: state of the dmxdev filter, as defined by &enum dmxdev_state.
112: * @dev: pointer to &struct dmxdev.
113: * @buffer: an embedded &struct dvb_ringbuffer buffer.
114: * @vb2_ctx: control struct for VB2 handler
115: * @mutex: protects the access to &struct dmxdev_filter.
116: * @timer: &struct timer_list embedded timer, used to check for
117: * feed timeouts.
118: * Only for section filter.
119: * @todo: index for the @secheader.
120: * Only for section filter.
121: * @secheader: buffer cache to parse the section header.
122: * Only for section filter.
123: */
124: struct dmxdev_filter {
125: union {
126: struct dmx_section_filter *sec;
127: } filter;
128:
129: union {
130: /* list of TS and PES feeds (struct dmxdev_feed) */
131: struct list_head ts;
132: struct dmx_section_feed *sec;
133: } feed;
134:
135: union {
136: struct dmx_sct_filter_params sec;
137: struct dmx_pes_filter_params pes;
138: } params;
139:
140: enum dmxdev_type type;
141: enum dmxdev_state state;
142: struct dmxdev *dev;
143: struct dvb_ringbuffer buffer;
144: struct dvb_vb2_ctx vb2_ctx;
145:
146: struct mutex mutex;
147:
148: /* only for sections */
149: struct timer_list timer;
150: int todo;
151: u8 secheader[3];
152: };
SourceFile:include/media/dmxdev.h]
|
| 24/2 |
2026/05/24 21:42 |
llm |
expert |
0m
Model:
Tokens:
input: 21081
output: 52
thoughts: 1553
Thoughts: **Analyzing the DVB Demux Feed Management**
Okay, so I'm looking at this `feed` structure, and it's a union. That means `feed.ts` and `feed.sec` are sharing the same memory. That's immediately a red flag. When `dvb_dmxdev_filter_set` is called for a SEC filter, it sets `type` to `SEC`, and if `DMX_IMMEDIATE_START` is set, it kicks off `dvb_dmxdev_filter_start`.
In `dvb_dmxdev_filter_start`, specifically for `SEC` filters, it first initializes `secfilter` and `secfeed` to `NULL`. The crucial line here is setting `*secfeed = NULL;`. `secfeed` points to `filter->feed.sec`. Since `feed` is a union, that means I'm overwriting `filter->feed.ts.next` (or `prev`) with `NULL`. That's corrupting the list head.
A `struct list_head` has `next` and `prev` pointers, but a `dmx_section_feed` is just a single pointer. Setting `*secfeed = NULL` overwrites the first pointer of `feed.ts`! This means the list head is compromised. The memory leak report probably stems from the lost `feed` object. I need to be on the lookout for a use-after-free or other memory corruption. If `filter->feed.ts` is overwritten, the list is lostβleaked, in effect.
Now, before the `*secfeed = NULL`, there's a loop that checks if another active filter with the same PID already exists. If so, it sets `*secfeed` to *that* feed. Otherwise, it *does* set `*secfeed = NULL` and then allocates a new section feed. It then overwrites `filter->feed.sec` with a valid pointer. So, now, the list head has been corrupted.
However, if `dvb_dmxdev_pes_filter_set` is called later, it correctly calls `dvb_dmxdev_filter_stop`, which cleans up `SEC` feeds. Then `dvb_dmxdev_filter_reset` correctly skips `dvb_dmxdev_delete_pids`. After this, it calls `INIT_LIST_HEAD(&dmxdevfilter->feed.ts)`, which then leaks all the old `feed` objects that were in the list.
So, where's the potential for a use-after-free or list corruption? What happens if `dvb_dmxdev_filter_set` (SEC) is called on a filter that's currently a PES filter, but *without* `DMX_IMMEDIATE_START`? In this scenario, `filter->feed.sec` is *not* modified in `dvb_dmxdev_filter_set`! Since they share memory, `filter->feed.sec` ends up containing whatever was in `filter->feed.ts.next`. This will be a *wild pointer* (pointing to the next `struct dmxdev_feed` in the list, or `&filter->feed.ts` if it was empty)! If I can call `DVB_DEMUX_START` later, it might be possible to trigger a use-after-free, or corrupted memory.
|
| 25/2 |
2026/05/24 21:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_demux_do_ioctl]
Results: map[SourceCode:1045: static int dvb_demux_do_ioctl(struct file *file,
1046: unsigned int cmd, void *parg)
1047: {
1048: struct dmxdev_filter *dmxdevfilter = file->private_data;
1049: struct dmxdev *dmxdev = dmxdevfilter->dev;
1050: unsigned long arg = (unsigned long)parg;
1051: int ret = 0;
1052:
1053: if (mutex_lock_interruptible(&dmxdev->mutex))
1054: return -ERESTARTSYS;
1055:
1056: switch (cmd) {
1057: case DMX_START:
1058: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1059: mutex_unlock(&dmxdev->mutex);
1060: return -ERESTARTSYS;
1061: }
1062: if (dmxdevfilter->state < DMXDEV_STATE_SET)
1063: ret = -EINVAL;
1064: else
1065: ret = dvb_dmxdev_filter_start(dmxdevfilter);
1066: mutex_unlock(&dmxdevfilter->mutex);
1067: break;
1068:
1069: case DMX_STOP:
1070: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1071: mutex_unlock(&dmxdev->mutex);
1072: return -ERESTARTSYS;
1073: }
1074: ret = dvb_dmxdev_filter_stop(dmxdevfilter);
1075: mutex_unlock(&dmxdevfilter->mutex);
1076: break;
1077:
1078: case DMX_SET_FILTER:
1079: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1080: mutex_unlock(&dmxdev->mutex);
1081: return -ERESTARTSYS;
1082: }
1083: ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter, parg);
1084: mutex_unlock(&dmxdevfilter->mutex);
1085: break;
1086:
1087: case DMX_SET_PES_FILTER:
1088: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1089: mutex_unlock(&dmxdev->mutex);
1090: return -ERESTARTSYS;
1091: }
1092: ret = dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter, parg);
1093: mutex_unlock(&dmxdevfilter->mutex);
1094: break;
1095:
1096: case DMX_SET_BUFFER_SIZE:
1097: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1098: mutex_unlock(&dmxdev->mutex);
1099: return -ERESTARTSYS;
1100: }
1101: ret = dvb_dmxdev_set_buffer_size(dmxdevfilter, arg);
1102: mutex_unlock(&dmxdevfilter->mutex);
1103: break;
1104:
1105: case DMX_GET_PES_PIDS:
1106: if (!dmxdev->demux->get_pes_pids) {
1107: ret = -EINVAL;
1108: break;
1109: }
1110: dmxdev->demux->get_pes_pids(dmxdev->demux, parg);
1111: break;
1112:
1113: case DMX_GET_STC:
1114: if (!dmxdev->demux->get_stc) {
1115: ret = -EINVAL;
1116: break;
1117: }
1118: ret = dmxdev->demux->get_stc(dmxdev->demux,
1119: ((struct dmx_stc *)parg)->num,
1120: &((struct dmx_stc *)parg)->stc,
1121: &((struct dmx_stc *)parg)->base);
1122: break;
1123:
1124: case DMX_ADD_PID:
1125: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1126: ret = -ERESTARTSYS;
1127: break;
1128: }
1129: ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
1130: mutex_unlock(&dmxdevfilter->mutex);
1131: break;
1132:
1133: case DMX_REMOVE_PID:
1134: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1135: ret = -ERESTARTSYS;
1136: break;
1137: }
1138: ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
1139: mutex_unlock(&dmxdevfilter->mutex);
1140: break;
1141:
1142: #ifdef CONFIG_DVB_MMAP
1143: case DMX_REQBUFS:
1144: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1145: mutex_unlock(&dmxdev->mutex);
1146: return -ERESTARTSYS;
1147: }
1148: ret = dvb_vb2_reqbufs(&dmxdevfilter->vb2_ctx, parg);
1149: mutex_unlock(&dmxdevfilter->mutex);
1150: break;
1151:
1152: case DMX_QUERYBUF:
1153: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1154: mutex_unlock(&dmxdev->mutex);
1155: return -ERESTARTSYS;
1156: }
1157: ret = dvb_vb2_querybuf(&dmxdevfilter->vb2_ctx, parg);
1158: mutex_unlock(&dmxdevfilter->mutex);
1159: break;
1160:
1161: case DMX_EXPBUF:
1162: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1163: mutex_unlock(&dmxdev->mutex);
1164: return -ERESTARTSYS;
1165: }
1166: ret = dvb_vb2_expbuf(&dmxdevfilter->vb2_ctx, parg);
1167: mutex_unlock(&dmxdevfilter->mutex);
1168: break;
1169:
1170: case DMX_QBUF:
1171: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1172: mutex_unlock(&dmxdev->mutex);
1173: return -ERESTARTSYS;
1174: }
1175: ret = dvb_vb2_qbuf(&dmxdevfilter->vb2_ctx, parg);
1176: if (ret == 0 && !dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx))
1177: ret = dvb_vb2_stream_on(&dmxdevfilter->vb2_ctx);
1178: mutex_unlock(&dmxdevfilter->mutex);
1179: break;
1180:
1181: case DMX_DQBUF:
1182: if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
1183: mutex_unlock(&dmxdev->mutex);
1184: return -ERESTARTSYS;
1185: }
1186: ret = dvb_vb2_dqbuf(&dmxdevfilter->vb2_ctx, parg);
1187: mutex_unlock(&dmxdevfilter->mutex);
1188: break;
1189: #endif
1190: default:
1191: ret = -ENOTTY;
1192: break;
1193: }
1194: mutex_unlock(&dmxdev->mutex);
1195: return ret;
1196: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 26/2 |
2026/05/24 21:42 |
llm |
expert |
0m
Model:
Tokens:
input: 25857
output: 53
thoughts: 1854
Thoughts: **Analysis of DVB Demux Filter State Transitions**
Alright, let's break this down. My initial thought process involves tracing the execution paths of the `ioctl` calls related to DVB demux filters, specifically focusing on the interactions between PES and SEC filters and how their associated feed structures are managed. The goal is to identify any potential wild pointer dereferences, memory leaks, or other subtle issues.
First, I examined the setup of a PES filter using `ioctl(DVB_DEMUX_DMX_SET_PES_FILTER)`. I see that `dvb_dmxdev_pes_filter_set` is called, a feed is allocated and added to the `feed.ts` list, and the filter's state is set to `DMXDEV_STATE_SET`.
Next, I looked at the setup of a SEC filter with `ioctl(DVB_DEMUX_DMX_SET_FILTER)`. Crucially, `dvb_dmxdev_filter_stop` is called, but it does nothing because the filter isn't in a running state. The `feed.sec` member ends up containing a pointer previously pointed to by `feed.ts.next`. This is a potential point of interest.
The subsequent `ioctl(DVB_DEMUX_START)` is where things get interesting. `dvb_dmxdev_filter_start` allocates a new section feed. It does this *after* setting the `secfeed` pointer (which is `&filter->feed.sec`) to `NULL`. This overwrites the potentially stale pointer. My main concern was a dangling pointer dereference before it's overwritten, but that doesn't seem to happen here. The crucial observation is that the old `feed` object is, indeed, leaked because it's never deallocated.
Then I considered all possible paths where `filter->feed.sec` *might* be dereferenced *before* it gets overwritten, but those paths don't exist.
I also checked if calling `DVB_DEMUX_STOP` *before* `START` has any impact. It doesn't, because the state is less than `DMXDEV_STATE_GO`.
Then I investigated `dvb_dmxdev_filter_free`, called on close. It stops and resets the filter, but there's no dereference there either.
If I set a PES filter and then immediately set a SEC filter, `dvb_dmxdev_filter_stop` handles stopping the PES filter correctly. The PES feeds are properly stopped and deallocated.
Then I double-checked other IOCTLs that might touch `feed.sec` or `feed.ts`. `DMX_ADD_PID` will error out because you can't add to a SEC filter. `DMX_REMOVE_PID` wouldn't touch it either.
Finally, I considered the scenario where a PES filter is started *with* the `DMX_IMMEDIATE_START` flag set, and then I try to create a SEC filter. This calls `dvb_dmxdev_filter_stop`, which *does* a clean-up because `state == DMXDEV_STATE_GO`. Then the SEC filter's `feed.sec` member is again set to `NULL` before being allocated.
So, while there's clearly a memory leak, the primary concern of a wild pointer dereference doesn't appear to be present in any of the traced code paths. The code does correctly overwrite the potentially dangerous stale pointers, which prevents a crash. However, the old `feed` objects are leaked, so there is room for resource management improvement.
|
| 27/2 |
2026/05/24 21:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_dmxdev_remove_pid]
Results: map[SourceCode: 910: static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev,
911: struct dmxdev_filter *filter, u16 pid)
912: {
913: struct dmxdev_feed *feed, *tmp;
914:
915: if ((filter->type != DMXDEV_TYPE_PES) ||
916: (filter->state < DMXDEV_STATE_SET))
917: return -EINVAL;
918:
919: list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) {
920: if ((feed->pid == pid) && (feed->ts != NULL)) {
921: feed->ts->stop_filtering(feed->ts);
922: filter->dev->demux->release_ts_feed(filter->dev->demux,
923: feed->ts);
924: list_del(&feed->next);
925: kfree(feed);
926: }
927: }
928:
929: return 0;
930: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 28/2 |
2026/05/24 21:42 |
llm |
expert |
0m
Model:
Tokens:
input: 28912
output: 54
thoughts: 44
Thoughts: Okay, here's my interpretation of those thoughts, summarized from my perspective as someone familiar with DVB device drivers:
**Analyzing PID Removal in DVB Demuxing**
Alright, I'm thinking about the DVB demuxer code, specifically around PID (Packet Identifier) removal. I see that `dvb_dmxdev_remove_pid` includes a check: `filter->type != DMXDEV_TYPE_PES`. That's standard, good practice to ensure it's handling the right type of filter. But now I'm thinking: What about the broader picture? What is the function of `dvb_dmxdev_delete_pids`? I need to look at how that interacts with the removal. I'll need to understand how the system manages the demux filters more thoroughly to make certain those functions play well with one another. I need to make sure that the PID management is coherent across those functions.
|
| 29/2 |
2026/05/24 21:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_dmxdev_delete_pids]
Results: map[SourceCode: 589: static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter)
590: {
591: struct dmxdev_feed *feed, *tmp;
592:
593: /* delete all PIDs */
594: list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) {
595: list_del(&feed->next);
596: kfree(feed);
597: }
598:
599: BUG_ON(!list_empty(&dmxdevfilter->feed.ts));
600: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 30/2 |
2026/05/24 21:42 |
llm |
expert |
0m
Model:
Tokens:
input: 29434
output: 53
thoughts: 473
Thoughts: **Analyzing Potential Memory Leaks in DVB Demux Driver**
Okay, let's break this down. My initial thought is that `dvb_dmxdev_delete_pids` is only ever called within `dvb_dmxdev_filter_reset`, and only when the filter `type` is `DMXDEV_TYPE_PES`. This suggests that the wild pointer or list corruption issues are likely mitigated because all access to the `feed.ts` list is guarded by checking that `type == DMXDEV_TYPE_PES`, and access to `feed.sec` list is guarded by checking `type == DMXDEV_TYPE_SEC`.
It looks like we have a potential leak on type changes, though. If the `type` changes from `PES` to `SEC`, the `feed.ts` list is leaked, but it's not accessed afterwards, so the impact is limited. Similarly, when the `type` changes from `SEC` to `PES`, `dvb_dmxdev_pes_filter_set` re-initializes the `feed.ts` list head using `INIT_LIST_HEAD(&dmxdevfilter->feed.ts)`, effectively overwriting the contents of `feed.sec` with a new, valid list head. Is the old `feed.sec` leaked?
Hold on... I need to consider a specific scenario: What happens if `type` is currently `SEC`, and then `dvb_dmxdev_pes_filter_set` is called? The critical section here is:
```c
dvb_dmxdev_filter_stop(dmxdevfilter);
dvb_dmxdev_filter_reset(dmxdevfilter);
```
`dvb_dmxdev_filter_stop` will be called first. Because the current `type` is `SEC`, the following code path will be executed:
```c
case DMXDEV_TYPE_SEC:
if (!dmxdevfilter->feed.sec)
break;
dvb_dmxdev_feed_stop(dmxdevfilter);
if (dmxdevfilter->filter.sec)
dmxdevfilter->feed.sec->
release_filter(dmxdevfilter->feed.sec,
dmxdevfilter->filter.sec);
dvb_dmxdev_feed_restart(dmxdevfilter);
dmxdevfilter->feed.sec = NULL;
break;
```
Interesting... It looks like `dvb_dmxdev_feed_restart` is called immediately following this. What's happening in there? Let me check what that function does...
|
| 31/2 |
2026/05/24 21:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_dmxdev_feed_restart]
Results: map[SourceCode: 529: /* restart section feed if it has filters left associated with it,
530: otherwise release the feed */
531: static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter)
532: {
533: int i;
534: struct dmxdev *dmxdev = filter->dev;
535: u16 pid = filter->params.sec.pid;
536:
537: for (i = 0; i < dmxdev->filternum; i++)
538: if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
539: dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&
540: dmxdev->filter[i].params.sec.pid == pid) {
541: dvb_dmxdev_feed_start(&dmxdev->filter[i]);
542: return 0;
543: }
544:
545: filter->dev->demux->release_section_feed(dmxdev->demux,
546: filter->feed.sec);
547:
548: return 0;
549: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 32/2 |
2026/05/24 21:42 |
llm |
expert |
0m
Model:
Tokens:
input: 30930
output: 25
thoughts: 2159
Thoughts: **Analysis of DVB Demux Filter State Transitions and Potential Memory Leaks**
Okay, let's break this down. My initial observation was that a memory leak could occur when transitioning a filter from `PES` to `SEC` type, specifically because the `feed.ts` list isn't freed. But I need to be more thorough, let's explore all possible scenarios.
First, I confirmed the clean-up of `feed.sec` in the standard flow. `dvb_dmxdev_feed_restart` handles the release, and `dvb_dmxdev_filter_stop` and `dvb_dmxdev_filter_reset` ensure the pointer is nulled out. Good.
The direct PES-to-SEC transition, where `feed.ts` isn't freed, is the simplest bug to understand - a leak. But what else?
I started exploring a scenario where a PES filter is set, *but not started* (state `DMXDEV_STATE_SET`), and then transition it to SEC. In this case, I found a couple of interesting cases:
1. **DMX_SET_FILTER followed by a close**: In this case, `feed.sec` ends up pointing to a `dmxdev_feed` object that's part of the original `feed.ts` list. Then when the filter is freed, that object is leaked.
2. **DMX_SET_FILTER followed by DMX_START**: However, there is no wild pointer dereference here. The key is to notice that `dvb_dmxdev_filter_start` first nulifies `feed.sec`, it then searches for a different active filter with the same PID. If it finds one, it assigns that other filter's `feed.sec` to *our* filter's `feed.sec`. If it *doesn't* find another filter, it then *allocates* a new section feed. That means the `feed.sec` pointer is always valid.
I went back and double-checked all the other code paths, including `dvb_dmxdev_filter_stop`, `dvb_dmxdev_filter_free`, and `dvb_dmxdev_feed_restart`. In all cases, `feed.sec` is either set to a valid pointer or is explicitly checked before use. `dvb_dmxdev_filter_stop` and `dvb_dmxdev_feed_restart` both either don't execute if `state < DMXDEV_STATE_GO`, or always has a valid `feed.sec` pointer.
So, in conclusion, the original hypothesis regarding the PES-to-SEC transition creating a memory leak stands. The `feed.ts` list is not freed. The other scenario of setting a SEC filter on an unstarted PES filter *does* create a memory leak, the `feed` object is leaked. However, `feed.sec` will always be valid, so there are no other potential crash scenarios.
|
| 33/2 |
2026/05/24 21:43 |
tool |
codesearch-find-references |
0m
Args: map[Name:dmxdev_filter::feed]
Results: map[References:[map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_add_pid SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:894] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_add_pid SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:902] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_delete_pids SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:594] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_delete_pids SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:594] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_delete_pids SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:594] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_delete_pids SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:594] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_delete_pids SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:599] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_restart SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:546] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:512] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:512] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:514] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:514] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:514] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:514] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:490] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:490] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:493] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:493] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:493] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_feed_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:493] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:696] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:707] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:760] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:760] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:768] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:768] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:768] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_start SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:768] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:561] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:565] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:566] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:569] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:574] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:574] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:574] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_filter_stop SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:574] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_pes_filter_set SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:968] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_remove_pid SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:919] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_remove_pid SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:919] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_remove_pid SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:919] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:dvb_dmxdev_remove_pid SourceFile:drivers/media/dvb-core/dmxdev.c SourceLine:919]] TruncatedOutput:false]
|
| 34/2 |
2026/05/24 21:43 |
llm |
expert |
0m
Model:
Tokens:
input: 36289
output: 48
thoughts: 445
Thoughts: **Analyzing a Potential Kernel Crash**
Okay, I've just finished tracing all instances of "feed" within `dmxdev.c`. I've honed in on a potentially nasty vulnerability. Specifically, within `dvb_dmxdev_feed_stop`, there's a critical dereference that looks ripe for a crash if conditions are right. If the feed type is `SEC`, the code calls `filter->feed.sec->stop_filtering(filter->feed.sec)`. Simple enough... on the surface.
Now, when is `dvb_dmxdev_feed_stop` actually invoked? Looking at `dvb_dmxdev_filter_stop`, I see a path. If the filter type is also `SEC`, then `dvb_dmxdev_feed_stop` is called. There's a check: `if (!dmxdevfilter->feed.sec) break;` - that's interesting. It looks like we're trying to prevent a NULL pointer dereference. But, here's the rub! If the type was *previously* set to something like `PES` and then changed to `SEC` via `dvb_dmxdev_filter_set`, `feed.sec` could *actually* contain the pointer to `feed.ts.next` (which is a pointer to the `struct dmxdev_feed` itself, or more accurately `&filter->feed.ts`), and that check would be bypassed.
So, `dvb_dmxdev_filter_stop` calls `dvb_dmxdev_feed_stop`, and *then* the problematic dereference occurs: `filter->feed.sec->stop_filtering(filter->feed.sec);`. The code is treating `filter->feed.sec` as a `struct dmx_section_feed`, but in this scenario, itβs a `struct dmxdev_feed`!
This looks like a classic type confusion, potentially leading to a kernel crash. My next step is to examine the layouts of `struct dmx_section_feed` and `struct dmxdev_feed` to confirm my suspicions and to assess the impact. I suspect there's a memory corruption at the very least, and potentially arbitrary code execution.
|
| 35/2 |
2026/05/24 21:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dmxdev_feed]
Results: map[SourceCode: 83: struct dmxdev_feed {
84: u16 pid;
85: struct dmx_ts_feed *ts;
86: struct list_head next;
87: };
SourceFile:include/media/dmxdev.h]
|
| 36/2 |
2026/05/24 21:43 |
llm |
expert |
0m
Model:
Tokens:
input: 37355
output: 49
thoughts: 10
Thoughts: Okay, here's my summary, taking on the persona you've described:
**Delving into the `dmx_section_feed` Structure**
Alright, let's dissect this `dmx_section_feed` struct. Immediately, I see this is at the heart of handling sections within a DVB demuxer context. Given my experience, I'm already anticipating the typical challenges: synchronisation issues, potential race conditions, and of course, memory management complexities. I'm keen to see how this structure elegantly addresses the buffering and delivery of these individual sections. I need to understand how it's designed to interface with other components, like the filter mechanisms. I'm especially interested in how the associated data, such as a PID, is tracked and handled. What locking primitives are employed to guarantee thread safety? Are they using read-write locks, mutexes, or something more sophisticated? Ultimately, the performance implications of the chosen strategy will be critical. I'll need to assess the overall architecture to determine if any opportunities for optimisation exist. I want to see how the implementation handles filtering based on tables and sections. Is there any buffering happening in the structure? Is it designed with efficiency in mind, or is it more of a straightforward, but possibly slower, approach? The devil is always in the details, so let's dig deeper!
|
| 37/2 |
2026/05/24 21:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dmx_section_feed]
Results: map[SourceCode: 138: /**
139: * struct dmx_section_feed - Structure that contains a section feed filter
140: *
141: * @is_filtering: Set to non-zero when filtering in progress
142: * @parent: pointer to struct dmx_demux
143: * @priv: pointer to private data of the API client
144: * @check_crc: If non-zero, check the CRC values of filtered sections.
145: * @set: sets the section filter
146: * @allocate_filter: This function is used to allocate a section filter on
147: * the demux. It should only be called when no filtering
148: * is in progress on this section feed. If a filter cannot
149: * be allocated, the function fails with -ENOSPC.
150: * @release_filter: This function releases all the resources of a
151: * previously allocated section filter. The function
152: * should not be called while filtering is in progress
153: * on this section feed. After calling this function,
154: * the caller should not try to dereference the filter
155: * pointer.
156: * @start_filtering: starts section filtering
157: * @stop_filtering: stops section filtering
158: *
159: * A TS feed is typically mapped to a hardware PID filter on the demux chip.
160: * Using this API, the client can set the filtering properties to start/stop
161: * filtering TS packets on a particular TS feed.
162: */
163: struct dmx_section_feed {
164: int is_filtering;
165: struct dmx_demux *parent;
166: void *priv;
167:
168: int check_crc;
169:
170: /* private: Used internally at dvb_demux.c */
171: u32 crc_val;
172:
173: u8 *secbuf;
174: u8 secbuf_base[DMX_MAX_SECFEED_SIZE];
175: u16 secbufp, seclen, tsfeedp;
176:
177: /* public: */
178: int (*set)(struct dmx_section_feed *feed,
179: u16 pid,
180: int check_crc);
181: int (*allocate_filter)(struct dmx_section_feed *feed,
182: struct dmx_section_filter **filter);
183: int (*release_filter)(struct dmx_section_feed *feed,
184: struct dmx_section_filter *filter);
185: int (*start_filtering)(struct dmx_section_feed *feed);
186: int (*stop_filtering)(struct dmx_section_feed *feed);
187: };
SourceFile:include/media/demux.h]
|
| 38/2 |
2026/05/24 21:43 |
llm |
expert |
0m
Model:
Tokens:
input: 38561
output: 52
thoughts: 4789
Thoughts: **Analysis of a DVB Demux Memory Leak**
Okay, so I've been tracing through this DVB demux code, and it looks like I've found a classic memory leak. The issue stems from how the code handles transitions between PES and SEC filters. Specifically, when you switch from a PES filter to a SEC filter using `dvb_dmxdev_filter_set` (via `DVB_DEMUX_DMX_SET_FILTER` ioctl), but with `DMX_IMMEDIATE_START` also set, it causes a memory leak if PES filters already have associated PIDs.
Here's the breakdown: First I have to understand that `dvb_dmxdev_filter_stop` is only called if `dmxdevfilter->state >= DMXDEV_STATE_GO`. If I call `dvb_dmxdev_filter_set` (SEC) on a PES filter that is NOT started, `state` is `DMXDEV_STATE_SET`.
The problem occurs during a PES to SEC transition. If I call `ioctl(DVB_DEMUX_DMX_SET_FILTER)` to set the filter to SEC, which in turn calls `dvb_dmxdev_filter_stop`. `dvb_dmxdev_filter_stop` stops the PES feeds, but leaves the underlying feed objects (`struct dmxdev_feed`) in the `feed.ts` list. Then when it switches the filter type, the list is effectively lost when transitioning from `PES` to `SEC`, specifically when `dvb_dmxdev_filter_start` is called or even a `DMX_SET_PES_FILTER`. The pointers to these `dmxdev_feed` objects are overwritten, thus leading to the memory leak. These objects are never freed because the `dvb_dmxdev_delete_pids` is only called when the filter is reset to PES, but this code path doesn't get executed. Each time this happens, there's a 32-byte leak per PID, which can snowball quickly if multiple PIDs are added via repeated `DMX_ADD_PID` ioctls.
So, in essence, there's no way to trigger a use-after-free or wild pointer dereference here. The leak happens because the kernel loses track of allocated `struct dmxdev_feed` memory, and it can eventually cause a denial of service (OOM).
Now, the important question: can an unprivileged user exploit this? Generally, no. The DVB device nodes are typically restricted to `root` or the `video` group, meaning a standard unprivileged user wouldn't have access. However, if a system is configured to allow console users to access DVB devices, then a logged-in unprivileged user could trigger this. I'll stick with the assumption that the DVB device nodes are restricted, so access is not typical. Therefore, I will set `Unprivileged: false`. The only way to hit this is to call the ioctls, and the ioctls are behind a permission barrier.
I've checked other union members (params, filter), and other code paths (`dvb_dmxdev_add_pid`, concurrency issues) but they appear to be safe. So, it is only triggered when setting the demux filter, with the leak happening as PIDs are changed with a SEC filter.
|
| 39/2 |
2026/05/24 21:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/media/dvb-core/dmxdev.c IncludeLines:true Name:dvb_dvr_do_ioctl]
Results: map[SourceCode:1286: static int dvb_dvr_do_ioctl(struct file *file,
1287: unsigned int cmd, void *parg)
1288: {
1289: struct dvb_device *dvbdev = file->private_data;
1290: struct dmxdev *dmxdev = dvbdev->priv;
1291: unsigned long arg = (unsigned long)parg;
1292: int ret;
1293:
1294: if (mutex_lock_interruptible(&dmxdev->mutex))
1295: return -ERESTARTSYS;
1296:
1297: switch (cmd) {
1298: case DMX_SET_BUFFER_SIZE:
1299: ret = dvb_dvr_set_buffer_size(dmxdev, arg);
1300: break;
1301:
1302: #ifdef CONFIG_DVB_MMAP
1303: case DMX_REQBUFS:
1304: ret = dvb_vb2_reqbufs(&dmxdev->dvr_vb2_ctx, parg);
1305: break;
1306:
1307: case DMX_QUERYBUF:
1308: ret = dvb_vb2_querybuf(&dmxdev->dvr_vb2_ctx, parg);
1309: break;
1310:
1311: case DMX_EXPBUF:
1312: ret = dvb_vb2_expbuf(&dmxdev->dvr_vb2_ctx, parg);
1313: break;
1314:
1315: case DMX_QBUF:
1316: ret = dvb_vb2_qbuf(&dmxdev->dvr_vb2_ctx, parg);
1317: if (ret == 0 && !dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx))
1318: ret = dvb_vb2_stream_on(&dmxdev->dvr_vb2_ctx);
1319: break;
1320:
1321: case DMX_DQBUF:
1322: ret = dvb_vb2_dqbuf(&dmxdev->dvr_vb2_ctx, parg);
1323: break;
1324: #endif
1325: default:
1326: ret = -ENOTTY;
1327: break;
1328: }
1329: mutex_unlock(&dmxdev->mutex);
1330: return ret;
1331: }
SourceFile:drivers/media/dvb-core/dmxdev.c]
|
| 40/2 |
2026/05/24 21:44 |
llm |
expert |
0m
Model:
Tokens:
input: 44894
output: 66
thoughts: 178
Thoughts: **My Assessment of the DVB Demux Filter Issue**
Okay, so I've confirmed that the `dvr` ioctls are *not* where we'll find `DMX_SET_FILTER` or `DMX_SET_PES_FILTER`. That's straightforward; it's all handled through the `/dev/dvb/adapter#/demux#` device nodes.
Now, digging deeper, I'm pretty confident in my analysis of the bug. It looks like we have a memory leak within `dvb_dmxdev_filter_set`. Specifically, it involves `struct dmxdev_feed` objects. The leak happens when a filter transitions from a `PES` type to a `SEC` type. The code fails to free the `feed.ts` list *before* it changes the filter type and overwrites the list head. That's a classic mistake.
The outcome? A straightforward memory leak, which will lead to a denial-of-service condition eventually. Thankfully, it doesn't appear to be exploitable for memory corruption or privilege escalation, which is a relief. The fact that it requires access to the DVB demux device node, which is typically restricted, further mitigates the risk.
So, this appears to be a case of 'set-results'.
|
| 41/2 |
2026/05/24 21:44 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 42/2 |
2026/05/24 21:44 |
llm |
expert |
0m
Model:
Tokens:
input: 45480
output: 660
thoughts: 357
Thoughts: **Final Thoughts on the DVB Demux Driver Bug**
Okay, the analysis is complete, time to formulate the final response. This DVB demux driver issue is a straightforward memory leak. It's in the way the driver handles filter type changes with `ioctl`. Specifically, when a user calls `ioctl(DVB_DEMUX_DMX_SET_PES_FILTER)`, the driver correctly allocates a `dmxdev_feed` object and adds it to a list. No issues there. However, if the user *then* calls `ioctl(DVB_DEMUX_DMX_SET_FILTER)` on the *same* file descriptor, switching to a section filter, the driver fails to free the previously allocated `dmxdev_feed` structure.
The culprit is the union, `filter->feed`, which is a list head (for TS filters) and a section feed. When it changes to section filter mode, the section feed or NULL overwrites the `dmxdev_feed`'s memory. This leads to the kernel effectively losing track of the `dmxdev_feed` objects, resulting in a persistent memory leak.
Fortunately, it's a pure leak. The leaked objects are never re-accessed after the filter switch, and the pointer involved is always overwritten with a valid pointer or `NULL` before dereferencing, so there's no path to memory corruption or privilege escalation here. It's a DoS, purely through memory exhaustion. An attacker can repeatedly trigger this filter-switching sequence to eat up kernel memory until the system OOMs.
The catch, of course, is that triggering this requires access to the DVB demux device node, `/dev/dvb/adapter#/demux#`. Typically, that's restricted to root or the `video` group, so it's not a trivial exploit for an unprivileged user. That's a key mitigation aspect to highlight in the final report.
|
| 43/1 |
2026/05/24 21:44 |
action |
format-explanation |
0m
Results: map[Explanation:**Analysis of the Bug**
The bug is a memory leak in the Linux kernel's DVB (Digital Video Broadcasting)
demux subsystem, specifically in `drivers/media/dvb-core/dmxdev.c`.
**Root Cause:**
When a user configures a demux filter as a PES (Packetized Elementary Stream)
filter using `ioctl(DVB_DEMUX_DMX_SET_PES_FILTER)`, the driver sets the filter
type to `DMXDEV_TYPE_PES`. It then allocates a `struct dmxdev_feed` object and
adds it to the `filter->feed.ts` linked list.
If the user subsequently reconfigures the exact same filter as a SEC (Section)
filter using `ioctl(DVB_DEMUX_DMX_SET_FILTER)`, the `dvb_dmxdev_filter_set()`
function changes the filter type to `DMXDEV_TYPE_SEC`. However, it fails to call
`dvb_dmxdev_filter_reset()` or otherwise free the existing `feed.ts` list before
doing so.
In `struct dmxdev_filter`, the `feed` member is a union:
```c
union {
struct list_head ts;
struct dmx_section_feed *sec;
} feed;
```
Because they share the same memory, when the filter is later started as a SEC
filter, the driver initializes `filter->feed.sec` (often setting it to `NULL`
before allocating a new section feed). This overwrites the `feed.ts` list head,
causing the kernel to lose all references to the previously allocated
`dmxdev_feed` objects, resulting in a permanent memory leak.
**Security Impact:**
* **Exploitable:** No. This is a pure memory leak. The leaked `dmxdev_feed`
objects are never accessed again. The union's memory overlap does not lead to a
use-after-free or wild pointer dereference because the driver unconditionally
overwrites the `feed.sec` pointer with a valid allocated section feed (or
`NULL`) before it is ever dereferenced as a SEC feed.
* **Denial of Service:** Yes. A local attacker with access to the device node
can repeatedly add PIDs to a PES filter and then switch it to a SEC filter,
leaking kernel memory (32 bytes per feed) until the system runs out of memory
(OOM), causing a denial of service.
* **Unprivileged Access:** No. Triggering this bug requires opening the DVB
demux device node (e.g., `/dev/dvb/adapter0/demux0`). By default on standard
Linux distributions, these device nodes are restricted to `root` and the `video`
group (permissions `0660`). Therefore, a completely unprivileged user cannot
reach this code.
* **Other Dimensions:** The bug is not reachable via network traffic, malicious
filesystems, or from within a standard user namespace (as device nodes are not
exposed/accessible by default). It is also not triggerable from a VM guest/host
context.]
|