โฌข DragonFlyBSD Kernel Audit
DF-2896 / verdict.json
โ† back to finding โ†“ download raw
{
  "finding_id": "DF-2896",
  "status": "not_reproduced",
  "reproduced": 0,
  "impact": "none",
  "confidence": "likely",
  "verdict": "cnwrite() captures constty->t_dev without any token or cdev reference (sys/kern/tty_cons.c:465-466) and dispatches dev_doperate() on it only after log_console() has slept and walked the whole write buffer (tty_cons.c:469-471, subr_prf.c:259-299). On default kernels (options UCONSOLE, X86_64_GENERIC:36) an unprivileged user controls that cdev's lifetime: TIOCCONS attaches their pty (kern/tty.c:959-977, verified live as uid=1001 via gate_test.c), closing the pair NULLs constty (tty.c:251-252), NULLs t_dev and destroy_dev()s the cdev (tty_pty.c:280-291). The race window (code-proven, milliseconds-to-seconds wide) therefore exists with certainty, but across 8 harness designs and ~20 minutes of genuine racing (~7000 teardowns vs ~2000 capture->dispatch envelopes, with recycling pressure and a deterministic misdirection detector) no manifestation occurred: the async devfs-thread teardown latency and the dedicated per-cpu objcache magazines for struct cdev decouple the free/realloc (with its ~us si_ops==NULL transient, devfs_core.c:2445) from the dispatch instants. Two guest hard-downs under heavier load left no panic text and are unattributable. Realistic ceiling if it ever lands: NULL-deref panic (local DoS requiring a concurrent privileged /dev/console writer, e.g. default syslogd) or a misdirected privileged console write into an unrelated pty; full RIP control is not credible (dedicated cdev objcache, kernel-initialized contents, static ops).",
  "exploit_chain": "",
  "evidence": [
    "findings/poc/DF-2896/VERDICT.md โ€” full path:line proof of the window and the attempt matrix",
    "findings/poc/DF-2896/gate_test.c โ€” unprivileged TIOCCONS gate test, PASSED on stock guest (uid=1001)",
    "findings/poc/DF-2896/racer.c โ€” unpriv constty lifecycle cycler (4864 iterations in final run)",
    "findings/poc/DF-2896/holder.c โ€” unpriv recycling pressure + misdirection detector ('W' on never-TIOCCONS'd ptys), 0 hits",
    "findings/poc/DF-2896/writer.c โ€” root /dev/console writer providing capture->dispatch envelopes",
    "findings/poc/DF-2896/logs/ โ€” run summaries, racer/holder/writer logs, serial tail",
    "findings/poc/DF-2896/fix.diff โ€” token-serialized capture+forward fix (validated: not_testable, no baseline repro)"
  ],
  "kernel_refs": [
    "sys/kern/tty_cons.c:456-472",
    "sys/kern/tty_cons.c:71",
    "sys/kern/tty.c:959-977",
    "sys/kern/tty.c:251-252",
    "sys/kern/tty_pty.c:280-291",
    "sys/kern/subr_prf.c:259-299",
    "sys/kern/kern_device.c:544-561",
    "sys/vfs/devfs/devfs_core.c:63-75",
    "sys/vfs/devfs/devfs_core.c:2436-2459",
    "sys/config/X86_64_GENERIC:36"
  ],
  "poc_changes": "Seed concept rewritten entirely: gate test proving unprivileged UCONSOLE TIOCCONS on the stock guest; serial-quiet constty cycler (immediate re-attach) after the first design wedged the guest with console firehose; dedicated unpriv holder for objcache recycling pressure plus a deterministic misdirection detector (fill byte 'W' appearing on never-TIOCCONS'd ptys); root writer downsized to 1x4MB at 3/s (envelope ~120ms). Three early harness configurations were invalid (unpriv actor EACCES under /root, csh redirect rejection, holder not rebuilt) โ€” found via heartbeat logging and fixed; only the last two runs count as genuine racing.",
  "attempts": 8,
  "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": "cc -O2 -o racer racer.c && cc -O2 -pthread -o writer writer.c && cc -O2 -o holder holder.c && cc -O2 -o pin pin.c",
  "run_cmd": "/tmp/df2896/run.sh  (racer + holder as uid=1001, writer as root; poll holder.log for HIT, serial boot.log for 'Fatal trap|panic:')",
  "code_hash": "27cbd1f1f2ef92c1d15357d22c243d8684b1bd625dd4e36b1d970e920ef9fd11",
  "notes": "Guest was reset with-src after the runs (clean). The unpriv gate (UCONSOLE) is live-verified, so the lifetime-control half of the race is certain; the manifestation half is probabilistic and did not land. Recommended follow-up: long-soak race (hours) or an instrumented kernel build to log cnwrite dispatches on freed slots. Two unattributed hard-downs occurred under console-firehose load (runs 4-5) โ€” full 105MB serial log contained no panic text.",
  "recommended_fix": "In cnwrite(), capture constty->t_dev under the constty tty's t_token (the token held by the ttyclose/pti_done teardown path) and hold the token across the dev_doperate() forward; see fix.diff.",
  "fix_status": "not_testable",
  "fix_kernel_uname": "",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "No baseline reproduction exists to diff against; the fix is source-reviewed only (serializes capture+forward against the exact teardown path that holds tp->t_token across constty=NULL / t_dev=NULL / destroy_dev).",
  "fix_evidence": "findings/poc/DF-2896/fix.diff"
}