# DF-0741 — gre_mobile_input bcopy size underflow

**Severity:** Medium (CVSS 3.1: `AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H`)
**Class:** CWE-787 OOB write + CWE-191 integer underflow
**Location:** `sys/netinet/ip_gre.c:237-238` (`gre_mobile_input`)

## Bug

`gre_mobile_input` (the IP-in-IP mobile-IP decapsulation handler,
registered for IPPROTO_MOBILE = 55 in `sys/net/gre/if_gre.c:140-154`)
computes a `bcopy` length without validating that the head mbuf is
large enough:

```c
bcopy((caddr_t)(ip) + (ip->ip_hl << 2) + msiz,
      (caddr_t)(ip) + (ip->ip_hl << 2),
      m->m_len - msiz - (ip->ip_hl << 2));          /* signed int -> size_t */
```

All three operands are `int`.  When a short / chained-mbuf packet has
`m->m_len < ip_hl*4 + msiz`, the third argument goes negative and
is then implicitly widened to `size_t` for `bcopy()` → near-`2^64`
byte copy → immediate page fault → kernel panic.

## Reachability

`gre` is a loadable module (`if_gre.ko`).  When an admin creates a gre
interface in mobile mode (`ifconfig greN -link0` → `g_proto =
IPPROTO_MOBILE`, `sys/net/gre/if_gre.c:461-470`) and brings it up,
`encap_attach` registers `in_mobile_protosw` whose `.pr_input =
gre_mobile_input` for IPPROTO_MOBILE.  Any packet routed to the host
with `ip_p = 55` and matching outer src/dst then takes the path:

```
ip_input -> ip_protox[55] -> encap4_input
  -> mask_match -> (*psw->pr_input)() = gre_mobile_input
```

`gre_mobile_input` does **no** `m_pullup()` and **no** length check.

## PoC

`trigger.c` (compile: `cc -O2 -Wall -o trigger trigger.c`,
run: `./run.sh`) opens a raw socket `SOCK_RAW`/`IPPROTO_MOBILE` with
`IP_HDRINCL` and sends three short crafted packets to 127.0.0.1 after
setting up `gre0` in mobile mode.  Each variant is shorter than the
mobile header requires, so each independently triggers the underflow.

## Reproduction

**Baseline** (`6.5-DEVELOPMENT #0`, shipped `if_gre.ko`):
- `panic: vm_fault: fault on stack guard`
- `memmove() at memmove+0x24f`
- `encap4_input() at encap4_input+0x20b`
- ssh dies, `vm.sh status` => down

**Patched** (rebuilt `if_gre.ko` with `fix.diff`):
- trigger exits 0 cleanly, x3 consecutive runs
- guest stays up
- no panic in `boot.log`

## Impact

DoS / kernel panic.  No `uid=0` escalation: the over-write that
*would* land in the mbuf cluster faults on the next unmapped page (the
kernel stack guard) before any attacker-controlled write reaches a
victim object, so there is no corruption primitive to convert.

Realistic threat: any remote attacker who can deliver a packet with
the matching outer IP src/dst to a host that has a gre-mobile tunnel
configured can panic the kernel with a single short datagram.

## Fix

`fix.diff` adds a one-line guard before the `bcopy`:

```c
if (m->m_len < (ip->ip_hl << 2) + msiz) {
    m_freem(m);
    return(IPPROTO_DONE);
}
```

This matches the finding markdown's recommended fix.

## Build single-fix module

`gre` is NOT in the static `X86_64_GENERIC` kernel, so the correct
single-fix build is the module, not a full `nativekernel`:

```sh
cd /usr/src/sys/net/gre
make KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC
cp if_gre.ko /boot/kernel/if_gre.ko
sync; reboot
```

A naive `make nativekernel` does NOT rebuild `if_gre.ko` (it reuses
the warm obj), so the first fix-validation pass appears to fail until
the module is rebuilt directly.

## Files

| file             | purpose |
|------------------|---------|
| `trigger.c`      | userspace raw-socket injector (3 short IPPROTO_MOBILE packets) |
| `build.sh`       | `cc -O2 -Wall -o trigger trigger.c` |
| `run.sh`         | setup gre-mobile tunnel + run trigger |
| `build.log`      | baseline trigger build output |
| `panic.txt`      | baseline panic signature from `boot.log` |
| `run.log.baseline_panic.txt` | full interleaved baseline panic |
| `fix.diff`       | standalone git-apply-able fix (one-line guard) |
| `fix_build.log`  | single-fix module build output (and full nativekernel log) |
| `fix_run.log`    | patched-kernel trigger output (exit 0, no panic) |
| `env.txt`        | guest uname / cc / sysctls |
| `VERDICT.md`     | full narrative |
| `manifest.json`  | machine-readable catalog |
