⬢ DragonFlyBSD Kernel Audit
DF-2775 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2775",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "leak",
  "confidence": "certain",
  "verdict": "Reproduced on the stock INVARIANTS guest two ways. (1) Deterministic primitive: an exact copy of the sysctl_intrnames walk (kern_intr.c:1106-1110) stalled between loading a rec pointer and using rec->name, then resumed after the rec was unlinked, freed and immediately reclaimed (kfree->kmalloc LIFO, 100 percent over 3 runs) with a chosen rec->name, printed the chosen string into the walk output: harness token DF2775-SECRET-LEAK-TOKE and the kernel version string from 0xffffffff811473a0 (arbitrary-address NUL-terminated kernel read, no KASLR). (2) Organic: an unprivileged uid 1001 reader hammering the real sysctl hw.intrnames (pinned cpu0) while kernel-side churn on cpu1 performed the interposed unregister at ~200k unlinks/s panicked the kernel in 4 of 4 runs within seconds with the identical signature Fatal trap 9 / trap 12 stopped at strlen cmpb 0,(rdi) on the reader cpu, i.e. ksnprintf percent s of rec->name (kern_intr.c:1107) on a rec freed by the concurrent unregister (kern_intr.c:486-487). Organic token capture in reader output: 0 hits, mechanically explained: the stock loop separates rec load from rec->name dereference by ~5ns so a content-controlled read needs the reader preempted exactly inside that window (the rig proves that case); organic outcomes are benign or crash, matching the 4 panics. Privilege ceiling: unpriv-readable heap disclosure plus local DoS panic; the churn side needs kernel-context (root-driven driver) activity, an unprivileged user cannot trigger alone, and there is no write primitive, so this is an info leak not a privesc. fix.diff (per-cpu migrate+crit snapshot, then a single SYSCTL_OUT) validated on a rebuilt kernel: same organic PoC gave reads=113091 hits=0 with 17.4M deeds and no panic (plus a fully-real mode-0 run reads=5829 hits=0 over 9.2M register/unregister iterations) and byte-identical sysctl output (1536 entries, correct slot contents).",
  "exploit_chain": "",
  "evidence": [
    "run.log section 1-2: DF2775-RIG walker entry dfh0/..../dfh7/DF2775-SECRET-LEAK-TOKE and dfh7/DragonFly 6.5-DEVELOPME (chosen-address reads via freed rec->name)",
    "panic.txt and logs/boot.after4thpanic.log: Fatal trap 9 stopped at strlen cmpb 0,(rdi) on cpuid 0 from the unprivileged reader, 4 of 4 organic runs",
    "logs/hits.log: t=1s reads=1927 hits=0 pre-churn baseline; reader rate evidence",
    "run.log section 7-9: fixed kernel A/B SUMMARY reads=113091 hits=0 secs=90 at 17,408,000 deeds (100 percent reclaim), mode-0 run reads=5829 hits=0, and chk.c output rc=0 bytes=10218 entries=1536 e508=[dfp0/dfp1/dfp2/dfp3]",
    "fix.diff: sysctl_intrnames migrate+crit snapshot rewrite"
  ],
  "kernel_refs": [
    "sys/kern/kern_intr.c:1100",
    "sys/kern/kern_intr.c:1106",
    "sys/kern/kern_intr.c:1107",
    "sys/kern/kern_intr.c:438",
    "sys/kern/kern_intr.c:448",
    "sys/kern/kern_intr.c:486",
    "sys/kern/kern_intr.c:487"
  ],
  "poc_changes": "Replaced the stale seeded pack (it contained an unrelated refuted kern_time.c nanosleep candidate) with a purpose-built in-kernel harness: df_harness.c (exact stock-walk copy with a load-to-use stall point, cpu1 churn thread with interposed unregister doing kfree plus same-cpu LIFO reclaim groom, fixed-walk snapshot variant), an unprivileged O_SYNC-logging reader (df2775_reader.c) and a pin-then-setuid launcher (pin_drop.c) because USCHED_SET_CPU needs privilege. Fixed include paths (machine/atomic.h), volatile sysctl node casts, buffer bounds in the walk copy, a persist-mode chain-growth bug, and switched reader logging to O_SYNC so hits survive panic reboots.",
  "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",
  "runtime_sec": 5400,
  "guest_dirty": 0,
  "build_cmd": "cd /usr/src && make -j6 nativekernel KERNCONF=X86_64_GENERIC (df_harness.c compiled in; kernel.stripped installed manually to /boot/kernel/kernel)",
  "run_cmd": "sysctl dfh.mode2775=2 dfh.target2775=0 dfh.run2775=2 ; nohup /root/pin_drop 0 1001 ./df2775_reader.bin 90 0 /home/maxx/logs/hits.log & sysctl dfh.mode2775=2 dfh.persist2775=1 dfh.secs2775=85 dfh.run2775=1",
  "code_hash": "15da5b1aa9b39d592f02f878101e81f28d056fc27f28823a6ec9b392054e2cbe",
  "notes": "Honest rates: deterministic groom 3/3; organic panic 4/4 (seconds to ~1 min at 200k deeds/s); organic token-in-output 0 with a documented 5ns-window mechanism. Impact recorded as leak (arbitrary-address NUL-terminated read through freed rec->name, proven deterministically; unpriv readable) with panic as additional demonstrated effect. Privesc assessment: none possible from this primitive alone (no write, churn needs kernel context). Guest reset with-src after evidence collection.",
  "recommended_fix": "Snapshot hw.intrnames per CPU under int_moveto_destcpu plus crit_enter (the protocol unregister_int uses for the unlink), copy the rendered strings into a kmalloc buffer, and SYSCTL_OUT once afterwards (see fix.diff hunk sysctl_intrnames)."
}