# VERDICT — DF-1287

## Status
**INCONCLUSIVE (source-confirmed; not runtime-triggerable on this guest).**

## Mechanism (source-confirmed)
`siba_pci_sprom` (`sys/dev/netif/bwn/siba/siba_core.c:1372`):

1. **Line 1378** — allocates a 64-word buffer (`SIBA_SPROMSIZE_R123 = 64`,
   `sibareg.h:303`) — i.e. 128 bytes.
2. **Line 1382** — `siba_sprom_read(siba, buf, SIBA_SPROMSIZE_R123)` reads
   64 words and sets `siba->siba_spromsize = 64`.
3. **Line 1383** — runs `sprom_check_crc(buf, siba->siba_spromsize)` on the
   64-word image.
4. **Lines 1384-1394** — *only if CRC fails*, free + realloc a 220-word
   buffer (`SIBA_SPROMSIZE_R4 = 220`, `sibareg.h:304`).
5. **Line 1398** — `sprom->rev = buf[siba_spromsize - 1] & 0xff`.
6. **Lines 1403-1428** — dispatch on `siba->siba_chipid` then on
   `sprom->rev`:
   - rev 1/2/3 → `siba_sprom_r123` (stays within 64 words).
   - rev 4/5   → `siba_sprom_r45`
   - rev 8     → `siba_sprom_r8`

`siba_sprom_r45` (`siba_core.c:1632`) walks `SIBA_SPROM4_PWR_INFO_CORE0..3`
(`sibareg.h:402-405`: `0x1080..0x110A`) plus per-core offsets, i.e.
`SIBA_OFFSET = (0x1080..0x110A - SIBA_SPROM_BASE) / 2 = 64..133`, far past
the 64-word buffer. `siba_sprom_r8` (`siba_core.c:1727`) walks
`SIBA_SPROM8_*` constants up to `SIBA_SPROM8_CDDPO = 0x1192`
(`sibareg.h:523`), i.e. `SIBA_OFFSET = 0xc8 = 200`, plus the
`SIBA_SHIFTOUT_4` macro adds another +2 → word 202.

The trigger is a crafted SPROM image with a valid 64-word R123 CRC and a
last byte claiming rev 4/5/8. `siba_sprom_r45` / `r8` then read up to
~276 bytes past the 128-byte allocation. The read values are written into
`struct siba_sprom` fields.

## Fix
Validate `siba->siba_spromsize >= SIBA_SPROMSIZE_R4` before dispatching to
`siba_sprom_r45` / `siba_sprom_r8` in any of the three dispatch sites
(chipid 0x4321 path, rev 4/5 case, rev 8 case). If too small, log a
warning and fall back to `siba_sprom_r123` (the only parser guaranteed to
fit). See `fix.diff`.

## Verification on this guest
- `siba_bwn` and `bwn` are statically compiled in
  (`device siba_bwn`, `device bwn` at `sys/config/X86_64_GENERIC:267-268`).
- No Broadcom BCM43xx PCI NIC in `pciconf -l` (only i440FX/PIIX/ACPI/
  virtio-net/virtio-blk/std-VGA), so `siba_pci_probe` never matches and
  `siba_pci_sprom` is never called. **Not runtime-triggerable.**
- Source-level proof:
  - `siba_core.c:1378` — initial alloc `SIBA_SPROMSIZE_R123 * 2 = 128` bytes.
  - `siba_core.c:1382-1383` — read + CRC.
  - `siba_core.c:1384-1394` — realloc only on CRC fail.
  - `siba_core.c:1398` — `sprom->rev = buf[siba_spromsize - 1] & 0xff`.
  - `siba_core.c:1408, 1418, 1421` — dispatch to r45 / r8 with no size check.
  - `siba_core.c:1632-1710` — `siba_sprom_r45` reads words 64..133+.
  - `siba_core.c:1727-1860` — `siba_sprom_r8` reads up to word ~202.
  - `sibareg.h:303-305, 402-405, 523, 532, 557` — confirming the constants.
- Fix validation: combined `X86_64_GENERIC` rebuild with this fix applied
  exited `rc=0`. Full log: `combined_build.log`.

## Exploit chain
None. The primitive is a kernel-heap OOB read past a 128-byte `M_DEVBUF`
allocation. The read values feed `struct siba_sprom` fields (wireless
calibration data). The realistic ceiling is information leak of adjacent
slab data; there is no write primitive through this path.

## Realistic impact ceiling
Hardware-attacker (PCIe WiFi card or USB-attached Broadcom chip with crafted
SPROM) heap OOB read at attach time. CVSS:
`AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:H` — `AV:L` because physical access is
the realistic vector (an external WiFi NIC); the read is large enough (up
to ~276 bytes) to leak substantial kernel heap.

## PoC changes
Folder was empty; added `trigger_analysis.c`, `build.sh`, `run.sh`,
`README.md`, this `VERDICT.md`, `fix.diff`, `manifest.json`,
`combined_build.log`.

## Recommended fix
Matches the finding proposal: validate `siba_spromsize >= SIBA_SPROMSIZE_R4`
before dispatching to r45/r8. See `fix.diff`. The implementation falls back
to `siba_sprom_r123` rather than skipping the parser entirely, so that
already-present R123 fields (MAC, board info) are still populated.

## Fix status
**not_testable** — bug requires absent hardware. `fix.diff` applies cleanly
and compiles into `X86_64_GENERIC` (combined build `rc=0`).
