β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-0703

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0703 Β· 11 files
FileTypeDescriptionSize
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
README.md readme human reproduce doc
↓ download 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.

VERDICT.md verdict source trace (per-datagram mbuf leak) + dead-code proof + fix
↓ download raw

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.diff applies 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)

Fix verification

not_testable

compile 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.