DF-2682 / verdict.json
{ "finding_id": "DF-2682", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Unprivileged syscall-only kernel heap use-after-free in kern_descrip.c: funsetown() clears *sigiop under sigio_token and then kfrees the struct sigio with no reference count and no token held (kern_descrip.c:1245-1273), while pgsigio() readers at uipc_socket2.c:601-602 (sowakeup/SS_ASYNC), uipc_socket.c:2537-2538, tty.c:2366/2380 and kern_event.c:2055 load the raw pointer without the token and dereference it (sio_pgid, sio_ucred->cr_uid, sio_pgrp->pgref, sio_proc). Reproduced on the stock INVARIANTS guest with a 3-thread socketpair harness: Fatal trap 12 in pgsigio+0x13 reading 0x40(%rax) == (freed sigio)->sio_ucred->cr_uid, kernel stopped in DDB, ssh dead. The primitive is a read-side UAF whose downstream dereferences (pgref atomic-increment, ksignal, lockmgr through forged pointers) are write-capable if the 48-byte M_SIGIO chunk is reclaimed under attacker control; a reliable uid=0 chain was not completed in this run (documented in VERDICT.md).", "exploit_chain": "unprivileged socketpair + F_SETOWN/O_ASYNC -> thread A: write() storm drives sowakeup()->pgsigio(so->so_sigio) lockless loads; thread B: fcntl(F_SETOWN)+dup/close churn drives fsetown()/soclose()->funsetown() which clears and kfrees the sigio; in-flight reader enters pgsigio() with freed pointer -> sio_ucred read from freed memory -> cr_uid deref at NULL+0x40 -> Fatal trap 12 (demonstrated). With slab reuse under attacker control the same chain yields pgref() increment / ksignal() writes through attacker-chosen pointers (escalation feasible in principle on this no-SMAP/SMEP/KASLR guest; not completed).", "evidence": [ "panic.txt: 'Fatal trap 12 ... fault virtual address = 0x40 ... Stopped at pgsigio+0x13: movl 0x40(%rax),%eax' from unprivileged sigio_uaf", "run.log: harness start line, ssh session death, vm.sh status=down immediately after", "sigio_uaf.c: the 3-thread harness (writer/F_SETOWN+dup-close churner)", "VERDICT.md: root cause lines, primitive characterization, fix iteration history", "sys/kern/kern_descrip.c:1245-1273 (free without reader exclusion), sys/kern/uipc_socket2.c:601-602 (unguarded load), sys/kern/sys_pipe.c:211-215 (the guarded idiom that the missing sites should mirror)" ], "kernel_refs": [ "sys/kern/kern_descrip.c:1238", "sys/kern/kern_descrip.c:1245", "sys/kern/kern_descrip.c:1273", "sys/kern/uipc_socket2.c:601", "sys/kern/uipc_socket.c:2537", "sys/kern/kern_sig.c:2639", "sys/kern/sys_pipe.c:211" ], "poc_changes": "None material; added <err.h>/<sys/socket.h> includes and fixed the drain side (recv on the O_ASYNC end) vs the seed sketch.", "attempts": 1, "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": 320, "guest_dirty": 1, "build_cmd": "cc -O2 -pthread -o sigio_uaf sigio_uaf.c", "run_cmd": "./sigio_uaf 300", "code_hash": "7e2acb53210b7cbb9c2d08845476532cb43f7798fad3766d30fb4c54f73a2533", "notes": "fix.diff (v4: token-guarded readers + ordered free, no token nesting) compiles and boots, but the race storm still crashes the restructured fsetown/funsetownlst replacement machinery shared with DF-2683; fix_status=fix_failed with 4 documented iterations (see VERDICT.md). Guest was reset with-src after the runs.", "recommended_fix": "Give struct sigio a reference count (or a dedicated list lock): readers acquire a ref under sigio_token before using the pointer; funsetown drops the last ref only after list removal, so pgsigio readers can never hold a freed sigio; mirror the existing pipewakeup() token-guard at all missing pgsigio call sites." } |