# DF-3381 — BPF catchpacket() bh_caplen unsigned underflow (heap buffer overflow)

## What
`sys/net/bpf.c` `catchpacket()` computes the capture length as
`hp->bh_caplen = totlen - hdrlen` after clamping `totlen` to `bd_bufsize`.
On x86-64 every attached DLT has `bif_hdrlen >= 32+`
(Ethernet = `roundup2(14+32,8)-14 = 34`; DLT_NULL (lo0/tun/wg) =
`roundup2(4+32,8)-4 = 36`) because `SIZEOF_BPF_HDR = sizeof(struct bpf_hdr)
= 32` (16-byte timeval + 12 bytes, padded) and `BPF_WORDALIGN` rounds to
`sizeof(long)` = 8.  `BIOCSETBLEN` happily clamps the store-buffer size to
`BPF_MINBUFSIZE = 32` (bpf.c:794-806) which is *smaller than the header it
must hold*.  The next tapped packet then executes:

```c
totlen = hdrlen + min(snaplen, pktlen);   /* >= 36   */
if (totlen > d->bd_bufsize)                /* 32      */
        totlen = d->bd_bufsize;            /* 32      */
...
(*cpfn)(pkt, (u_char *)hp + hdrlen,
        (hp->bh_caplen = totlen - hdrlen)); /* 32-36 = 0xfffffffc */
```

i.e. a ~4 GiB copy (`bpf_mcopy`/`_bcopy`) into a `kmalloc(32)` store buffer,
starting 34/36 bytes *past the start* of that 32-byte allocation.  Attacker
packet data is written linearly over the adjacent slab until the chain/page
runs out.

## Reproduce
```
cc -O -o trigger trigger.c     # in guest (gcc 8.3)
./trigger                      # root starts it; the triggering child is uid=nobody
```
Expected: `panic: bpf_mcopy` (or a page fault inside the 4 GB copy),
backtrace `bpf_mcopy <- catchpacket <- bpf_mtap <- bpf_ptap <- if_simloop`.

Observed (guest `DragonFly 6.5-DEVELOPMENT #0 ... X86_64_GENERIC`,
INVARIANTS): exactly that — see `panic.txt`, `run.log`.

## Fix
`fix.diff` (validated in-guest, see VERDICT.md): reject `BIOCSETBLEN` once
buffers exist, reject `BIOCSETIF` when `bd_bufsize < bif_hdrlen`, and guard
`catchpacket()` against `totlen < hdrlen` (plus a NULL-store-buffer guard).
