# DF-3423 — VERDICT

**Status: reproduced (impact: panic — including a demonstrated unbounded
kernel-heap `bcopy` overwrite effective on any kernel). Confidence: certain.**

## What was run

Same guest/kernel/module as DF-3422 (env.txt).  Tunnel put in MOBILE mode with
`ifconfig gre0 -link0 up` (`gre_ioctl` SIOCSIFFLAGS → `g_proto =
IPPROTO_MOBILE`, `if_gre.c:461-470`; encap re-attached with
`in_mobile_protosw`, `if_gre.c:563-566`).

## Baseline results

* `mobile` — outer ip_len=28 (20 IP + 8-byte mobile header, S-bit clear,
  checksum crafted to 0).  This is a **well-formed minimal mobile-GRE
  packet**; the decap's `ip->ip_len = ntohs(...)+sizeof(struct ip)-msiz`
  (`ip_gre.c:247`) claims 40 bytes for a 20-byte mbuf (the "+20"
  double-counts the outer header; the comment's rip_input premise does not
  hold on DragonFly), and since neither `M_HASH` nor `M_LENCHECKED` is ever
  cleared (`ip_gre.c:259-261`), `ip_input` trusts it:
  **`panic: mbuf too short`** — `ip_input+0xfaf` (KASSERT `ip_input.c:576`),
  backtrace `ip_input ← netmsg_service_loop` (panic.mobile.txt).
  Deterministic for ANY valid mobile-GRE packet — mobile tunnels cannot work
  on INVARIANTS kernels; on production kernels every decapped packet is
  processed with ip_len 20 bytes longer than the data (in-cluster OOB reads
  by transports).
* `mobileuflow` — outer ip_len=20 (NO mobile header; the 8 "header" bytes are
  read from the frame padding past ip_len, checksum still valid).  msiz=8 ⇒
  `bcopy(src, dst, m->m_len - msiz - (ip->ip_hl<<2))` = `bcopy(..., -8)`
  (`ip_gre.c:237-238`) ⇒ `(size_t)-8`:
  **`panic: overflowed mbuf 0xfffff80117e0c400`** with **`SECONDARY PANIC ON
  CPU 0` in `m_free ← sosendtcp ← soo_write ← kern_pwritev`**
  (panic.mobileuflow.txt): the unbounded copy had smashed neighbouring kernel
  heap objects — the ssh session's own mbuf came back with corrupted
  metadata.  On a production kernel the m_free INVARIANTS check is absent:
  this is a silent, unbounded, attacker-initiated kernel-heap overwrite
  (source bytes partially attacker-controlled via the frame), i.e. a genuine
  memory-corruption primitive, not just a DoS.

## Fix validation

`fix.diff` (shared with DF-3422, ip_gre.c only): guard
`m->m_len >= (ip->ip_hl<<2) + msiz` before the checksum/bcopy;
`ip->ip_len` corrected to `ntohs(ip->ip_len) - msiz`;
`M_HASH|M_LENCHECKED` cleared before `netisr_queue`.
Rebuilt `if_gre.ko`, re-ran both variants + 20 stress injections:
no panic, guest up, `dmesg` clean (`run.patched.log`).  fix_status: fixed.

## Escalation note

The `mobileuflow` primitive (huge ascending kernel copy whose source prefix
is attacker-controlled frame bytes) is the strongest of the batch; turning it
into a controlled uid=0 chain would require heap-layout steering of the copy
direction/extent — the bcopy copies *upward through the direct map* with a
fixed relationship between src and dst (dst = src - msiz), so every byte
between them is preserved while everything above the source range is
overwritten by higher memory — a destructive smear rather than a surgical
write.  We characterize it and stop there; the fix removes the primitive
entirely.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
