DF-2755 / verdict.json
{ "finding_id": "DF-2755", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Unprivileged uid-1001 PoC racing TIOCSCTTY master-vs-slave from 6 threads of a session leader deterministically (2/2 runs, seconds-scale) panics the stock INVARIANTS kernel with 'vref: bad refcnt 00000000 1' from devfs_fo_ioctl+0x13f -- the racy s_ttyvp twin's vref() on a vnode whose refcount the double-vrele already drove to zero while the fd was still open. That is a proven vnode-refcount-underflow UAF primitive (silent on non-INVARIANTS kernels); the identical race exists in vn_ioctl's mplock-only twin (the mplock excludes no other s_ttyvp writer). fix.diff (atomic_cmpset_ptr ownership transfer on all five s_ttyvp writers) validated in-guest: nativekernel rebuild, 3 runs / 309.5M racer iterations clean, ctty semantics intact.", "exploit_chain": "unpriv setsid + posix_openpt -> arm s_ttyvp slot with slave vnode -> N threads race ioctl(TIOCSCTTY) on master+slave fds of same tty -> same-side twin interleave double-vreles old-slot vnode -> v_refcnt underflow -> premature vnode destruction with live fd/namecache references -> vnode-cache recycle = type confusion through stale devfs_dev_fileops fd (or stale ncp vget) -> arbitrary kernel memory corruption; uid0 chain documented but not completed on the INVARIANTS guest (assert fires first, DF-2687 precedent)", "evidence": [ "run.log / run.2.log + panic1.txt / panic2.txt: stock-kernel panics, trace vref+0x36 <- devfs_fo_ioctl+0x13f <- mapped_ioctl <- syscall2", "run.3.log: fixed kernel, 3 clean runs / 309,517,530 iterations + ctty_sanity exit 0", "fix.diff: cmpset ownership transfer, all 5 s_ttyvp writers, applied+built in-guest", "VERDICT.md: full interleaving analysis and non-duplicate argument vs DF-2687" ], "kernel_refs": [ "sys/kern/vfs_vnops.c:1030-1055", "sys/vfs/devfs/devfs_vnops.c:1595-1611", "sys/vfs/devfs/devfs_vnops.c:1153-1156", "sys/kern/tty.c:328-382", "sys/kern/kern_descrip.c:2031-2034" ], "poc_changes": "Seed sketch did not exist for this novel finding; two PoC designs were needed: (1) 4 threads on the SAME fd was a no-op because the twin early-outs when s_ttyvp==vp (98.8M iterations, no crash -- design flaw); (2) final design races master-fd vs slave-fd thread groups of the same session leader so the slot ping-pongs between the two vnodes and same-side threads interleave read-read-store-store. Panicked within the first run window.", "attempts": 2, "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": 480, "guest_dirty": 1, "build_cmd": "cc -O2 -pthread -o df2755 df2755.c", "run_cmd": "./df2755 3 60 (unprivileged; stock kernel -> panic, fixed kernel -> clean)", "code_hash": "920546cab1f3f1d74f1042b844ddd4a90fd73d46fe484e33a02124eaa8e87fd8", "notes": "Guest reset to clean (vm.sh reset with-src) after each panic and after fix validation. The vn_ioctl twin (this audit's file) is the same defect one layer up; all tty fds currently land on devfs_dev_fileops (devfs VOP_OPEN switches f_ops, devfs_vnops.c:1078), making the devfs twin the reachable instance -- both are patched by fix.diff. Distinct from DF-2687 (session-struct orphan UAF via missing t_session dissociation); DF-2755 is the vnode-refcount side and its PoC never closes the last fd.", "recommended_fix": "Convert every s_ttyvp writer (both TIOCSCTTY twins, devfs half-close, fdrevoke scan, ttyclosesession) to atomic_cmpset_ptr ownership transfer so exactly one racer releases the slot reference", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Mon Aug 31 20:54:26 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied to guest /usr/src (patch -p1, all hunks clean), make -j6 nativekernel KERNCONF=X86_64_GENERIC BUILD_RC=0, installkernel, reboot into #1 20:54:26. Baseline panic (2/2 on stock) absent on patched kernel across 3 runs / 309.5M racer iterations; ctty semantics (TIOCSCTTY/TIOCGPGRP//dev/tty) verified intact; guest then reset with-src.", "fix_evidence": ["run.3.log", "build.log", "fix.diff"] } |