⬢ DragonFlyBSD Kernel Audit
DF-3315 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3315",
  "status": "not_reproduced",
  "reproduced": 0,
  "impact": "none",
  "confidence": "speculative",
  "verdict": "Structural race confirmed in source: in_pcblist_range (sys/netinet/in_pcb.c:2456-2476) walks non-shared pcbinfo lists (ripcbinfo, infotoken==NULL) with zero synchronization and advances its marker via a non-atomic read/unlink/relink dance; a same-cpu netisr teardown (rip_detach -> in_pcbdetach: in_pcbofflist + kfree, in_pcb.c:1210-1226) landing inside the window would relink the marker after a freed inpcb (UAF write of a pointer into freed M_PCB memory) and read/export freed-recycled memory (UAF read). Reader half is fully unprivileged (sysctl net.inet.raw.pcblist); churn half is root-gated raw sockets but unpriv-drivable via setuid /sbin/ping. NOT reproduced under maximal stress: ~13.8M sysctl dumps (~6e9 marker advances) vs ~7M concurrent teardowns across 3 rounds (115s single reader @45k dumps/s, 230s x 8 concurrent readers) - no panic, no corruption, guest healthy. On this kernel the netisr0 teardown work executes at the walkers blocking points where the marker protocol works as designed; safety rests on scheduler serialization, not on synchronization in the code. Filed as Low/speculative hardening; fix.diff makes the advance+snapshot atomic via crit_enter/crit_exit. Twin non-atomic dance in tcp_subr.c tcp_pcblist (fully unpriv churn) needs the same fix.",
  "exploit_chain": "if the window were entered: unpriv sysctl net.inet.raw.pcblist loop + unpriv setuid-ping churn -> LIST_INSERT_AFTER on freed inpcb -> pointer write into freed M_PCB chunk + succ->le_prev into freed chunk -> list corruption -> panic; freed-recycled inpcb contents bcopyd to user (info leak). Not demonstrated.",
  "evidence": "VERDICT.md (full analysis + attempts table); run.log (3 rounds, ~13.8M dumps / ~7M teardowns / 0 panics); pcb_race.c (reader+churn harness); fix.diff (crit_enter protection, patch -p1 --dry-run verified)",
  "kernel_refs": [
    "sys/netinet/in_pcb.c:2456",
    "sys/netinet/in_pcb.c:2458",
    "sys/netinet/in_pcb.c:2459",
    "sys/netinet/in_pcb.c:2460",
    "sys/netinet/in_pcb.c:2469",
    "sys/netinet/in_pcb.c:1210",
    "sys/netinet/in_pcb.c:1217",
    "sys/netinet/in_pcb.c:1225",
    "sys/netinet/in_pcb.c:255",
    "sys/netinet/raw_ip.c:115",
    "sys/netinet/tcp_subr.c:1226"
  ],
  "poc_changes": "Seed sketch (none existed - new finding): wrote pcb_race.c with -r reader (unpriv sysctl hammer, dynamic sizing+data passes) and -c churner (root, holds 96 raw ICMP sockets, close+recreate round-robin); run.sh wrapper plus multi-reader stress.sh variant used in round 3.",
  "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": 700,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -o pcb_race pcb_race.c",
  "run_cmd": "./pcb_race -c 300 96 & ; su maxx -c /tmp/df3315/pcb_race -r 280  (and 8x concurrent readers via stress.sh)",
  "code_hash": "3c2c982b8e53b241c47c2608497b6ae0966e24a1ca5742ffd05f90f76d02cd7d",
  "notes": "Known findings DF-0333/0334/0335 re-checked during this pass: all UNFIXED in tree (in_pcb.c:2469 raw bcopy intact; :428/:885 div0 intact; :2532-2555 portrange intact); DB status new. sotoxsocket (uipc_socket2.c:827) also exports xso_so/so_pcb raw pointers - same sink/fix as DF-0333. Killed candidates documented in VERDICT.md/negative notes: NULL inp_socket deref in in_pcbbind_laddrport_check (only assignment is in_pcb.c:286); cross-cpu 4-tuple duplication (udp/tcp connect migrate to netisr_hashport before final check: udp_usrreq.c:1620-1638, tcp_usrreq.c:1205+); in_pcbpurgeif0 imo UAF (in_delmulti never blocks - igmp_leavegroup/igmp_sendpkt NOWAIT path; membership add/del netisr0-only via ASSERT_NETISR0); in_pcbsrcaddr_check KKASSERT(sin_len) (kernel-set at udp_usrreq.c:971); localgroup sort bounds (il_inpcnt<il_inpsiz invariant, in_pcb.c:2200).",
  "recommended_fix": "Wrap the in_pcblist_range marker advance + pcb snapshot in crit_enter()/crit_exit() (see fix.diff); apply the same to tcp_pcblist() in tcp_subr.c."
}