โฌข DragonFlyBSD Kernel Audit
DF-0754 / fix_run.log
โ† back to finding โ†“ download raw
=== DF-0754 FIX VALIDATION: before/after contrast (deterministic harness) ===
=== Fix: mpls_output(struct mbuf **mp) โ€” same root-cause fix as DF-0753 ===

==================== BASELINE (unpatched mpls_output.c:50 by-value) ====================
--- Manifestation 1a (PUSH + m_prepend OOM -> mpls_output_process:145 DOUBLE-FREE) ---
M1a: PUSH + m_prepend OOM -> mpls_output_process:145 DOUBLE-FREE
     (mpls_output.c:143 by-value, :145 m_freem on already-freed m)
----------------------------------------------------------------
  ip_output: m=0x8004a12c0 label=101 (head)
  [inject] m_prepend: alloc OOM -> m_freem(m=0x8004a12c0 label=101) + return NULL
  mpls_output_process: error=105 -> m_freem(stale m=0x8004a12c0 label=101)
  [DOUBLE-FREE] m_freem on already-freed mbuf 0x8004a12c0 (label=101) (magic=0xdeadc0de)
  ip_output: mpls_output_process=FALSE -> goto done (m already freed at :145)

  --- M1a result ---
  allocs=1 frees=1  outstanding(live)=0  double_free=1  uaf=0
  if_output derefs=0  last_received_label=-1
  EXPECT: double_free >= 1 (mpls_output_process:145 frees freed m)
  *** MANIFESTATION 1 (DOUBLE-FREE) CONFIRMED ***

--- Manifestation 1b (SWAP + m_pullup OOM -> DOUBLE-FREE) ---
M1b: SWAP + m_pullup OOM -> mpls_output_process:145 DOUBLE-FREE
     (mpls_swap takes m by value; m_pullup frees old m on OOM)
----------------------------------------------------------------
  ip_output: m=0x8004a1230 label=103 (head)
  [inject] m_pullup: alloc OOM -> m_freem(n=0x8004a1230 label=103) + return NULL
  mpls_output_process: error=105 -> m_freem(stale m=0x8004a1230 label=103)
  [DOUBLE-FREE] m_freem on already-freed mbuf 0x8004a1230 (label=103) (magic=0xdeadc0de)
  [DOUBLE-FREE] m_freem on already-freed mbuf 0x8004a11a0 (label=102) (magic=0xdeadc0de)
  ip_output: mpls_output_process=FALSE -> goto done (m already freed at :145)

  --- M1b result ---
  allocs=2 frees=2  outstanding(live)=0  double_free=2  uaf=0
  if_output derefs=0  last_received_label=-1
  EXPECT: double_free >= 1 (stale m freed by m_pullup, re-freed at :145)
  *** MANIFESTATION 1 (DOUBLE-FREE) CONFIRMED ***

--- Manifestation 1c (PUSH ok then ENOTSUP -> new head LEAKED) ---
M1c: PUSH ok then later op ENOTSUP -> stale head freed, NEW head LEAKED
     (mpls_output_process:145 frees stale m; new pushed head unreachable)
----------------------------------------------------------------
  ip_output: m=0x8004a1110 label=104 (head)
  mpls_push: new head m=0x8004a1080 label=105
  mpls_output_process: error=91 -> m_freem(stale m=0x8004a1110 label=104)
  ip_output: mpls_output_process=FALSE -> goto done (m already freed at :145)

  --- M1c result ---
  allocs=2 frees=1  outstanding(live)=1  double_free=0  uaf=0
  if_output derefs=0  last_received_label=-1
  EXPECT: double_free=0 (old chain freed once), live_allocs >= 1 (new pushed head LEAKED)
  *** MBUF LEAK: 1 outstanding alloc(s) not freed ***

--- Manifestation 2a (PUSH realloc SUCCESS -> stale m to driver + leak) ---
M2a: PUSH realloc SUCCESS -> ip_output:698 if_output(STALE m)
     (driver gets demoted old head; new pushed head leaked)
----------------------------------------------------------------
  ip_output: m=0x8004a0ff0 label=106 (head)
  mpls_push: new head m=0x8004a0f60 label=107
  ip_output: mpls_output_process=TRUE -> ifp->if_output(stale m=0x8004a0ff0)
  if_output: received m=0x8004a0ff0 label=106
  if_output: m LIVE, m_len=20 pkthdr.len=20 M_PKTHDR=1 M_MPLSLABELED=0

  --- M2a result ---
  allocs=2 frees=1  outstanding(live)=1  double_free=0  uaf=0
  if_output derefs=1  last_received_label=106
  EXPECT: if_output_received_label == original IP mbuf label (STALE), live_allocs >= 1 (new head leaked)
  *** MBUF LEAK: 1 outstanding alloc(s) not freed ***

--- Manifestation 2b (SWAP m_pullup realloc SUCCESS -> UAF at if_output) ---
M2b: SWAP m_pullup realloc SUCCESS -> ip_output:698 if_output(FREED m)
     (stale m is freed memory; driver deref = UAF)
----------------------------------------------------------------
  ip_output: m=0x8004a0ed0 label=109 (head)
  ip_output: mpls_output_process=TRUE -> ifp->if_output(stale m=0x8004a0ed0)
  if_output: received m=0x8004a0ed0 label=109
  [UAF] deref of freed mbuf 0x8004a0ed0 (label=109) at if_output(m) deref (magic=0xdeadc0de)

  --- M2b result ---
  allocs=3 frees=2  outstanding(live)=1  double_free=0  uaf=1
  if_output derefs=1  last_received_label=-1
  EXPECT: uaf >= 1 (driver deref of freed m at if_output)
  *** UAF CONFIRMED ***

==================== PATCHED (mpls_output(struct mbuf **mp)) ====================
--- All 6 scenarios PASS (zero double-free, zero UAF, zero leak) ---
All M1/M2 scenarios should PASS (zero double-free/UAF/leak).
  allocs=1 frees=1  outstanding(live)=0  double_free=0  uaf=0
  if_output derefs=0  last_received_label=-1
  EXPECT (fixed): double_free=0, uaf=0, live_allocs=0 (no stale deref)
  *** PASS โ€” fix eliminates the manifestation ***
  allocs=2 frees=2  outstanding(live)=0  double_free=0  uaf=0
  if_output derefs=0  last_received_label=-1
  EXPECT (fixed): double_free=0, uaf=0, live_allocs=0
  *** PASS โ€” fix eliminates the manifestation ***
  allocs=2 frees=2  outstanding(live)=0  double_free=0  uaf=0
  if_output derefs=1  last_received_label=105
  EXPECT (fixed): double_free=0, live_allocs=0 (new head freed, not leaked)
  *** PASS โ€” fix eliminates the manifestation ***
  allocs=2 frees=2  outstanding(live)=0  double_free=0  uaf=0
  if_output derefs=1  last_received_label=107
  EXPECT (fixed): if_output_received_label == pushed head label (CORRECT), live_allocs=0
  *** PASS โ€” fix eliminates the manifestation ***
  allocs=3 frees=3  outstanding(live)=0  double_free=0  uaf=0
  if_output derefs=1  last_received_label=0
  EXPECT (fixed): uaf=0, live_allocs=0 (driver gets the new head)
  *** PASS โ€” fix eliminates the manifestation ***
  allocs=1 frees=1  outstanding(live)=0  double_free=0  uaf=0
  if_output derefs=1  last_received_label=111
  *** PASS โ€” fix eliminates the manifestation ***

==================== MODULE BUILD (patched mpls.ko) ====================
=== DF-0754 FIX VALIDATION: patched mpls.ko module build ===
=== Source: /usr/src/sys/netproto/mpls/ with fix.diff applied ===
=== Compiler: cc 8.3 [DragonFly] Release/2019-02-22 ===
=== Build flags: -D_KERNEL -Wall -Werror -DKLD_MODULE ===

FIX_MODULE_BUILD=0

=== Result ===
-rw-r--r--  1 root  wheel  7152 Jul  5 08:40 /usr/obj/root/mpls_build/mpls.ko
SHA256 (/usr/obj/root/mpls_build/mpls.ko) = 72fb38102de44c5979d3a3ba02a6c9047253a6bf9328ffbd272a48f32cf4141b

=== Patched signatures (proof the fix is compiled in) ===
/usr/src/sys/netproto/mpls/mpls_output.c:46:static int mpls_swap(struct mbuf **, mpls_label_t);
/usr/src/sys/netproto/mpls/mpls_output.c:47:static int mpls_pop(struct mbuf **, mpls_s_t *);
/usr/src/sys/netproto/mpls/mpls_output.c:50:mpls_output(struct mbuf **mp, struct rtentry *rt)
/usr/src/sys/netproto/mpls/mpls_output.c:149:mpls_output_process(struct mbuf **mp, struct rtentry *rt)
/usr/src/sys/netproto/mpls/mpls_output.c:186:mpls_swap(struct mbuf **mp, mpls_label_t label) {
/usr/src/sys/netproto/mpls/mpls_output.c:217:mpls_pop(struct mbuf **mp, mpls_s_t *sbit) {
/usr/src/sys/netproto/mpls/mpls_var.h:59:int			mpls_output(struct mbuf **, struct rtentry *);
/usr/src/sys/netproto/mpls/mpls_var.h:60:boolean_t		mpls_output_process(struct mbuf **, struct rtentry *);
/usr/src/sys/netinet/ip_output.c:695:		if (!mpls_output_process(&m, ro->ro_rt))
/usr/src/sys/netinet/ip_output.c:739:			if (!mpls_output_process(&m, ro->ro_rt))
/usr/src/sys/netproto/mpls/mpls_input.c:208:	error = mpls_output(&m, cache_rt->ro_rt);

VERDICT: UNPATCHED harness shows DOUBLE-FREE (M1a/M1b), LEAK (M1c/M2a),
        UAF (M2b) in ALL five DF-0754 manifestation scenarios.
        PATCHED harness shows ALL PASS. Patched mpls.ko builds clean (-Werror).
        => fix.diff (same as DF-0753) CLOSES DF-0754.