⬢ DragonFlyBSD Kernel Audit
DF-3099 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3099",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "REPRODUCED as a deterministic unprivileged local DoS, with the OOB-write sibling honestly characterized. Root cause: hpfs_hpbmap (alsubr.c:126-131) hands hpfs_write a run hint computed from the raw on-disk al_len; (runl+1)*DEV_BSIZE at hpfs_vnops.c:394-396 is signed-32-bit multiplication that wraps to exactly 0 for al_len=0x800000 (a benign-looking 4GB first extent), so towrite=szmin(off+resid, 0)=0, xfersz=0. off==0: getblk(size 0)+clrbuf+uiomove(n=0) make no progress and the while(uio_resid) loop (vnops.c:385) spins forever holding the vnode lock - unkillable livelock (observed: RUN_EXIT=124, load 11.99, mountpoint and ssh management starved). off>0: bread(size 0) + uiomove(b_data+off, (size_t)(0-off)) underflows size_t and aims an attacker-controlled copyin at a size-0 buffer - on the stock build the zero-size buffers data kva is unmapped, so all 13 probe offsets returned deterministic EFAULT (memory-safety defect real, corruption not landed on this build). Read-side sibling is safe: DFlys min is a u_int inline (libkern.h:76) so toread<=MAXBSIZE always and toread==0 breaks (vnops.c:331); hpfs_write lacks the equivalent check. Requires crafted image + RW mount (stock mount_hpfs forces MNT_RDONLY); trigger is unprivileged pwrite. Fix validated.",
  "exploit_chain": "crafted image (alleaf{0,0x800000,lsn}) -> root mounts RW via mount(2) helper with uid handed to user -> unprivileged pwrite(fd, 1, offset 0) -> hpfs_write (vnops.c:385-430) -> hpfs_hpbmap returns runl=0x7FFFFF -> (runl+1)*512 == 0 (int32 wrap) -> towrite=0 -> getblk(0)+clrbuf+uiomove(0 bytes) -> infinite kernel loop holding vnode lock -> system-wide starvation DoS (no uid0 path: livelock only; the off>0 uiomove underflow is a latent OOB copyin that faults on this build).",
  "evidence": [
    "run.whang.log: pwrite trigger never returned (RUN_EXIT=124), guest load 11.99, subsequent ssh timed out",
    "whang_console.txt: console excerpt around the wedge",
    "run.wprobe.2.log: 13/13 pwrite(1 @ 1..511) = -1 EFAULT (underflowed uiomove aimed at unmapped kva of size-0 buffer)",
    "run.readpanic.2.log: read-side negative result (umin clamp; EINVAL from benign device OOB read)",
    "run.control log path in fix_run.log control test: benign image reads fine (no regression)",
    "fix_run.log: patched kernel - whang returns instantly, wleak/wprobe perform normal in-bounds writes"
  ],
  "kernel_refs": [
    "sys/vfs/hpfs/hpfs_vnops.c:385",
    "sys/vfs/hpfs/hpfs_vnops.c:394-396",
    "sys/vfs/hpfs/hpfs_vnops.c:408-411",
    "sys/vfs/hpfs/hpfs_vnops.c:413",
    "sys/vfs/hpfs/hpfs_vnops.c:420",
    "sys/vfs/hpfs/hpfs_alsubr.c:126-131",
    "sys/kern/kern_subr.c:117"
  ],
  "poc_changes": "Built from scratch; initial readpanic hypothesis (getblk size>MAXBSIZE panic) was disproven by source analysis of DFlys u_int min() inline and empirically (EINVAL); write-side whang/wprobe variants are the reproducible manifestations.",
  "attempts": 6,
  "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": 420,
  "guest_dirty": 1,
  "build_cmd": "python3 craft_img.py df3099_wleak.img wleak; cc -O2 -Wall -o /tmp/df3099_poc /tmp/df3099_poc.c; cc -O2 -I/usr/src/sys -o /root/mount_rw /root/mount_rw.c",
  "run_cmd": "sh /tmp/df3099_setup.sh; (as uid 1001) /tmp/df3099_run /tmp/hpfsmp/FILE whang|wprobe|wleak",
  "code_hash": "27d77b51cfcfaa7ede3395e817fbc7dfc075b25f579f94b9f49dcf62cd92b240",
  "notes": "The off>0 uiomove underflow is a genuine memory-safety defect (attacker-length copyin at a zero-size buffers data pointer) but on the stock build zero-size getblk buffers carry no mapped data area, so it deterministically EFAULTs; impact therefore dos, not memcorrupt. mount_hpfs(8) forces MNT_RDONLY (sbin/mount_hpfs/mount_hpfs.c:106) - write path needs a direct mount(2) (DF-0859 precedent).",
  "recommended_fix": "Clamp the hpbmap run hint to [0, MAXBSIZE/DEV_BSIZE-1] and add a towrite==0 progress guard in hpfs_write (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): whang livelock (124, guest starvation), wprobe EFAULT x13. Patched (fix.diff): whang returns immediately (pwrite=1), wleak/wprobe do normal in-bounds writes (pwrite=512), control image reads unchanged. No regression.",
  "fix_evidence": "fix_run.log"
}