โฌข DragonFlyBSD Kernel Audit
DF-0598 / leak_sample.txt
โ† back to finding โ†“ download raw
# DF-0598 โ€” leak sample (code-level proof harness output)
# Source: ./run.sh 10  and  ./run.sh 1000  on the unpatched #0 guest.
# The harness replicates smb_sm_lookupint (sys/netproto/smb/smb_conn.c:123-180)
# line-for-line in userspace.  Each failing lookup leaks exactly one VC ref
# onto the LAST VC visited by SMBCO_FOREACH (the stale post-loop `vcp`).

================ run.sh 10  (10 failing lookups) ================
=== VULNERABLE smb_sm_lookupint (sys/netproto/smb/smb_conn.c:123-180) ===
  call #0: rc=1 (nonzero => lookup FAILED), but *vcpp=alice@a (NOT NULL!)
  after 10 failed lookups:
    usecount[alice@a] = 11  (baseline 1, delta +10)   <-- +10 leaked refs
    usecount[bob@b]   = 1  (baseline 1, delta +0)
    usecount[carol@c] = 1  (baseline 1, delta +0)
    TOTAL leaked refs across all VCs = 10  (expected 10)
    every miss returned *vcpp=alice@a (WRONG VC)

=== FIXED smb_sm_lookupint (fix.diff applied) ===
  call #0: rc=1 (nonzero => lookup FAILED), *vcpp=(null) โ€” correct
  after 10 failed lookups:
    usecount[alice@a] = 1  (baseline 1, delta +0)     <-- 0 leaked refs
    usecount[bob@b]   = 1  (baseline 1, delta +0)
    usecount[carol@c] = 1  (baseline 1, delta +0)
    TOTAL leaked refs across all VCs = 0  (expected 0)

================ run.sh 1000  (1000 failing lookups) ================
=== VULNERABLE smb_sm_lookupint ===
  after 1000 failed lookups:
    usecount[alice@a] = 1001  (baseline 1, delta +1000)   <-- linear, unbounded
    usecount[bob@b]   = 1  (baseline 1, delta +0)
    usecount[carol@c] = 1  (baseline 1, delta +0)
    TOTAL leaked refs across all VCs = 1000  (expected 1000)

=== FIXED smb_sm_lookupint ===
  after 1000 failed lookups:
    TOTAL leaked refs across all VCs = 0  (expected 0)

================ INTERPRETATION ================
The leak is deterministic and linear: N failing lookups โ†’ N leaked refs,
all pinned onto ONE VC (the last one SMBCO_FOREACH visited before the list
exhausted).  That VC's co_usecount grows without bound and can never return
to 0, so smb_co_gone()'s drain loop (smb_conn.c:263-265 `while (co_usecount > 0)
tsleep(...)`) hangs the closing thread forever.  The fix (clearing vcp on
every unlock/continue path + an `error==0` guard on the post-loop ref block)
produces exactly 0 leaked refs at any scale.