# VERDICT DF-4498 — sbsh encap_frame() infinite repack loop on 1–4 byte frames

## Bottom line

**INCONCLUSIVE at runtime (hardware-gated), defect CONFIRMED at source level.**
The loop is provable by pure code logic — no race, no device misbehavior
needed, only a frame with `0 < m_pkthdr.len < 5` in `if_snd` while the unit is
ACTIVE. The QEMU guest has no Granch SBNI12/16 PCI device (`env.txt`), so the
driver never attaches and the frame can never be enqueued.

## Source-level proof

1. `start_xmit_frames()` dequeues any mbuf with nonzero `m_pkthdr.len` and
   calls `encap_frame()` (`if_sbsh.c:636-643`).

2. `encap_frame()` loop head (`:660-672`):
   ```c
   look_for_nonzero:
       for (m = m_head; !m->m_len; m = m->m_next) ;
       cur_tbd = sc->regs->LTDR & 0x7f;
       do {
           if (m->m_len < 5 || cur_tbd == ((sc->head_tdesc - 1) & 0x7f)) {
               if ((m_head = repack(sc, m_head)) != NULL)
                   goto look_for_nonzero;
               else
                   return;
           }
   ```

3. `repack()` always produces `m_new->m_pkthdr.len = m_new->m_len =
   m->m_pkthdr.len` (`:720-721`). For a 1–4 byte frame the repacked mbuf has
   `m_len < 5` again, so the `goto look_for_nonzero` path re-tests the same
   condition on a fresh 1–4 byte mbuf — **unbounded
   allocate / copy / free cycle**.

4. The cycle runs in the caller's context holding `ifp->if_serializer`
   (network thread / syscall path via `sbsh_start`, `:313-321`), with
   `M_NOWAIT` allocations that keep succeeding under normal memory pressure —
   the only exit is mbuf exhaustion, which the loop itself prevents by freeing
   what it allocates. Result: permanent kernel-mode spin on one CPU with the
   interface serializer held (livelock DoS; everything that touches the ifnet
   or takes that serializer blocks).

5. Secondary defect on the same lines: when `repack()` fails (returns NULL),
   `encap_frame()` returns without freeing `m_head` — the dequeued packet is
   leaked (`:668-671`).

## Injection vectors (why Low and not Medium)

* `/dev/bpf` write of a runt frame — requires root/group on the bpf node
  (default root-only on DF).
* Bridge member ingress: if `sbsh0` is a bridge member, an **unprivileged LAN
  peer** on another member port can emit a runt frame that
  `bridge_output()` forwards into `if_snd` (bridges do not enforce a minimum
  frame size). Config-dependent.
* Normal stack paths (ether_output ≥ 14 bytes) cannot produce it.

## Why not `reproduced`

`sbsh0` cannot exist on this guest (PCI 0x55:0x9d absent — `env.txt`);
trigger compiled (`build.log`) and failed only at `BIOCSETIF`
(`run.log`).

## Fix

`fix.diff`: (1) `repack()` refuses frames `< 5` bytes (returns NULL → caller
drops); (2) `encap_frame()` frees `m_head` on the repack-failure path, fixing
the leak as well.

## 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.
