DF-0753 / run.log
DF-0753 stale-mbuf-pointer / double-free harness
Transcribes mpls_output/mpls_push/mpls_swap/mpls_pop/mpls_forward
and m_prepend/m_pullup verbatim from sys/netproto/mpls/ & sys/kern/uipc_mbuf.c
--------------------------------------------------------
Scenario A: PUSH, leading_space=2 (< sizeof(struct mpls)=4)
-> m_prepend allocates new head, caller goes stale
--------------------------------------------------------
mpls_forward: m=0x8004b12c0 (head, m_flags=0x2)
if_output: received LIVE mbuf 0x8004b12c0 (m_len=24 pkthdr.len=24)
*** mbuf is NOT the pushed-label head (STALE) ***
mpls_forward: forwarded OK (no double-free path)
=== A: PUSH no-headroom, if_output success ===
allocs=2 frees=1 double_free=0 uaf=0
--------------------------------------------------------
Scenario A2: PUSH no-headroom, if_output RETURNS ERROR
-> if_output freed m, then m_freem(m) = DOUBLE-FREE
--------------------------------------------------------
mpls_forward: m=0x8004b11a0 (head, m_flags=0x2)
if_output returned -1
mpls_forward bad: m_freem(0x8004b11a0) โ [UAF] deref of freed mbuf 0x8004b11a0 at m_freem at bad: (magic=0xdeadc0de)
[DOUBLE-FREE] m_freem on already-freed mbuf 0x8004b11a0 (magic=0xdeadc0de)
=== A2: PUSH no-headroom, if_output error -> DOUBLE-FREE ===
allocs=2 frees=1 double_free=1 uaf=1
*** DOUBLE-FREE CONFIRMED (DF-0753 mpls_input.c:218) ***
*** USE-AFTER-FREE CONFIRMED (stale m in mpls_forward) ***
--------------------------------------------------------
Scenario B: SWAP, first mbuf has m_len=2 (< 4) -> m_pullup
frees old m, returns new; caller's m DANGLING
--------------------------------------------------------
mpls_forward: m=0x8004b1080 (head, m_flags=0x4002)
mpls_output returned -2
mpls_forward bad: m_freem(0x8004b1080) โ [UAF] deref of freed mbuf 0x8004b1080 at m_freem at bad: (magic=0xdeadc0de)
[DOUBLE-FREE] m_freem on already-freed mbuf 0x8004b1080 (magic=0xdeadc0de)
[DOUBLE-FREE] m_freem on already-freed mbuf 0x8004b0ff0 (magic=0xdeadc0de)
=== B: SWAP fragmented -> m_pullup UAF + double-free ===
allocs=3 frees=2 double_free=2 uaf=1
*** DOUBLE-FREE CONFIRMED (DF-0753 mpls_input.c:218) ***
*** USE-AFTER-FREE CONFIRMED (stale m in mpls_forward) ***
--------------------------------------------------------
Scenario C: CONTROL โ leading_space=14 (normal ether_input)
PUSH fast-path, NO realloc, NO bug
--------------------------------------------------------
mpls_forward: m=0x8004b0ed0 (head, m_flags=0x2)
if_output: received LIVE mbuf 0x8004b0ed0 (m_len=28 pkthdr.len=28)
(has M_MPLSLABELED โ but is it the RIGHT head?)
mpls_forward: forwarded OK (no double-free path)
=== C: control โ no bug (explains why live trigger is hard) ===
allocs=1 frees=1 double_free=0 uaf=0
--------------------------------------------------------
Scenario D: 3 PUSHes (max rt_shim), leading_space=14
3*4=12 <= 14 headroom, fast-path, NO realloc
--------------------------------------------------------
mpls_forward: m=0x8004b0e40 (head, m_flags=0x2)
if_output: received LIVE mbuf 0x8004b0e40 (m_len=36 pkthdr.len=36)
(has M_MPLSLABELED โ but is it the RIGHT head?)
mpls_forward: forwarded OK (no double-free path)
=== D: 3 PUSHes normal headroom โ no bug ===
allocs=1 frees=1 double_free=0 uaf=0
=========================================================
DF-0753 harness complete.
Root cause: mpls_output() takes `struct mbuf *m` BY VALUE.
PUSH via m_prepend, and SWAP/POP via m_pullup, rebind the
LOCAL m in mpls_output โ the caller mpls_forward never sees
the new head. Forwarding then uses a STALE pointer.
On if_output error: m_freem(stale) = DOUBLE-FREE.
On SWAP/POP m_pullup: stale = freed memory = UAF.
Fix: change mpls_output to take `struct mbuf **mp` and write
the new head through *mp at every rebind.