⬢ DragonFlyBSD Kernel Audit
DF-3083 / verdict.json
← back to finding ↓ download raw
{
 "finding_id": "DF-3083",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "panic",
 "confidence": "certain",
 "verdict": "One corrupted dirent byte (rec_len 16->17, so rec_len % 4 != 0) in a crafted ext2 image, mounted read-write, panics the stock kernel on the first unprivileged path lookup that walks past it: ext2_check_direntry rejects the entry, ext2_search_dirblock calls ext2_dirbad (ext2_lookup.c:701), which panics on any non-MNT_RDONLY mount (ext2_lookup.c:777-780). The identical lookup on a read-only mount survives (control run), and the walker already skips to the next block, so the panic is pure fail-stop policy. Single-fix kernel (printf instead of panic): same lookup returns ENOENT, console shows the ext2_dirbad message, no panic.",
 "exploit_chain": "crafted image (1 flipped byte) + RW mount (root, or unpriv with vfs.usermount=1) -> stat/open/ls -l of any name at/after the corrupt entry -> ext2_search_dirblock:701 -> ext2_dirbad:778 panic -> reboot-loopable local DoS; also the automatic follow-on of DF-3082 on valid images (zero-filled freed htree leaves)",
 "evidence": [
  "panic.txt: panic ext2_dirbad /mnt/e2 ino 12 offset 56 via ext2_lookup<-ext2_search_dirblock<-vop_compat_nresolve",
  "run.log: RO control survived; RW trigger transcript",
  "fix_run.log: stat file4 rc=1 (ENOENT expected, NO panic) + console ext2_dirbad printf + FIX-OK",
  "fix.diff (shared with DF-3082)"
 ],
 "kernel_refs": [
  "sys/vfs/ext2fs/ext2_lookup.c:698",
  "sys/vfs/ext2fs/ext2_lookup.c:701",
  "sys/vfs/ext2fs/ext2_lookup.c:771",
  "sys/vfs/ext2fs/ext2_lookup.c:777",
  "sys/vfs/ext2fs/ext2_lookup.c:801",
  "sys/vfs/ext2fs/ext2_lookup.c:803",
  "sys/vfs/ext2fs/ext2_lookup.c:805",
  "sys/vfs/ext2fs/ext2_lookup.c:532"
 ],
 "poc_changes": "Initial trigger used ls -l; readdir EIOs before emitting names on this image so no lstat reached the corrupt entry - replaced with a direct stat of a name positioned after the corrupt entry (deterministic). run.sh updated; craft.py in pack regenerates the image.",
 "attempts": 3,
 "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (baseline); fix kernel #1: Sun Sep 6 03:12:08 UTC 2026",
 "runtime_sec": 20,
 "guest_dirty": 0,
 "build_cmd": "host: python3 craft.py; guest root: cc -O2 -o /root/vntool /root/vntool.c; fix kernel shared with DF-3082 (make nativekernel + installkernel)",
 "run_cmd": "guest root: sh run.sh",
 "code_hash": "890092f0c08c90be9e36e1075b22b0bc0ad8d7c5509056e27a5ba8125f757c8a",
 "notes": "vfs.usermount=0 on this guest (root performs the mount); with vfs.usermount=1 the whole chain is unprivileged. Panic trace double-prints ext2_dirbad (compiler tail-duplication artifact). Guest reset to with-src snapshot after the runs.",
 "recommended_fix": "replace the panic with printf+SDT in ext2_dirbad (fix.diff, validated in-guest)",
 "fix_status": "fixed",
 "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Sun Sep 6 03:12:08 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
 "fix_baseline_reproduced": 1,
 "fix_patched_reproduced": 0,
 "fix_verdict": "Same mangled image, RW mount, same lookup on the single-fix kernel: ENOENT returned, console prints ext2_dirbad: : bad dir ino 12 at offset 56: mangled entry, entries before the corrupt byte still resolve (stat file1 ok), guest stays up (fix_run.log).",
 "fix_evidence": [
  "fix_run.log",
  "fix.diff",
  "build.log.gz (in DF-3082 pack, shared build)"
 ]
}