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

METEORSETGEO accepts frames=0 β†’ bigbuf resize block skipped entirely β†’ oversized rows/cols stored β†’ 626,696-byte OOB RISC-DMA write and OOB read() heap disclosure with the default RGB16 format (no SACTPIXFMT; distinct trigger from DF-1216)

Summary

geo->frames (u_short) is checked only against >32; frames=0 passes, makes rows*columns*frames*2 == 0 so the resize block (including the size check) is skipped, and rows=2046/cols=1022 are stored anyway. The RISC builders and video_read() size by rows*cols*Bpp, ignoring frames: footprint 4,182,024 B vs the default 3,555,328 B allocation -> 626,696 B of OOB device-DMA write past bigbuf and the same OOB read via video_read's uiomove (kernel heap disclosure). VERIFIED: harness reproduces the bypass arithmetic on the guest (frames=0 -> 626,696 B OOB both directions; frames=1 control -> 0 B after realloc). Unprivileged heap corruption + info leak using only METEORSETGEO + capture + read().

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3789 Β· 10 files
FileTypeDescriptionSize
harness_frames0.c β€” 2.8 KB view raw
poc_frames0.c β€” 1.6 KB view raw
build.log β€” 1.5 KB view raw
run.log β€” 750 B view raw
env.txt β€” 412 B view raw
poc_build_and_device_check.log β€” 1 B view raw
fix.diff β€” 865 B view raw
VERDICT.md β€” 3.4 KB ↓ raw
manifest.json β€” 976 B view raw
verdict.json β€” 2.0 KB view raw
VERDICT.md
↓ download raw

DF-3789 β€” verdict

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

REPRODUCED at source level (SETGEO-arithmetic harness, DF-1216 methodology): frames=0 -> temp==0 -> resize block skipped; footprint 4,182,024 B vs 3,555,328 B allocation -> 626,696 B OOB both directions. Control run with frames=1 reallocs to 1022 pages and shows 0 B OOB.

Root cause (citations)

METEORSETGEO validates geo->frames only against the UPPER bound (if (geo->frames > 32), bktr_core.c:1642); frames is a u_short and 0 passes. The resize block is guarded by if ((temp = rows * columns * frames * 2)) (bktr_core.c:1657) so with frames=0 the product is 0 and the block (including the size check) is SKIPPED ENTIRELY; rows/cols are stored anyway (1687-1689). rows=2046 / cols=1022 are individually legal (masks 0x7fe / 0x3fe). The RISC builders and video_read() size everything by rowscolsBpp, ignoring frames: footprint = 204610222 = 4,182,024 B against the default 868-page (3,555,328 B) bigbuf -> 626,696 bytes of OOB DMA write past bigbuf AND a 626,696-byte OOB kernel-heap read via video_read's uiomove (count = rowscolsBpp, bktr_core.c:1084-1106). Distinct trigger from DF-1216 (which needs METEORSACTPIXFMT to switch Bpp); here the default RGB16 format alone suffices - the hole is the missing lower bound on frames.

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 -> METEORSETGEO{2046,1022,frames=0,RGB16} -> METEORCAPTUR (DMA writes 626,696 B past bigbuf) -> read() returns them (heap disclosure).

Decisive evidence

run.log: bypass case vs frames=1 control (0 B OOB after realloc).

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-3789

Confirmed kernel references

Detail

Exploit chain

open /dev/bktr0 -> METEORSETGEO{2046,1022,frames=0,RGB16} -> METEORCAPTUR (DMA writes 626,696 B past bigbuf) -> read() returns them (heap disclosure).

Evidence (decisive lines)

run.log: bypass case vs frames=1 control (0 B OOB after realloc).

PoC changes

PAGE_SHIFT define added; control case fixed to track the realloc

Verified recommended fix

Reject frames < 1 and add a defensive geometry-vs-allocation check in start_capture (fix.diff)

Verdict

REPRODUCED at source level (SETGEO-arithmetic harness, DF-1216 methodology): frames=0 -> temp==0 -> resize block skipped; footprint 4,182,024 B vs 3,555,328 B allocation -> 626,696 B OOB both directions. Control run with frames=1 reallocs to 1022 pages and shows 0 B OOB.