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

BT848_REEPROM: signed (offset+count) guard bypassed with negative offset β†’ readEEProm() writes up to 32767 bytes into eeProm.bytes[256] (~32 KiB kernel heap overflow of the 260-byte M_IOCTLOPS ioctl argument, content attacker-controllable via DF-1217's raw I2C write)

Summary

tuner_ioctl passes user short offset/count unvalidated; readEEProm's only bound 'if ((offset + count) > max)' is bypassed by offset=-32511/count=32767 (sum == max). The i2c address-set masks offset to 0x00 (ACKs on EEPROM cards) and the loop writes 32767 bytes into the 256-byte bytes[] of the 260-byte kmalloc'd ioctl buffer -> 32,511-byte linear heap overflow whose content is EEPROM data - which the same unprivileged user can fully control by first writing the EEPROM through BT848_I2CWR (i2cWrite's 3-byte form is a 24Cxx byte-write). VERIFIED: harness reproduces the guard bypass and 32511-bytes-past-allocation write on the guest. Unprivileged fully-controlled ~32 KiB kernel heap overflow (groomable, arbitrary content via EEPROM priming).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3785 Β· 10 files
FileTypeDescriptionSize
harness_eeprom.c β€” 2.8 KB view raw
poc_eeprom_overflow.c β€” 1.2 KB view raw
build.log β€” 1.5 KB view raw
run.log β€” 474 B view raw
env.txt β€” 412 B view raw
poc_build_and_device_check.log β€” 1 B view raw
fix.diff β€” 1.9 KB view raw
VERDICT.md β€” 3.4 KB ↓ raw
manifest.json β€” 972 B view raw
verdict.json β€” 2.1 KB view raw
VERDICT.md
↓ download raw

DF-3785 β€” verdict

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

REPRODUCED at source level (guard-arithmetic harness): (offset=-32511, count=32767) passes the exact driver guard, the loop writes 32767 bytes into bytes[256]; first OOB byte lands at ioctl-buffer offset 260 of a 260-byte allocation. The i2c/EEPROM data path requires real hardware (absent on this guest).

Root cause (citations)

tuner_ioctl's BT848_REEPROM (bktr_core.c:2086-2092) passes user-controlled short offset/count straight to readEEProm(). The only bound, if ((offset + count) > max) (bktr_card.c:433), is a signed comparison against the EEPROM size (256) and is bypassed with a negative offset: offset=-32511, count=32767 gives offset+count == 256 == max -> guard passes. The i2c address-set uses offset&0xff (0x00), which ACKs on any EEPROM card, and the read loop then writes count bytes into data[0..32766] while data is eeProm.bytes[256] inside the 260-byte kmalloc(M_IOCTLOPS) ioctl argument (mapped_ioctl, sys/kern/sys_generic.c:687-689) -> 32,511 bytes of linear kernel heap overflow. Content is EEPROM data, which the same unprivileged user can make fully attacker-controlled by first writing the EEPROM via BT848_I2CWR (i2cWrite's 3-byte form dev+W/wordaddr/data is exactly a 24Cxx byte write - see DF-1217). Unprivileged: /dev/tuner0 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/tuner0 (0444) -> (optionally prime EEPROM via BT848_I2CWR) -> BT848_REEPROM{offset=-32511, count=32767} -> 32.5 KiB controlled heap overflow past a 260-byte M_IOCTLOPS allocation.

Decisive evidence

run.log: guard evaluation (256 > 256 = false -> PASSES) and the 32511-bytes-past-buffer result; canary inspection.

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

Confirmed kernel references

Detail

Exploit chain

open /dev/tuner0 (0444) -> (optionally prime EEPROM via BT848_I2CWR) -> BT848_REEPROM{offset=-32511, count=32767} -> 32.5 KiB controlled heap overflow past a 260-byte M_IOCTLOPS allocation.

Evidence (decisive lines)

run.log: guard evaluation (256 > 256 = false -> PASSES) and the 32511-bytes-past-buffer result; canary inspection.

PoC changes

first harness version smashed its own stack buffer (SIGSEGV) - switched to an mmap'd region

Verified recommended fix

Validate offset/count against sizeof(bytes) in the ioctl and fix the signed guard in readEEProm (fix.diff)

Verdict

REPRODUCED at source level (guard-arithmetic harness): (offset=-32511, count=32767) passes the exact driver guard, the loop writes 32767 bytes into bytes[256]; first OOB byte lands at ioctl-buffer offset 260 of a 260-byte allocation. The i2c/EEPROM data path requires real hardware (absent on this guest).