# DF-3362 — pf_fragcache overlap-chop path: INVARIANTS KASSERT panics on multi-mbuf fragments

## What
`pf_fragcache()`'s precut>0 ("chop") path (sys/net/pf/pf_norm.c:653-690)
duplicates the incoming fragment and tail-trims the duplicate down to the
IP header before re-attaching the chopped payload:

```c
*m0 = m_dup(m, M_NOWAIT);
m_adj(*m0, (h->ip_hl << 2) - (*m0)->m_pkthdr.len);   /* negative */
...
KASSERT(((*m0)->m_next == NULL), ...);               /* pf_norm.c:667 */
```

`m_adj(mp, negative)` (sys/kern/uipc_mbuf.c:1859) tail-trims by zeroing
the lengths of trailing mbufs — it does **not** unlink them. For any
fragment that arrives as a multi-mbuf chain, the trimmed duplicate still
has `m_next != NULL` → KASSERT panics on every INVARIANTS kernel. The
second KASSERT at :684 (`(int)m->m_len == ntohs(h->ip_len) - precut`)
is built on the same single-mbuf assumption and fires instead whenever
the payload does not fit into the header mbuf.

Multi-mbuf fragments are not exotic: `tapwrite()` builds every frame as
an MGETHDR + MGET chain (sys/net/tap/if_tap.c tapwrite), and various
tunnel/virtual/aggregate input paths do the same.

## Preconditions
- pf enabled with `scrub ... fragment crop` (fragcrop) — the
  non-buffering fragment cache mode.
- Attacker sends an overlapping fragment (precut>0) that arrives in a
  multi-mbuf chain, e.g. via a tap/VM-facing interface. Two packets:
  first fills the cache range, second overlaps it.
- INVARIANTS kernel (DFly development/debug kernels; GENERIC release
  kernels compile KASSERTs out — there the code proceeds with the
  zero-length mbufs chained, benign for the packet but violating the
  author's invariants).

## Reproduce (see panic.txt — observed)
```
# root on the guest:
kldload pf
ifconfig tap create && ifconfig tap0 up
printf 'scrub in on tap0 all fragment crop\n' > /root/poc/fragcrop.conf
pfctl -f /root/poc/fragcrop.conf && pfctl -e
/root/poc/fragcache_kassert    # writes two Ethernet frames to /dev/tap0
```

## Expected / observed output
```
panic: (*m0)->m_next != NULL: pf_fragcache
cpuid = 3
pf_fragcache() at pf_fragcache+0xcef
pf_normalize_ip() at pf_normalize_ip+0x49d
pf_test() at pf_test+0x114
pf_check_in() at pf_check_in+0x2e
pfil_run_hooks() at pfil_run_hooks+0x6f
Debugger("panic")
```
Guest dies on the second frame (the first only creates the cache entry).

## Fix
Unlink the zero-length trailing mbufs after the tail-trim and assert on
the recomputed packet total instead of the head mbuf length — see
fix.diff (validated: kernel rebuilt with the fix in-guest, the same
trigger no longer panics).
