Unauthenticated remote mbuf leak in edge socket handler β kernel memory exhaustion DoS
Summary
LATENT (dead code). edge_socket_handler :203-228: while(running) sbinit(&sio) soreceive(so,...,&sio) m=sio.sb_mb process m_data. NEXT iteration sbinit zeroes sio.sb_mb=NULL frees NOTHING. Each received UDP datagram leaks one mbuf chain forever. Edge socket binds INADDR_ANY :247 no auth any source. Remote unauth attacker floods UDP -> mbuf zone exhaustion -> network stall/panic. Also per-datagram kprintf :213/222 console flood amplification. Fix: m_freem(m) each iteration.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0703 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| ipfw3_sync_deadpath.c | trigger-source | empirical dead-path demo | 4.0 KB | view raw |
| build.sh | build-script | cc -o ipfw3_sync_deadpath ... | 292 B | view raw |
| run.sh | run-script | issue the SYNC sockopt | 391 B | view raw |
| VERDICT.md | verdict | source trace (per-datagram mbuf leak) + dead-code proof + fix | 4.6 KB | β raw |
| fix.diff | suggested-fix | m_freem(m) each loop iteration | 571 B | view raw |
| run.log | run-log | dead-path demo output (ENOPROTOOPT) | 399 B | view raw |
| env.txt | environment | uname, cc version | 365 B | view raw |
| README.md | readme | human reproduce doc | 197 B | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
| live_reachability_check.txt | reachability-test | Live ipfw3_sync reachability evidence - dead code (ptr=NULL) | 1.2 KB | view raw |
DF-0703 β PoC evidence pack
See VERDICT.md for the full analysis (verdict, mechanism, fix, fix-validation).
Reproduce: ./build.sh && ./run.sh. Machine-readable catalog: manifest.json.
DF-0703 β Mbuf leak in edge_socket_handler (every received datagram leaks its mbuf chain forever)
Same ipfw3_sync dead-code caveat as DF-0700 β see that VERDICT for the full unreachability proof. The "remote unauthenticated attacker" impact claimed in the finding summary presupposes the edge socket is bound and the handler kthread is running; neither happens on this kernel because the start path is dead code.
Verdict
NOT REPRODUCED β LATENT dead code. Real source bug (unconditional per-datagram mbuf leak); unreachable on this kernel because the kthread that runs the leaky loop is never started. Fix is defense-in-depth.
The bug (source-level trace)
ip_fw3_sync_edge_socket_handler() (sys/net/ipfw3_basic/ip_fw3_sync.c:185-230) is
the body of the "edge" sync kthread. It receives UDP datagrams and dispatches on
their type:
while (fw3_sync_ctx.running & 1) {
sbinit(&sio, 1000000000); /* :204 β zeroes sio.sb_mb */
error = so_pru_soreceive(so, NULL, NULL, &sio, NULL, &flags); /* :205 */
if (error) break;
m = sio.sb_mb; /* :208 β take the chain */
type = (int *)m->m_data;
if (*type == SYNC_TYPE_SEND_TEST) { ... } /* :210-213 */
else if (*type == SYNC_TYPE_SEND_STATE) { ... } /* :214-219 */
else if (*type == SYNC_TYPE_SEND_NAT) { ... } /* :220-222 */
else { kprintf("Error ignore\n"); } /* :223-225 */
/* <-- NO m_freem(m). The received mbuf chain is never freed. */
}
soreceive fills sio.sb_mb with a freshly-allocated mbuf chain for each datagram.
The handler reads m = sio.sb_mb and uses m->m_data, but never calls
m_freem(m). On the next iteration, sbinit(&sio, ...) (:204) re-zeroes
sio.sb_mb (sets it to NULL) without freeing the previous chain β so every
received datagram leaks one mbuf chain permanently. (There is also a per-datagram
kprintf at :213/:222 that floods the console β a log-amplification side issue.)
The edge socket is bound to INADDR_ANY (:247), so any UDP source can deliver
datagrams to it β hence the finding's "remote unauthenticated" framing. A remote
flooder would exhaust the mbuf zone β network stall / panic (the mbuf zone is
finite and shared by all networking).
Why unreachable on this kernel
The handler is a kthread started by ip_fw3_ctl_sync_edge_start() (:232-266):
error = kthread_create(ip_fw3_sync_edge_socket_handler, NULL,
&fw3_sync_ctx.edge_td, "sync_edge_thread"); /* :260-261 */
edge_start is a sockopt handler β only reachable through ip_fw3_ctl_sync_sockopt
β only dispatched via ip_fw3_ctl_sync_ptr (ip_fw3.c:133), which is never
assigned anywhere in the tree (grep-verified β same dead dispatcher as DF-0700).
So edge_start is never called, the kthread is never created, the edge socket is
never bound, and the leaky loop never runs. There is nothing for a remote attacker
to send datagrams to.
Plus the base-kernel ipfw3 stub returns ENOPROTOOPT until kldload ipfw3, and
the modules aren't loaded by default. Empirically confirmed by
ipfw3_sync_deadpath (run.log): the sockopt that would start the edge returns
ENOPROTOOPT.
Fix (defense-in-depth)
Free the received mbuf chain at the end of each loop iteration. See fix.diff:
else { kprintf("Error ignore\n"); }
if (m != NULL) /* +DF-0703 */
m_freem(m); /* free each received datagram's chain */
}
Fix validation
fix.diffapplies cleanly.- Compile-validated with DF-0701/0702,
make -j6 nativekernelβ rc=0. fix_status: not_testableβ dead code; validated applies + compiles.
Files
| file | desc |
|---|---|
ipfw3_sync_deadpath.c |
empirical dead-path demo |
fix.diff |
m_freem(m) each iteration |
run.log |
dead-path demo output (ENOPROTOOPT) |
env.txt |
guest environment |
Kernel references (confirmed)
sys/net/ipfw3_basic/ip_fw3_sync.c:185-230βedge_socket_handlersys/net/ipfw3_basic/ip_fw3_sync.c:204βsbinitzeroessio.sb_mbwithout freeingsys/net/ipfw3_basic/ip_fw3_sync.c:205βso_pru_soreceivefills a new chainsys/net/ipfw3_basic/ip_fw3_sync.c:208βm = sio.sb_mb(taken, never freed)sys/net/ipfw3_basic/ip_fw3_sync.c:247β edge bindsINADDR_ANY(would be remote-reachable if started)sys/net/ipfw3_basic/ip_fw3_sync.c:260-261β kthread create inedge_startsys/net/ipfw3/ip_fw3.c:133βip_fw3_ctl_sync_ptr = NULL(dead dispatcher; edge_start unreachable)
Fix verification
not_testablecompile validated
see evidence pack
Confirmed kernel references
Detail
Exploit chain
none β dead code.
Evidence (decisive lines)
Same as DF-0700.
PoC changes
Added live_reachability_check.txt.
Verified recommended fix
fix.diff ensures mbuf chains are freed in the receive loop. Matches finding proposal.
Verdict
NOT REPRODUCED (dead code). Same root cause as DF-0700. The mbuf leak in edge_socket_handler is unreachable because ip_fw3_ctl_sync_ptr is NULL and the edge socket handler is never invoked.
No comments yet.