# DF-1249 — dc_pnic_rx_bug_war unbounded copy loop (heap overflow if PNIC produces >5 fragments)

## Verdict
**SOURCE-CONFIRMED (real code-level hardening gap, speculative exploitation), INCONCLUSIVE at runtime** — the `if_dc` driver IS compiled into the running GENERIC kernel, but the guest's NIC is `vtnet0` (virtio-net); no DEC 21143 / PNIC 82c168/82c169 NIC is attached, so `dc_pnic_rx_bug_war` is never called. Fix authored and compile-validated.

## Mechanism (source trace)
`dc_pnic_rx_bug_war()` salvages a corrupted multi-fragment RX frame from the PNIC chip bug by copying fragments into a fixed salvage buffer:

- `sys/dev/netif/dc/if_dc.c:1976` — `sc->dc_pnic_rx_buf = kmalloc(DC_RXLEN * 5, M_DEVBUF, M_WAITOK);` → buffer is **7680 bytes** (5 × 1536).
- `sys/dev/netif/dc/if_dc.c:2414-2429`:
  ```
  ptr = sc->dc_pnic_rx_buf;          /* 7680-byte buffer */
  bzero(ptr, DC_RXLEN * 5);
  while (1) {
      c = &sc->dc_ldata->dc_rx_list[i];
      rxstat = c->dc_status;
      m = sc->dc_cdata.dc_rx_chain[i];
      bcopy(mtod(m, char *), ptr, DC_RXLEN);     /* copies 1536 bytes each iteration */
      ptr += DC_RXLEN;                            /* advances 1536, NO bound check   */
      if (i == idx || rxstat & DC_RXSTAT_LASTFRAG)
          break;
      dc_newbuf(sc, i, m);
      DC_INC(i, DC_RX_LIST_CNT);                  /* ring is 64 or 192 descriptors   */
  }
  ```
- The loop's only termination is `i == idx` (reaching the LASTFRAG descriptor index) or `rxstat & LASTFRAG`. **There is no cap on the number of iterations.** The caller (`if_dc.c:2525-2533`) enters this path when `FIRSTFRAG` and `LASTFRAG` are not both set in the same descriptor, walking from the saved FIRSTFRAG index to the LASTFRAG index.
- If the PNIC chip (or a corrupted descriptor ring) yields more than 5 fragments between FIRSTFRAG and LASTFRAG, `ptr` walks past the end of the 7680-byte `dc_pnic_rx_buf` into adjacent kernel heap. The ring (`DC_RX_LIST_CNT` = 64 or 192, `if_dcreg.h:459/461`) bounds the *worst case* to 64–192 iterations ⇒ up to ~98KB–294KB of overflow.
- Additionally `total_len = DC_RXBYTES(rxstat)` (`if_dc.c:2432`, 14-bit) is used unchecked in `bcopy(ptr, mtod(m,char*), total_len)` at line 2453.

## Why not reproduced at runtime
- Guest NIC is `vtnet0` (virtio-net). The `dc` driver probe never matches a virtio device, so no `dc_softc` with `DC_PNIC_RX_BUG_WAR` is ever instantiated.
- The PNIC RX-bug war is gated by `sc->dc_flags & DC_PNIC_RX_BUG_WAR`, set only for `DC_TYPE_PNIC` (`if_dc.c:1973-1975`), which requires a Lite-On 82c168/82c169 chip.
- The finding's own confidence is **speculative**: it requires the PNIC chip to produce >5 descriptors for a single corrupted RX event (worse than the known bug the war was written to handle). This is a driver-robustness issue against misbehaving hardware, not an unprivileged-user-reachable bug.

## Fix (fix.diff, compile-validated)
Cap the loop at 5 iterations (matching the `DC_RXLEN * 5` buffer) so a misbehaving PNIC cannot overflow the salvage buffer. The patched `if_dc.c` builds cleanly into `if_dc.ko` (52696 bytes) with gcc 8.3, `-Werror`.

## Realistic impact ceiling
Kernel heap overflow from a misbehaving PNIC NIC (worst-case ~294KB overflow). On this guest: **not reachable** (vtnet0 NIC, no PNIC). Defense-in-depth driver hardening.
