DF-2694 / verdict.json
{ "finding_id": "DF-2694", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Unprivileged local kernel panic reproduced twice (TCP sorecvtcp KKASSERT at uipc_socket.c:1852; AF_UNIX generic soreceive -> sbunlinkmbuf KKASSERT at uipc_sockbuf.c:552) by racing recv() into sleeping (swap-in) page faults against shutdown(fd, SHUT_RD). Root cause: soshutdown() bypasses ssb_lock by design, and sorflush() frees the receive mbufs holding only the ssb token - but a blocked DFly thread releases its lwkt tokens (lwkt_switch->lwkt_relalltokens), so the moment the copier sleeps in uiomove() its chain is freed underneath it (M_SOLOCKED is honored only by sbcompress, never on the flush/free path). The copier performs uiomove() out of freed clusters (UAF read) and then trips the sockbuf invariants. On non-INVARIANTS production kernels the generic path instead stores a stale freed-memory pointer into sb->sb_mb (sockbuf corruption).", "exploit_chain": "unpriv user -> thread A recv() on socket with queued data into pre-swapped (or otherwise cold) user pages -> thread B shutdown(fd, SHUT_RD) while A sleeps in a vm_fault inside uiomove() -> sorflush frees/zeroes so_rcv under A -> A resumes copying freed mbufs -> panic (INVARIANTS) or sb->sb_mb stale-pointer store (production). Escalation to uid=0 blocked on the audited INVARIANTS build: the assert/NULL-deref fires inside the same syscall before any harvestable state is returned; documented as hard blocker.", "evidence": [ "panic.txt (assertion \"m\" failed in sorecvtcp at uipc_socket.c:1852, TCP mode)", "panic2.txt (assertion \"sb->sb_mb == m\" failed in sbunlinkmbuf at uipc_sockbuf.c:552, AF_UNIX mode)", "run.log (both decisive baseline runs; note on why sleeping faults are required)", "fix.diff + fix_build.log + fix_run.log (patched kernel: no panic, rounds complete)" ], "kernel_refs": [ "sys/kern/uipc_socket.c:1953-1957", "sys/kern/uipc_socket.c:1963-1991", "sys/kern/uipc_socket.c:1746-1753", "sys/kern/uipc_socket.c:1756-1758", "sys/kern/uipc_socket.c:1851-1852", "sys/kern/uipc_socket2.c:753-760", "sys/kern/uipc_sockbuf.c:451-508", "sys/kern/uipc_sockbuf.c:376", "sys/kern/uipc_sockbuf.c:548-578", "sys/kern/uipc_mbuf.c:m_free", "sys/kern/lwkt_thread.c:lwkt_switch/lwkt_relalltokens", "sys/kern/uipc_socket.c:1322" ], "poc_changes": "Initial trigger used usleep() delays (quantized to 10ms ticks at hz=100) and resident/zero-fill receive windows: no hits, because the copier never slept and its lwkt token stayed held. Fixed by (a) spin-barrier reader start + busy-wait delays, (b) SIGPIPE ignore (writer EPIPE killed the process), (c) closing the peer side before joining the blocked writer (deadlock), (d) pre-swapping a pool of receive windows with a 3.4GB memory pig so every uiomove fault sleeps in vm_fault - this alone turned 0/900+ benign rounds into a 2/2 panic rate.", "attempts": 9, "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": 3900, "guest_dirty": 0, "build_cmd": "cc -O2 -pipe -pthread -o poc_race poc_race.c", "run_cmd": "./poc_race 40 3000 3000 0 # and: ./poc_race 60 200 500 1", "code_hash": "9e7afec3e66f57614f46f92250cfa49699dfa59a89c9189988df1704bd625e3b", "notes": "Two distinct receive paths affected (sorecvtcp for TCP via tcp_usrreq.c:994/1020; generic soreceive for AF_UNIX/UDP/raw). Requires the copier to block mid-copy (swap-in or file-I/O fault on the user receive buffer) - realistic under memory pressure. Upstream DragonFly HEAD is byte-identical (checked via gitweb): bug is live upstream. Fix validated by in-guest nativekernel rebuild. Fix history: v1 (SOLOCKED scan) incomplete for generic soreceive; v2 (SSB_LOCK wait) validated on both paths.", "recommended_fix": "sorflush(): after socantrcvmore(), poll (bounded) under the token until no M_SOLOCKED mbufs remain before snapshotting/zeroing/freeing the receive sockbuf", "status": "reproduced", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Sun Aug 30 23:56:25 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 v1 (wait for M_SOLOCKED mbufs) protected only the TCP path - AF_UNIX still panicked at sbunlinkmbuf:552 on the v1 kernel; fix v2 (shipped: wait, bounded 1000x1-tick, for SSB_LOCK to clear in sorflush after socantrcvmore) covers every receiver path: identical triggers that panicked 2/2 on stock now complete cleanly 0/2 (mode 0: 40 rounds, mode 1: 60 rounds), guest stays up, EOF semantics and data integrity preserved.", "fix_evidence": [ "fix.diff (v2)", "fix_build.log", "fix_run.log", "panic.txt", "panic2.txt" ] } |