โฌข DragonFlyBSD Kernel Audit
DF-0753 / run.log
โ† back to finding โ†“ download raw
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.