β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3783

BT848SCLIP / capture geometry β†’ unbounded RISC program emission overflows the fixed 8-page dma_prog/odd_dma_prog buffers (up to 37Γ—; also clip-less via 2046-row odd-only YUV4:2:2)

Summary

rgb_prog/rgb_vbi_prog/yuv422_prog emit RISC dwords into fixed 8-page (8192-dword) buffers with no capacity check. With 99 driver-validated clip nodes every scanline emits ~(nodes+1)*3 dwords: default 640x480 -> 71,526 dwords (254 KiB past end); 2046x1022 -> 304,860 dwords (1.19 MiB past end). Without any clip list, yuv422 odd/even-only (interlace=1) with rows=2046 emits 10,236 dwords (8,176 bytes past end). The overflow happens on the CPU during the METEORCAPTUR ioctl - no DMA needed. Content: OP_WRITE dwords (low bits = attacker-chosen byte counts from clip y-ranges) interleaved with bigbuf physical addresses. VERIFIED: verbatim-port harness on the guest proves 8x-37x overflow for validated clip lists and 1.25x for plain geometry. Unprivileged local user (device 0444) on any machine with a Bt848/878 card: linear kernel-heap corruption of 0.25-1.2 MiB during an ioctl -> panic at minimum; groomable corruption (semi-controlled dword pattern, huge volume) makes privilege escalation plausible.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3783 Β· 10 files
FileTypeDescriptionSize
harness.c β€” 10.3 KB view raw
poc_clip_risc_overflow.c β€” 1.6 KB view raw
build.log β€” 1.5 KB view raw
run.log β€” 1.3 KB view raw
env.txt β€” 412 B view raw
poc_build_and_device_check.log β€” 1 B view raw
fix.diff β€” 3.8 KB view raw
VERDICT.md β€” 4.5 KB ↓ raw
manifest.json β€” 1.1 KB view raw
verdict.json β€” 2.6 KB view raw
VERDICT.md
↓ download raw

DF-3783 β€” verdict

REPRODUCED | reproduced=1 | impact=dos | confidence=certain

REPRODUCED at source level (arithmetic harness). The verbatim-ported emission code (split/getline/notclipped + rgb_prog loop) proves the program size is 8.7x-37x the 8-page buffer for validated clip lists, and 1.25x for a plain 2046-row odd-only YUV4:2:2 geometry. Runtime kernel trigger impossible on this guest (no Bt848 hardware, driver not built); on real hardware the METEORCAPTUR ioctl linearly overwrites ~0.25-1.2 MiB of kernel heap during program build.

Root cause (citations)

The RISC program builders (rgb_prog, rgb_vbi_prog, yuv422_prog, yuv12_prog, yuvpack_prog) emit instructions into bktr->dma_prog / bktr->odd_dma_prog, which are FIXED 8-page (32 KiB = 8192 dword) allocations (bktr_core.c:141, 395-396). Nothing bounds the number of emitted dwords against that capacity.

(a) CLIP LIST: BT848SCLIP (bktr_core.c:1219-1271) accepts up to 99 active clip nodes. In rgb_prog/rgb_vbi_prog each scanline covered by clips makes the while(getline()) loop emit one WRITE split (2 dwords) + one SKIP split (1 dword) PER NODE (bktr_core.c:2465-2534, 2536-2616). With the default 640x480 geometry and 99 validated clip nodes the program is 71,526 dwords (8.7x the buffer, 254 KiB past the end); at max legal geometry 2046x1022 it is 304,860 dwords (37x, 1.19 MiB past the end). The overflow happens on the CPU during the METEORCAPTUR ioctl (build_dma_prog), no DMA required.

(b) GEOMETRY ONLY: yuv422_prog emits 5 dwords/line; odd/even-only capture (interlace=1, METEORSETGEO oformat ONLY_ODD/ONLY_EVEN) with rows=2046 emits 10,236 dwords = 8,176 bytes past the 32 KiB buffer with NO clip list at all (bktr_core.c:3126-3133).

Overflow content is semi-controlled: OP_WRITE dwords (0x1x flags | attacker chosen byte counts derived from clip y-ranges) interleaved with physical addresses of bigbuf. Linear kernel-heap corruption -> panic; with heap grooming a privesc primitive is plausible. Unprivileged: /dev/bktr0 is 0444.

Guest reality (why no runtime kernel trigger)

The audit guest is DragonFly 6.5-DEVELOPMENT x86_64 (X86_64_GENERIC). The bktr driver is NOT compiled into the GENERIC kernel (sys/conf/files: optional bktr pci), there is no /boot/modules tree, no /dev/bktr, /dev/tuner, /dev/vbi* nodes, no Brooktree PCI device (QEMU has no Bt848/Bt878 device model; pciconf -lv shows no vendor 109e). Therefore the kernel code path is unreachable ON THIS GUEST. Verification is source-level: faithful harnesses of the exact kernel arithmetic (the same methodology as the accepted DF-1216 evidence pack), run on the DragonFly guest with its native cc, plus fix.diff compile-validation against the guest's /usr/src. All PoC triggers compile cleanly against the guest's kernel headers (poc_build_and_device_check.log).

Exploit chain

open /dev/bktr0 (0444) -> BT848SCLIP(99 nodes) -> METEORCAPTUR -> build_dma_prog/rgb_prog writes 71k+ dwords into 8192-dword buffer -> kernel heap corruption during the ioctl (panic / groomable corruption).

Decisive evidence

run.log: [A] 640x480 99 clips -> 71526/71526 dwords vs cap 8192 (8x, 254 KiB past end each buffer); [B] 2046x1022 -> 304860 dwords (37x, 1.19 MiB past end); [C] yuv422 odd-only rows=2046, no clips -> 10236 dwords (8176 B past end). Clip list passes the driver's own validation (rc=0, max_clip_node=99). fix_build.log: all-fpatches module builds with -Werror.

Fix validation

fix.diff (in this directory) applies cleanly against the pristine sys/ tree (git apply --check) and compiles as part of the bktr KLD module on the guest's /usr/src with -Werror together with the seven other pass-2 bktr fixes (fix_build.log, MAKE_RC=0, bktr.ko + bktr_mem.ko produced). Runtime patched-behavior validation is not possible on this guest (no device), hence fix_status=not_testable (compile-validated + logic-reviewed).

Kernel references

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

Runtime fix validation impossible on this guest (no Bt848/878 hardware, driver not in GENERIC, no module dir). fix.diff applies cleanly to sys/ and the patched bktr KLD module compiles with -Werror on the guest's /usr/src (fix_build.log).

fix.diff; fix_build.log (all-8-fixes module build, MAKE_RC=0)
↓ fix.diffper-fix-DF-3783

Confirmed kernel references

Detail

Exploit chain

open /dev/bktr0 (0444) -> BT848SCLIP(99 nodes) -> METEORCAPTUR -> build_dma_prog/rgb_prog writes 71k+ dwords into 8192-dword buffer -> kernel heap corruption during the ioctl (panic / groomable corruption).

Evidence (decisive lines)

run.log: [A] 640x480 99 clips -> 71526/71526 dwords vs cap 8192 (8x, 254 KiB past end each buffer); [B] 2046x1022 -> 304860 dwords (37x, 1.19 MiB past end); [C] yuv422 odd-only rows=2046, no clips -> 10236 dwords (8176 B past end). Clip list passes the driver's own validation (rc=0, max_clip_node=99). fix_build.log: all-fpatches module builds with -Werror.

PoC changes

none - verbatim port compiled on first syntax fix (errno.h include)

Verified recommended fix

Add dma_prog_limit bound-checks to split() and the yuv emit loops (fix.diff)

Verdict

REPRODUCED at source level (arithmetic harness). The verbatim-ported emission code (split/getline/notclipped + rgb_prog loop) proves the program size is 8.7x-37x the 8-page buffer for validated clip lists, and 1.25x for a plain 2046-row odd-only YUV4:2:2 geometry. Runtime kernel trigger impossible on this guest (no Bt848 hardware, driver not built); on real hardware the METEORCAPTUR ioctl linearly overwrites ~0.25-1.2 MiB of kernel heap during program build.