Drive-controlled / TOCTOU OOB kernel heap read in DVD_STRUCT_BCA and DVD_STRUCT_MANUFACT
Summary
In acd_read_structure DVD_STRUCT_BCA and DVD_STRUCT_MANUFACT cases use s->length=ntohs(d->length) as bcopy length where d->length is drive-controlled u_int16_t then re-read s->length from user pointer in bcopy. Neither value bounded against actual size of kernel buffer d (188 bytes BCA 2048 bytes MANUFACT). Malicious ATAPI device or user racing s->length causes bcopy to read up to 65535 bytes past d allocation into adjacent kernel heap written to user memory. [VERIFIED with corrections: drive-controlled OOB read AND kernel heap corruption via kern_ioctl buffer overrun (up to 1860 bytes/call leaked to caller); TOCTOU arm refuted (kernel buffer); hostile-drive hardening class - RESTRICTEDROOT + capability gated]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2505 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| dfrig4_acd.c | β | 15.0 KB | view raw | |
| Makefile | β | 445 B | β download | |
| build.sh | β | 298 B | view raw | |
| build.log | β | 6.4 KB | view raw | |
| run.sh | β | 287 B | view raw | |
| run.log | β | 3.3 KB | view raw | |
| fix_validation.log | β | 1.4 KB | view raw | |
| fix_run.log | β | 1.6 KB | view raw | |
| fix_build.log | β | 333.7 KB | β download | |
| fix.diff | β | 882 B | view raw | |
| fix_combined.diff | β | 1.2 KB | view raw | |
| env.txt | β | 1.2 KB | view raw | |
| verdict.json | β | 5.8 KB | view raw |
Fix verification
fixedfixed
fix_validation.log / fix_run.log: patched kernel #1 (Thu Oct 15 23:37:29 UTC 2026), rig rebuilt against patched /usr/src: test30 s->length clamped 4096->188 with zero markers and zero destination smashes; test31 clamped 3000->2048 clean; test32 clamped 65535->188 clean, survives; test33 control unchanged. Baseline: 3/3 hostile tests proved OOB read (+2/3 dest overflow). fix.diff applied to guest /usr/src only; audit sys/ tree untouched.
Confirmed kernel references
Detail
Exploit chain
malicious ATAPI drive (compromised firmware / USB-IDE bridge): respond to READ DVD STRUCTURE (format 3 or 4) with data_length=0xFFFF (DMA stays within the 192/2052-byte allocation length) -> acd_read_structure atapi-cd.c:1583/1588 stores ntohs() verbatim -> bcopy(d+4, s->data, 65535) reads 65531 bytes past the 192-byte heap chunk into the kernel ioctl buffer and writes 65523 bytes past it (kernel heap corruption; content = adjacent heap) -> driver returns 0 -> kern_ioctl copyouts 2060 bytes -> caller receives up to 1860 bytes/call of kernel heap beyond the 188-byte legit payload. Repeatable at will. No control over leaked offset selection beyond the claimed length.
Evidence (decisive lines)
run.log (baseline kernel, tests 33/30/31/32 with full production-call readbacks: s->length values, 0xAA marker spans [188..4095]/[2300..2999]/[188..16123], destination guard smash 1/10, control clean); fix_validation.log + fix_run.log (patched kernel: clamped 188/2048, zero markers/smashes); dfrig4_acd.c header documents the seam; build.log full compile of the verbatim production file.
PoC changes
No seeded sources existed; the rig was authored fresh following findings/poc/DF-2496/dfrig3_ata.c: (a) interposition moved to ata_atapicmd (the ATAPI response boundary this driver trusts) instead of ata_queue_request; (b) s allocated as a faithful kern_ioctl M_IOCTLOPS 2060-byte buffer (kernel malloc type bound via extern); (c) >2048 allocations use the kmem carve path on this guest, so freed-hole sandwiches never place the production MANUFACT chunk -- marker chunks are carved from inside the seam (while d exists, before the production bcopy) as pure instrumentation; the hostile attack input remains the 2-byte claimed length; (d) retry loop for <2048 magazine placement nondeterminism.
Verified recommended fix
Clamp both BCA and MANUFACT copies: copylen = min(ntohs(d->length), length-4) and min(copylen, sizeof(s->data)) before storing s->length and bcopy (fix.diff does exactly this).
Verdict
Drive-controlled OOB kernel heap read CONFIRMED at both DVD_STRUCT_BCA and DVD_STRUCT_MANUFACT sites through 100% production code (unmodified atapi-cd.c compiled into the dfrig4_acd KLD; only ata_atapicmd() interposed at the hardware boundary to serve a hostile 2-byte response length -- the DF-2496-proven verbatim-module pattern). BCA: claimed 4096 vs 192-byte allocation -> 3907 OOB-sourced marker bytes land in s->data[188..], and the same bcopy WRITES ~2048 bytes past the 2060-byte M_IOCTLOPS kern_ioctl buffer (guard smashed). MANUFACT: claimed 3000 vs 2052 -> 700 marker bytes past the allocation. Max arm 0xFFFF: s->length=65535 accepted, 65531 bytes read from a 192-byte chunk (0xAA x15936), non-fatal on the contained run. Compliant-length control (188) shows zero false positives. kern_ioctl then copyouts the full 2060-byte buffer to the caller on rc=0 -> up to 1860 bytes/call of kernel heap disclosed to the ioctl caller. CORRECTIONS to the finding: (1) the user-race/TOCTOU arm is refuted on DragonFly -- ap->a_data is a kernel M_IOCTLOPS buffer (sys/kern/sys_generic.c:675), not user memory; (2) reachability requires a malicious/compromised ATAPI device AND the RESTRICTEDROOT open gate (sys/kern/subr_disk.c:1071) plus the MST_READ_DVDROM capability gate, so this is a hostile-drive hardening bug, not an unprivileged-user primitive. Single-fix kernel validated: clamp to length-4/sizeof(s->data) -> all hostile tests clamp cleanly, zero markers, zero smashes.
No comments yet.