โฌข DragonFlyBSD Kernel Audit
DF-3023 / verdict.json
โ† back to finding โ†“ download raw
{
  "finding_id": "DF-3023",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Unprivileged pwrite() with a negative offset on the default tmpfs /tmp panics INVARIANTS kernels: sys_extpwrite() passes the offset unvalidated, tmpfs_write() (unlike tmpfs_read:559) never rejects uio_offset<0, and the unsigned size_t uio_resid makes the EFBIG/RLIMIT/growth checks wrap mod 2^64, driving tmpfs_reg_resize(vp, -16384) into KKASSERT(newsize>=0) at tmpfs_subr.c:990. Reproduced twice from clean boots; guest drops to db>. Production (non-INVARIANTS) kernels fail cleanly with ENOSPC via the tm_pages_max guard - no memory corruption, no escalation path (resize target is kernel-derived; buffer cache returns EFAULT for the silent-write variants). Fix (uio_offset<0 -> EINVAL in tmpfs_write) validated on a rebuilt kernel: panic gone, EINVAL returned, no regression.",
  "exploit_chain": "",
  "evidence": [
    "panic.txt โ€” serial-console panic signature (tmpfs_reg_resize newsize>=0, stack via kern_pwritev->vn_write->vop_write->tmpfs_write)",
    "run.log โ€” decisive run: probe EFAULT + panic trigger (second clean-boot reproduction; first in boot_panic_raw.txt)",
    "run.patched.log โ€” patched kernel: both variants return EINVAL=22, guest stays up",
    "fix_build.log โ€” nativekernel build/install/boot record + regression check",
    "trigger.c โ€” minimal unpriv reproducer"
  ],
  "kernel_refs": [
    "sys/vfs/tmpfs/tmpfs_vnops.c:666",
    "sys/vfs/tmpfs/tmpfs_vnops.c:713",
    "sys/vfs/tmpfs/tmpfs_vnops.c:726",
    "sys/vfs/tmpfs/tmpfs_vnops.c:760",
    "sys/vfs/tmpfs/tmpfs_vnops.c:559",
    "sys/vfs/tmpfs/tmpfs_subr.c:990",
    "sys/kern/sys_generic.c:360",
    "sys/kern/vfs_vnops.c:766",
    "sys/sys/_uio.h:59"
  ],
  "poc_changes": "Seed concept (positive 2^63-offset wrap) was refuted live: size_t uio_resid is unsigned so offset+resid cannot wrap for positive offsets (guest returned EFBIG). Re-derived the negative-offset variant: entry-sum wraps to exactly 0 for offset=-N/resid=N, and iteration-1 sum goes negative for offsets below -16384; empirically bisected the trigger matrix (EFAULT/EFBIG/panic) to pwrite(32752 @ -32752).",
  "attempts": 4,
  "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); #1 Sat Sep  5 11:23:49 UTC 2026 (patched)",
  "runtime_sec": 5400,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -o poc/df3023 poc/df3023.c   (fix kernel: cd /usr/src && patch -p0 < fix.diff && make -j6 nativekernel && make installkernel && reboot)",
  "run_cmd": "as unprivileged user on tmpfs /tmp: ./df3023   (expect: probe -> EFAULT; panic trigger -> kernel panic at tmpfs_subr.c:990, guest down at db>)",
  "code_hash": "21418db7435eed319f1970b07c9300d957920fae59108b5c88f137946bd5b66f",
  "notes": "16K-aligned single-block negative writes pass all limits but the buffer cache rejects loffset<0 with EFAULT - no silent unaccounted-page primitive. Production kernels: clean ENOSPC (wrong errno only). Impact confined to INVARIANTS/DEBUG builds (dev machines, this audit fleet's standard kernel).",
  "recommended_fix": "tmpfs_write: reject uio_offset < 0 with EINVAL, mirroring tmpfs_read (fix.diff validated in-guest); belt-and-suspenders: sys_extpwrite could also validate offset sign.",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Sat Sep  5 11:23:49 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Single-fix kernel (uio_offset<0 -> EINVAL at tmpfs_vnops.c:702) built via make nativekernel and booted. Exact PoC rerun: both negative-offset pwrites return EINVAL, no panic, guest up (run.patched.log). Normal write/pwrite/ftruncate regression OK. Baseline panicked twice before the fix.",
  "fix_evidence": "fix_build.log, run.patched.log, env.txt"
}