DF-3083 / verdict.json
{ "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)" ] } |