DF-5816 / verdict.json
{ "finding_id": "DF-5816", "status": "reproduced", "reproduced": true, "impact": "leak", "confidence": "certain", "verdict": "unp_pcblist (sys/kern/uipc_usrreq.c:1432-1509) exports, via the world-readable net.local.{stream,dgram,seqpacket}.pcblist sysctls (1511-1519), a full struct xunpcb per AF_UNIX socket whose contents include live kernel pointers: xu_unpp (1476), a verbatim bcopy of struct unpcb (1494) carrying unp_socket/unp_conn/unp_addr/unp_vnode/unp_rvnode/unp_fp/list links, and xso_so/so_pcb from sotoxsocket (1495, sys/kern/uipc_socket2.c:827-831). Reproduced as uid=1001 on the stock guest, 3/3 runs: 24 non-NULL kernel-range pointers per snapshot, kernel self-invariants so_pcb==xu_unpp and xso_so==unp_socket hold in the exported data (proving live pointers, not garbage), a socketpair created by the PoC shows mutual unp_conn==peer.xu_unpp (e.g. 0xfffff8011788b2b0 <-> 0xfffff8011788b110), and a bound SOCK_DGRAM record discloses unp_addr (kmem sockaddr, 0xfffff8008d8011c0), unp_vnode and unp_rvnode. Impact ceiling: unprivileged mapping of kernel heap objects plus the system-wide AF_UNIX connection graph -- the exact disclosure that makes heap grooming deterministic for the companion AF_UNIX bugs (DF-2702 file-pointer leak). Distinct from DF-2558 (uninitialized stack padding in the same output; zeroing cannot fix this). fix.diff (privilege-gate via caps_priv_check_td/SYSCAP_RESTRICTEDROOT, scrub pointer fields for non-root) validated in-guest: patched kernel #1 shows 0 non-NULL exported pointers for uid=1001 while root still sees 24 (netstat functionality retained); guest reset clean afterwards.", "exploit_chain": "", "evidence": [ "run.log: 'global: 24 non-NULL exported pointers, 0 outside kernel range, so_pcb==xu_unpp held for all 4 records' in all 3 runs (uid=1001)", "run.log: socketpair graph 'record A: unp_conn = 0xfffff8011788b110 == B.xu_unpp' / 'record B: unp_conn = 0xfffff8011788b2b0 == A.xu_unpp'", "run.log: bound dgram 'unp_addr 0xfffff8008d8011c0 KERNEL-RANGE', 'unp_vnode 0xfffff801170aa680 KERNEL-RANGE', 'unp_rvnode 0xfffff8008f520f00 KERNEL-RANGE', path prefix match proves the record is the PoC's own socket", "run.log: mirror self-validation 'xu_len == 912 == sizeof(mirror)' per record (layout drift would abort)", "fix_validation.log: patched kernel #1, uid=1001, 'global: 0 non-NULL exported pointers' in all 3 runs; correlation steps find no pointers (leak gone)", "fix_build_excerpt.txt: patch hunks 1-4 clean, nativekernel KBUILD_OK, installkernel, uname #1: Tue Oct 13 04:30:17 UTC 2026; root still sees 24 pointers on patched kernel" ], "kernel_refs": [ "sys/kern/uipc_usrreq.c:1475-1495", "sys/kern/uipc_usrreq.c:1432-1509", "sys/kern/uipc_usrreq.c:1511-1519", "sys/sys/unpcb.h:68-85", "sys/sys/unpcb.h:111-127", "sys/kern/uipc_socket2.c:827-831" ], "poc_changes": "PoC authored fresh (no seed). Fixes during bring-up: DF sysctlbyname takes 5 args; sun_path comparison changed to strncmp-prefix because the kernel bcopy copies sun_len bytes with no NUL terminator. Mirror structs validated at runtime via the kernel-set xu_len field.", "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": 540, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -o pcblist_ptrleak pcblist_ptrleak.c", "run_cmd": "sh run.sh (uid=1001, 3 consecutive runs; same binary on patched kernel for fix validation)", "code_hash": "695e00a8104a3e18680a2285d3769b245f82940442861f6ac1cc97411c52dc30", "notes": "Info leak, not memory corruption - no uid=0 chain applicable; ceiling is a full unprivileged kernel-heap object map + AF_UNIX connection graph (stable across runs on this no-KASLR guest), enabling deterministic slab targeting for adjacent AF_UNIX bugs. Pass-3 negative space (VERDICT.md): sun_len normalized by getsockaddr (bind/find_lockref strncpy exact-fit; pcblist 256B unions), single-mbuf control chains (fake trailing RIGHTS cmsg never parsed as pointers), MCLGET M_WAITOK non-NULL, close-cascade vs in-flight send safe via unpcb refcount + so_pcb!=NULL sofree gate, sonewconn_faddr so_incomp+SS_ASSERTINPROG kills the accept/soabort race, GC sweep revalidation kills mark/sweep races, deferred-discard ordering sound, externalize error paths balanced.", "recommended_fix": "unp_pcblist: scrub xu_unpp, xu_unp pointer fields and xu_socket.xso_so/so_pcb for readers failing caps_priv_check_td(req->td, SYSCAP_RESTRICTEDROOT) (keep address payloads and counters)", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Tue Oct 13 04:30:17 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "Patched kernel #1 (fix.diff applied to guest /usr/src, make nativekernel + installkernel + reboot): uid=1001 pcblist now exports 0 non-NULL pointers (baseline: 24 kernel-range pointers + socketpair graph); records still enumerate with intact xu_len and address payloads; root still sees all 24 pointers so privileged consumers (netstat) are unaffected. Bad behavior eliminated, no functional regression.", "fix_evidence": [ "fix_validation.log", "fix_build_excerpt.txt", "fix.diff" ] } |