⬢ DragonFlyBSD Kernel Audit
DF-3225 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3225",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "REPRODUCED on DragonFly 6.5-DEVELOPMENT #0 (X86_64_GENERIC, INVARIANTS, 6 vCPU). procfs_control(ATTACH) (procfs_ctl.c:134-159) lacks the tracer-cycle/ancestor check that ptrace PT_ATTACH has (sys_process.c:193-199); because the ctl writer becomes the victims parent (proc_reparent(p, curp), :155), two cooperating unprivileged same-uid processes attach each other and build a p_pptr cycle (A.p_pptr=B, B.p_pptr=A, both P_TRACED). A subsequent ptrace(PT_ATTACH, C) from either then never returns from the kernel: the ancestor walk at sys_process.c:194 (for (pp = curp->p_pptr; pp != NULL; pp = pp->p_pptr)) spins forever while holding Cs p_token (acquired :162). Observed: ptrace call never returned; kill -9 had no effect on the spinner; kill(C) from another process blocked forever (kern_sig.c:802 needs C.p_token); guest sshd stopped accepting sessions (banner timeouts, no panic on console); host qemu burned 300-393% CPU (3-4 vCPUs pinned in-kernel) with the guest idle; only a hard reset recovered. Fix validated: patched nativekernel refuses the cycle-creating attach-back with EINVAL; ptrace(PT_ATTACH) returns normally; no wedge.",
  "exploit_chain": "fork A,B,C (unpriv) -> A: ctl attach+run on B (B.p_pptr=A, B traced) -> B (SA_NOCLDSTOP armed): ctl attach+run on A (A.p_pptr=B) => parent cycle -> A: ptrace(PT_ATTACH, C) -> kernel spins in-kernel forever holding C.p_token, thread unkillable -> every later operation needing Cs token or touching the cyclic parent topology wedges another CPU -> progressive permanent system livelock from a plain unprivileged user.",
  "evidence": "run.log: markers through [B 855] attaching A (856) back -> cycle, [A 856] ptrace(PT_ATTACH, 854): if vulnerable, never returns, [main] A SURVIVED SIGKILL -> stuck in kernel spin, and the ABSENT final line [main] kill(C) returned (runner hung in kill(2)). wedge_evidence.txt: ssh banner timeouts + host qemu 300-393% CPU. boot.log: serial console shows no panic. run.fixed.log: patched kernel B attach A: Invalid argument.",
  "kernel_refs": [
    "sys/vfs/procfs/procfs_ctl.c:134",
    "sys/vfs/procfs/procfs_ctl.c:140",
    "sys/vfs/procfs/procfs_ctl.c:151",
    "sys/vfs/procfs/procfs_ctl.c:155",
    "sys/vfs/procfs/procfs_ctl.c:157",
    "sys/kern/sys_process.c:162",
    "sys/kern/sys_process.c:193",
    "sys/kern/sys_process.c:194",
    "sys/kern/kern_sig.c:802"
  ],
  "poc_changes": "PoC written fresh. Two orchestration fixes were required to reach the livelock: (1) the ctl WRITER becomes the new parent, so A and B themselves must issue their own attach/run writes (not an orchestrator); (2) B must sigaction(SIGCHLD, SA_NOCLDSTOP) before attaching back, else the SIGCHLD emitted when As attach-stop completes (kern_sig.c:1637 ksignal(p->p_pptr, SIGCHLD)) stops B (B is traced by A) at syscall exit and the two merely freeze each other (mutual-stop, observed in the first attempt) without triggering the walk.",
  "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": 900,
  "guest_dirty": 0,
  "build_cmd": "cc -O -o df3225 df3225.c",
  "run_cmd": "./df3225   (VM under serial control; vulnerable kernel: permanent wedge, hard reset required)",
  "code_hash": "e64d52e90e3487c6d28688d02ca0176546a23189e370ea0d0fd12125a7c7dcd2",
  "notes": "No panic by design: the defect is an unbounded in-kernel loop, so the honest classification is a permanent, progressive livelock (RUN_RC=124-style wedge + guest unresponsive) rather than a crash. The consumer of the poisoned graph is sys_process.c:194, but the enabling defect (cycle creation) is procfs ATTACH, hence filed against procfs_ctl.c. Note the same walk could be hardened independently with an iteration cap, but the security fix is refusing cycle-creating attaches. Guest reverted to clean with-src snapshot after fix validation.",
  "recommended_fix": "Add the ptrace-style ancestor-cycle check to procfs ATTACH (walk curps p_pptr chain, EINVAL if it contains the target).",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Tue Sep  8 20:08:46 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Baseline (stock #0): full wedge (run.log + wedge_evidence.txt). Patched (nativekernel with fix.diff): the cycle-creating attach-back is refused EINVAL (run.fixed.log, run.fixed2: df3225: B attach A: Invalid argument), the cycle never forms, ptrace(PT_ATTACH, C) returns normally, guest stays responsive throughout. The runners post-refusal hang in run.fixed.log is PoC orchestration (B stopped-in-exit while traced keeps the sync pipe open), not kernel behavior; the guest remained fully responsive.",
  "fix_evidence": "findings/poc/DF-3225/run.fixed.log; findings/poc/DF-3225/fixbuild.log"
}