Unlocked/wrong-lock readers of the port list vs lagg_port_destroy: unlocked lagg_sysctl_active walk, and port if_ioctl/if_output/if_lagg torn down without the port serializer
Summary
Three readers race lagg_port_destroy: (a) lagg_sysctl_active walks sc_ports with NO lock whatsoever - any concurrent port add/destroy yields a walk over a freed lagg_port, and the sysctl is readable by any unprivileged user (0444). (b) lagg_port_destroy restores if_type/if_ioctl/if_output/if_lagg holding only the lagg WLock + global ifnet_lock, NOT the port ifnet's serializer, while lagg_port_ioctl asserts its caller holds that serializer: an ioctl that loaded if_ioctl before the swap can read if_lagg after it is NULLed -> lp stays NULL -> fallback executes 'if (lp->lp_ioctl != NULL)' -> guaranteed NULL-deref panic (unpriv SIOCGIFMEDIA suffices); or reads a stale lp after kfree -> call through freed memory. (c) lagg_port_output reads ifp->if_lagg with no NULL check and calls lp->lp_output - same window -> NULL function-pointer call. Beyond DF-0369 (discarded return only), the fallback is reachable with lp == NULL. Unverified (speculative timing; mechanism certain). Fix: RLOCK the sysctl walk; restore if_lagg BEFORE if_ioctl/if_output/if_type; NULL-check lp in the fallback/output. [VERIFIED pass2: unlocked sysctl walk vs port teardown - 4000x300 race iterations no crash; mechanism line-confirmed, window needs in-flight traffic; RLOCK fix compile-validated]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3348 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 854 B | β raw | |
| VERDICT.md | β | 2.0 KB | β raw | |
| lagg_race.sh | β | 647 B | view raw | |
| manifest.json | β | 722 B | view raw | |
| verdict.json | β | 1.5 KB | view raw |
DF-3348 -- verification pack
Method: Code-line analysis + attempted live reproduction Status: NOT REPRODUCED | impact: none | confidence: likely
What this is
lagg_sysctl_active walks sc_ports with no LAGG lock (if_lagg.c:1620-1638) and lagg_port_destroy swaps if_ioctl/if_output/if_lagg without the port serializer. Race attempted live: 4000 sysctl walks against 300 port add/remove cycles - no crash (the freed lp is slab-contained and the window needs packet traffic in flight during teardown). Mechanism line-confirmed.
Build
cc -O -o <prog> <prog>.c (on the guest)
Run
(driver script)
(success criterion: see VERDICT.md)
Evidence
run log in VERDICT.md (lagg_race.sh: sysctl=net.link.lagg.0.active, 4000 walks, no crash)
Kernel references: sys/net/lagg/if_lagg.c:1620-1638, sys/net/lagg/if_lagg.c:755-775
DF-3348 -- VERDICT
NOT REPRODUCED (impact=none, confidence=likely) Guest: 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
Summary
lagg_sysctl_active walks sc_ports with no LAGG lock (if_lagg.c:1620-1638) and lagg_port_destroy swaps if_ioctl/if_output/if_lagg without the port serializer. Race attempted live: 4000 sysctl walks against 300 port add/remove cycles - no crash (the freed lp is slab-contained and the window needs packet traffic in flight during teardown). Mechanism line-confirmed.
How it was verified
- Method: Code-line analysis + attempted live reproduction
- Kernel references: sys/net/lagg/if_lagg.c:1620-1638, sys/net/lagg/if_lagg.c:755-775
- Evidence: run log in VERDICT.md (lagg_race.sh: sysctl=net.link.lagg.0.active, 4000 walks, no crash)
- Reachability: local-unpriv where noted in the summary; root-config for tun/lagg/carp/ipfw3/gre items; remote-facing for the packet parsers (wlan harnesses model the RX/TX path verbatim because the guest has no 802.11 hardware; precedent DF-0393/DF-0594/DF-0621/DF-0350).
- Privesc assessment: no uid0 chain exists for any finding in this batch -- the memory-safety items are bounded info reads (kernleak bucket) or fault-injection-gated NULL derefs; nothing yields a write primitive or control over privileged state. The one panic-class live item (DF-3414) is root-only (SIOCIFDESTROY).
Fix
fix_status: not_testable -- Fix: LAGG_RLOCK around the sysctl walk (and hold the port serializer across the if_* swap); not A/B-able without a crash oracle.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testableFix: LAGG_RLOCK around the sysctl walk (and hold the port serializer across the if_* swap); not A/B-able without a crash oracle.
Confirmed kernel references
Detail
Evidence (decisive lines)
run log in VERDICT.md (lagg_race.sh: sysctl=net.link.lagg.0.active, 4000 walks, no crash)
Verdict
lagg_sysctl_active walks sc_ports with no LAGG lock (if_lagg.c:1620-1638) and lagg_port_destroy swaps if_ioctl/if_output/if_lagg without the port serializer. Race attempted live: 4000 sysctl walks against 300 port add/remove cycles - no crash (the freed lp is slab-contained and the window needs packet traffic in flight during teardown). Mechanism line-confirmed.
No comments yet.