DF-3225 / verdict.json
{ "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" } |