mbuf chain leak on duplicate SMB responses enables remote kernel memory-exhaustion DoS
| Field | Value |
|---|---|
| ID | DF-0627 |
| Status | new |
| Severity | High |
| CVSS 3.1 | CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H |
| CWE | CWE-401 Missing Release of Memory after Effective Lifetime |
| File | sys/netproto/smb/smb_iod.c |
| Lines | 363-366 (leak); 374-377 (skipped free) |
| Area | netproto/smb (kernel SMB client response dispatch) |
| Confidence | certain |
| Discovered | 2026-07-02 |
| Reported | pending |
Summary
In smb_iod_recvall(), when a second SMB response arrives for an
already-answered request (same MID, md_top already set, not
SMBR_MULTIPACKET), the else branch logs "duplicate response β¦
ignored" and breaks out of the TAILQ_FOREACH without freeing the
freshly-received mbuf chain. The post-loop m_freem() is gated on
rqp == NULL, which is false after a successful match, so the mbuf is
orphaned. A malicious SMB server can flood duplicate responses for an
outstanding MID and leak an unbounded number of mbuf chains (each up to
SMB_MAXPKTLEN = 128 KB), exhausting kernel memory and
panicking/OOM-killing the client.
Root cause
sys/netproto/smb/smb_iod.c:353-378:
353: SMB_IOD_RQLOCK(iod);
354: TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
355: if (rqp->sr_mid != mid)
356: continue;
357: SMBRQ_SLOCK(rqp);
358: if (rqp->sr_rp.md_top == NULL) {
359: md_initm(&rqp->sr_rp, m); /* ownership transferred */
360: } else {
361: if (rqp->sr_flags & SMBR_MULTIPACKET) {
362: md_append_record(&rqp->sr_rp, m); /* ownership transferred */
363: } else {
364: SMBRQ_SUNLOCK(rqp);
365: SMBERROR("duplicate response %d (ignored)\n", mid);
366: break; /* m NOT freed, NOT attached */
367: }
368: }
369: SMBRQ_SUNLOCK(rqp);
370: smb_iod_rqprocessed(rqp, 0);
371: break;
372: }
373: SMB_IOD_RQUNLOCK(iod);
374: if (rqp == NULL) { /* FALSE here β rqp matched */
375: SMBERROR("drop resp with mid %d\n", (u_int)mid);
377: m_freem(m); /* SKIPPED */
378: }
md_initm() (subr_mchain.c:316) takes ownership of m by storing it in
md_top. In the duplicate else branch the mbuf is neither attached
(md_initm/md_append_record not called) nor freed (no m_freem), and
the only post-loop m_freem at line 377 is gated on rqp==NULL which is
false because rqp matched at line 355. The local pointer m is
overwritten at the next loop iteration, so the chain is permanently
orphaned.
Threat model & preconditions
- Attacker position: the SMB server the victim client is connected to (network position, authenticated session sufficient).
- Precondition: the client has an active VC with at least one
outstanding request whose MID matches the flood (any normal file I/O on
a mounted share, e.g.
mount_smbfs //user@evil/share /mnt; touch /mnt/x). - Trigger: the server sends ONE valid SMB reply for that MID, then
immediately sends K (e.g. 50000) additional NetBIOS-framed SMB messages,
each bearing the same MID at offset 30. Each duplicate is a full
standalone NB session message, so
nbssn_recv(smb_trantcp.c:313) returns it as a separate mbuf chain. The client'ssmb_ioddrains the burst in onerecvall()pass; each duplicate hits theelsebranch at line 363 and leaks its chain. - Impact: each leak is one mbuf chain of attacker-chosen length up to 128 KB; a burst of N duplicates leaks up to N chains. Sustained flooding drives the kernel into mbuf/memory exhaustion and panic.
- UI:R because a local user must mount the attacker's share.
Recommended fix
Free the orphaned mbuf in the duplicate branch before break:
--- a/sys/netproto/smb/smb_iod.c
+++ b/sys/netproto/smb/smb_iod.c
@@ -360,11 +360,12 @@ smb_iod_recvall(struct smbiod *iod)
md_append_record(&rqp->sr_rp, m);
} else {
SMBRQ_SUNLOCK(rqp);
+ m_freem(m);
SMBERROR("duplicate response %d (ignored)\n", mid);
break;
}
This ensures every received mbuf chain is either handed to a requestor
(md_initm/md_append_record) or freed (signature mismatch at line 348,
no-match drop at line 377, or duplicate at the new line).
References
sys/netproto/smb/smb_iod.c:363-366β the duplicate-response else branch that breaks without freeingm.sys/netproto/smb/smb_iod.c:374-377β the post-loopm_freemgated onrqp == NULL, which is false after a match.sys/kern/libmchain/subr_mchain.c:316βmd_initmtransfers ownership.
Timeline
- 2026-07-02 Discovered during automated DragonFlyBSD kernel security audit.
- 2026-07-02 Reported to DragonFlyBSD security contact (pending).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0627 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| build.sh | build-script | no-op; trigger is a malicious SMB server | 626 B | view raw |
| run.sh | run-script | documents the malicious-server harness + netstat -m observation | 1.2 KB | view raw |
| fix.diff | suggested-fix | m_freem(m) in the duplicate else branch before break | 494 B | view raw |
| VERDICT.md | verdict | line-by-line source proof of the leak | 2.8 KB | β raw |
| README.md | readme | build/run/expected | 1.2 KB | β raw |
| env.txt | environment | guest env + SMB module notes | 365 B | view 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 SMB reachability test - module loadable, mount_smbfs hangs at SESSION_SETUP | 1.9 KB | view raw |
| evil_smb_dup.c | exploit-attempt | Malicious SMB1 server sending duplicate responses to trigger mbuf leak | 12.3 KB | view raw |
DF-0627 PoC β mbuf leak on duplicate SMB responses
Build
No client binary to build β the trigger is a malicious SMB server (network
attacker position) plus a victim that mounts its share. build.sh is a no-op
placeholder.
Run / trigger (requires the malicious-server harness)
- Run a malicious SMB1 server reachable from the guest.
- Victim mounts the share:
mount_smbfs //user@evil/share /mntand issues any request that produces an outstanding MID (e.g.touch /mnt/x). - Server sends ONE valid reply for that MID, then a burst of K additional NetBIOS-framed SMB messages with the SAME MID.
- Observe the leak:
ssh dfbsd 'netstat -m'β mbuf count climbs by K (each up to 128 KB). Sustained flooding β mbuf/memory exhaustion β panic.
Expected (bug present)
netstat -m mbuf/memory use climbs monotonically with each duplicate; eventually
the kernel panics on mbuf exhaustion.
Expected (FIXED)
Each duplicate's mbuf chain is freed in the else branch; netstat -m stays flat.
Proof
The bug is proven by source tracing in VERDICT.md (the else branch at
smb_iod.c:363-366 breaks without freeing m, and the post-loop m_freem at
:377 is gated on rqp==NULL which is false after a match).
DF-0627 β mbuf chain leak on duplicate SMB responses (remote DoS)
Verdict
CONFIRMED by exhaustive source tracing β memory leak / remote DoS. Not exercised at runtime in this evidence pack because the trigger requires a malicious SMB server (network-attacker position); the leak is unambiguous in the code.
Mechanism (cited)
sys/netproto/smb/smb_iod.c smb_iod_recvall() (:322-379):
- :324 SMB_TRAN_RECV(vcp, &m, td) receives an mbuf chain m from the network.
- :354 TAILQ_FOREACH(rqp, ...) finds the outstanding request matching the
response MID.
- :358-359 first response: md_initm(&rqp->sr_rp, m) β ownership of m
transfers to the request (md_top = m, sys/kern/libmchain/subr_mchain.c:319).
- :361-362 multipacket: md_append_record(&rqp->sr_rp, m) β ownership transfers.
- :363-366 duplicate response (same MID, md_top already set, not
SMBR_MULTIPACKET): the else branch does
SMBRQ_SUNLOCK(rqp); SMBERROR("duplicate response ..."); break; β it breaks out
of the loop without calling md_initm/md_append_record AND without
m_freem(m). m is neither attached nor freed.
- :374-377 the only post-loop m_freem(m) is gated on if (rqp == NULL) β but
rqp is non-NULL (it matched at :355), so m_freem is SKIPPED.
- Next loop iteration :323 m = NULL; then :324 overwrites m; the orphaned
chain is permanently leaked.
md_initm ownership claim: sys/kern/libmchain/subr_mchain.c:316-319
mdp->md_top = mdp->md_cur = m;. SMB_MAXPKTLEN = 0x1FFFF (sys/netproto/smb/smb.h:292);
nbssn_recv (smb_trantcp.c:313) returns each NetBIOS session message as a separate
chain, bounded by :305.
Threat model / impact
- Attacker = the SMB server the victim client is connected to (network position;
authenticated session sufficient).
UI:Rβ a local user must mount the attacker's share. - Trigger: server sends one valid reply for an outstanding MID, then K additional
NetBIOS-framed SMB messages with the SAME MID. Each duplicate hits
:363and leaks its chain (up to 128 KB). Sustained flooding β mbuf/memory exhaustion β panic/OOM. - Impact: remote kernel memory-exhaustion DoS (CWE-401). Pure resource leak; no memory corruption, no privilege boundary crossed β no uid0 path.
Why no runtime run here
Reproducing requires a malicious SMB1 server (NEGOTIATE + SESSION_SETUP + TREE_CONNECT
+ the duplicate flood). That harness is out of scope for this evidence pack; the bug
is proven line-by-line above. netstat -m on a victim under attack would show the
mbuf count climbing monotonically per duplicate.
Fix
fix.diff adds m_freem(m) in the duplicate else branch before break, so every
received chain is either attached (md_initm/md_append_record) or freed. Matches
the finding markdown's recommended fix exactly.
Fix verification
not_testablecompile validated
Confirmed kernel references
Detail
Exploit chain
none β memory leak (DoS), not a memory-corruption primitive. The bug leaks one mbuf chain per duplicate response. Trigger requires a working SMB session + malicious server sending duplicate responses for an outstanding MID.
Evidence (decisive lines)
smbfs.ko loaded: YES smb_iod_recvall in smbfs.ko: YES (offset 0x86f0) mount_smbfs available: YES Evil server accepts connection, processes NEGOTIATE + SESSION_SETUP mount_smbfs HANGS after SESSION_SETUP (D state, never sends TREE_CONNECT) Source trace confirms else branch at smb_iod.c:363-366 does NOT free m Post-loop m_freem at :377 gated on rqp==NULL (FALSE after match) -> m leaks
PoC changes
Created evil_smb_dup.c: malicious SMB1 server based on DF-0901's stub_smbd.c, modified to send duplicate responses in a single TCP write(). Added live_reachability_check.txt documenting the SMB mount failure.
Verified recommended fix
fix.diff adds m_freem(m) in the duplicate-response else branch at smb_iod.c:366 before break. Applies and compiles. Matches finding proposal.
Verdict
NOT REPRODUCED at runtime, but SOURCE-CONFIRMED real. smbfs.ko loads successfully and smb_iod_recvall (smb_iod.c:353-378) is present. The bug IS traced line-by-line: duplicate-response else branch at lines 363-366 breaks out of TAILQ_FOREACH WITHOUT freeing mbuf m; post-loop m_freem at line 377 is gated on rqp==NULL which is FALSE after a match at line 355, so m leaks. I built evil_smb_dup.c (a malicious SMB1 server that sends duplicate responses) and attempted to trigger via mount_smbfs. The server correctly handles NBSS session setup, NEGOTIATE, and SESSION_SETUP, but mount_smbfs HANGS after SESSION_SETUP β it never sends TREE_CONNECT. The issue is that the SESSION_SETUP response format doesn't satisfy the DragonFly smbfs client's NTLMSSP authentication expectations (the client expects either anonymous auth acceptance with proper word format or an NTLMSSP challenge with STATUS_MORE_PROCESSING_REQUIRED). The bug trigger requires a fully functional SMB1 server that correctly completes the NTLMSSP exchange.
No comments yet.