# DF-3756 VERDICT — OOB write past `tulip_mediainfo[10]` in `tulip_srom_decode()`

## Status: `inconclusive` (kernel path untestable on this guest — no DEC 2114x
## hardware; defect itself source-certain and logic-model-CONFIRMED)

## Root cause

`tulip_srom_decode()` (`sys/dev/netif/de/if_de.c:2073`) parses the card's
serial EEPROM ("SROM") contents byte by byte using a walking pointer `dp`
(the OOB *read* side of that walk is known finding DF-1205). Independently of
the read walk, the function also fills a **fixed 10-entry array** through a
second walking pointer:

- `sys/dev/netif/de/if_devar.h:483` — `tulip_media_info_t tulip_mediainfo[10];`
- `if_de.c:2080` — `tulip_media_info_t *mi = sc->tulip_mediainfo;`
- `if_de.c:2121` (21041 branch) / `if_de.c:2183` (2114x branch) —
  `blocks = *dp++;` — **raw, unvalidated SROM byte** (0–255)
- block loop `if_de.c:2122` / `if_de.c:2184` — for every well-formed media
  block the code writes through `mi` and advances it **with no bound check**:
  - case 0 (21140 GPR): writes `if_de.c:2205-2222`, `mi++` at `if_de.c:2223`
  - case 1 (21140 MII): `mi++` at `if_de.c:2276` (PHY found)
  - case 2 (21142 SIA): writes `if_de.c:2289-2322`, `mi++` at `if_de.c:2323`
  - case 3 (21142 MII): `mi++` at `if_de.c:2373`
  - case 4 (21143 SYM): writes `if_de.c:2386-2400`, `mi++` at `if_de.c:2401`
  - 21041 extended blocks: `mi++` at `if_de.c:2138`

Nothing relates `blocks` (≤255) to the 10 available slots. An SROM leaf with
≥11 type-0 blocks (4 bytes each; 23 fit in a 128-byte SROM) drives `mi` past
the array into the remainder of the heap-allocated softc and beyond:

```
tulip_mediainfo[10]  (+280B region)
tulip_setup_tag       if_devar.h:488
tulip_setup_map       if_devar.h:489
tulip_setup_dma_addr  if_devar.h:490
tulip_setupbuf        if_devar.h:491   <- real kernel pointer (DMA-mapped buffer)
tulip_setupdata[48]   if_devar.h:492
tulip_boardid[24]     if_devar.h:493
tulip_rombuf[128]     if_devar.h:494
... then past the softc allocation into adjacent kernel heap
```

`sizeof(tulip_media_info_t)` = 28 (x86-64); the model's crafted 23-block SROM
produced 13 OOB writes reaching +364 bytes past the array, clobbering
`tulip_setup_tag`, `tulip_setup_map`, **`tulip_setupbuf`** (the pointer later
used as the `bcopy()` *destination* in `tulip_txput_setup()`, `if_de.c:3637`),
13/48 `tulip_setupdata` words, and 8 `tulip_boardid` bytes (run.log).
Additionally each block stores `sc->tulip_mediums[media] = mi`
(`if_de.c:2206/2290`…), leaving OOB pointers that `tulip_media_set()`
(`if_de.c:247`) dereferences at runtime.

## Reachability / gates

`tulip_pci_attach()` → `tulip_read_macaddr()` (`if_de.c:4278`) →
`tulip_srom_decode()` (`if_de.c:2502`). Decode is reached when
`TULIP_HAVE_ISVSROM` is set, which happens when:

1. the SROM CRC is valid (`if_de.c:2480`) — the CRC is 2 attacker-chosen
   bytes, trivially computed for a crafted image; **or**
2. bytes 126/127 are 0xFF with the legacy zero-prefix format
   (`if_de.c:2486-2497`); **or**
3. **chipid ≥ 21142 — unconditionally, no CRC check at all**
   (`if_de.c:2498-2500`).

So on 21142/21143-based cards, *any* EEPROM garbage with a plausible leaf
pointer reaches the overflow. All of this runs at boot or module load, in
device attach context.

## Threat model

Requires attacker-crafted SROM content: a malicious or reprogrammed PCI card
planted in the machine, or an attacker with brief physical access able to
reprogram the card's serial EEPROM (the 2114x SROM is rewriteable on many
boards). Not reachable remotely or by local users of a stock machine. Given
that precondition the primitive is strong: 28-byte-stride OOB writes of
SROM-controlled data over kernel heap including a live kernel pointer
(`tulip_setupbuf`) whose later use (`bcopy` at `if_de.c:3637`,
`bus_dmamem_free` paths) turns corruption into a write-through/freed-pointer
primitive → potential arbitrary kernel write with a fully crafted EEPROM.
Severity Low **because of the hardware precondition** (same precedent as
DF-1205); the memory-safety defect itself is certain.

## Verification narrative

1. Read the full driver + `if_devar.h`; established array sizes and every
   `mi++` site (listed above). No bound check exists anywhere between
   `mi = sc->tulip_mediainfo` and the last `mi++`.
2. Guest check (`env.txt`): driver `pci/if_de` compiled into
   X86_64_GENERIC, `hw.de.*` OID count = 0, no 0x1011 PCI devices → no de(4)
   instance can exist → kernel path not executable on this rig.
3. Built and ran the 1:1 userspace logic model on the guest
   (`build.log`, `run.log`): 13 OOB writes confirmed, +364 bytes past the
   array, pointer members clobbered. Model arithmetic matches the kernel
   struct layout exactly (28B entries; verified by printing
   `sizeof(tulip_media_info_t)`).
4. QEMU's upstream `tulip` device model (DEC 21143) has a **hardcoded, valid
   SROM** in `hw/net/tulip.c`; feeding a crafted EEPROM would require
   patching QEMU itself, so kernel-path reproduction is not possible on this
   rig without new infrastructure.

**Classification:** `inconclusive` / reproduced=0 / impact=none (on this
guest). The bound violation is proven at source level and by the logic model;
kernel-path execution requires hardware absent from the audit rig.

## Recommended fix

Bound `mi` against `sc->tulip_mediainfo[10]` at the top of both block loops
(see `fix.diff` — also stops the 21041-branch variant at `if_de.c:2138`).
The DP/rombuf read walk is DF-1205's to fix; this diff deliberately only
bounds the `mi` writes.

## Kernel references

- sys/dev/netif/de/if_devar.h:483 (array[10]), 488-494 (vulnerable tail)
- sys/dev/netif/de/if_de.c:2080, 2121-2138, 2183-2184, 2205-2223, 2276,
  2289-2323, 2373, 2386-2401, 2480-2502, 3637, 4278

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