⬢ DragonFlyBSD Kernel Audit
DF-3101 / verdict.json
← back to finding ↓ download raw
{
 "finding_id": "DF-3101",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "panic",
 "confidence": "certain",
 "verdict": "REPRODUCED on the stock INVARIANTS kernel #0: an unprivileged ftruncate(2) on a crafted HPFS image (fnode with AB_NODES root alblk pointing at a 5000-sector AlSec chain, each an_nextoff=0xFFFFFFFF) drives hpfs_truncatealblk into unbounded recursion until the kernel stack guard page double-faults: console shows DOUBLE FAULT - KERNEL STACK GUARD HIT! / panic: double fault, rsp exactly on the guard. Root cause: hpfs_alsubr.c:760 loop condition (bn <= an_nextoff) is always true for bn=0, :770-775 recursion has no depth limit/visited-set; entry hpfs_vnops.c:559-569 -> hpfs_subr.c:774. Deterministic DoS/panic under the crafted-image threat model (RW mount required - stock mount_hpfs(8) forces MNT_RDONLY, so a direct mount(2) helper is needed, DF-0859 precedent). Fix (depth limit 32) validated on a rebuilt kernel: same trigger returns EINVAL, no panic.",
 "exploit_chain": "attacker supplies crafted HPFS image -> root mounts RW (mount(2) helper; mountpoint uid handed to user) -> unprivileged user opens FILE O_WRONLY (owner) -> ftruncate(fd,0) -> hpfs_setattr (vnops.c:559) -> hpfs_truncate (subr.c:774) -> hpfs_truncatealblk recursion 5000 deep (alsubr.c:770-775) -> kernel stack guard page -> double fault -> panic. No memory corruption escapes the guard page; impact ceiling is denial of service.",
 "evidence": [
  "panic.txt: DOUBLE FAULT - KERNEL STACK GUARD HIT! / panic: double fault / rsp=0xfffff801182dd000 on the guard page",
  "run.3.log: RW mount of df3101.img, unprivileged ftruncate trigger, RUN_EXIT=124 (syscall never returned), guest down",
  "craft_img.py: 5000-sector AB_NODES AlSec chain (an_nextoff=0xFFFFFFFF each) terminating in an empty leaf",
  "fix_run.log: patched kernel (fix.diff, depth limit) - same trigger returns EINVAL, no panic, guest stays up"
 ],
 "kernel_refs": [
  "sys/vfs/hpfs/hpfs_alsubr.c:742",
  "sys/vfs/hpfs/hpfs_alsubr.c:760",
  "sys/vfs/hpfs/hpfs_alsubr.c:764",
  "sys/vfs/hpfs/hpfs_alsubr.c:770-775",
  "sys/vfs/hpfs/hpfs_subr.c:774",
  "sys/vfs/hpfs/hpfs_vnops.c:559-569"
 ],
 "poc_changes": "Image crafter + RW-mount helper built from scratch on the verified DF-0857/DF-0859 pack skeleton; trigger is a 3-line ftruncate program. No seed PoC existed.",
 "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",
 "runtime_sec": 145,
 "guest_dirty": 1,
 "build_cmd": "python3 craft_img.py df3101.img; cc -O2 -Wall -o /tmp/df3101_poc /tmp/df3101_poc.c; cc -O2 -I/usr/src/sys -o /root/mount_rw /root/mount_rw.c",
 "run_cmd": "sh /tmp/df3101_setup.sh; (as uid 1001) /tmp/df3101_run /tmp/hpfsmp/FILE",
 "code_hash": "47b0776879f4634830f8be1287be5254ea900b407ab091b69454e254f7ddc395",
 "notes": "Cyclic variant of the same chain instead self-deadlocks on the per-level held buffer lock (unkillable hang) - noted in VERDICT.md, not separately verified. Distinct from DF-0858 (hpfs_hpbmap goto-loop, no stack growth).",
 "recommended_fix": "Depth-limit hpfs_truncatealblk recursion (depth argument, HPFS_MAXALSECDEPTH=32, EINVAL past it); see fix.diff.",
 "fix_status": "fixed",
 "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Sun Sep 6 09:09:15 UTC 2026 (fix.diff applied, make nativekernel, X86_64_GENERIC)",
 "fix_baseline_reproduced": 1,
 "fix_patched_reproduced": 0,
 "fix_verdict": "Baseline (unpatched #0): double-fault panic. Patched (fix.diff, make nativekernel, reboot): identical unprivileged trigger returns EINVAL, no panic, guest stays up.",
 "fix_evidence": "fix_run.log"
}