# DF-5278 — VERDICT

**Status: reproduced** (impact: denial of service — non-terminating parse
loop with unbounded kernel memory growth; confidence: certain)

## Evidence (userspace verbatim harness, `../DF-5276/run.t2a.log`, `run.t3.log`)

1. **Dead oversize check (harness `t2a`)**: a String item with length
   0x0500 (> `0x7f*4 - off`, the VPD address-space bound):

   ```
   pci0:2:3:1: invalid VPD data, remain 0x500
   t2a:   vpd_ident len = 1280 (parse did not abort -- state=-1 was overwritten)
   ```

   The kernel *prints* "invalid VPD data" and then keeps parsing: the
   `state = -1` at `pci.c:1096` is unconditionally overwritten by the
   `switch (name)` at `pci.c:1107-1136` (state 1/2/5). `remain` up to
   65535 is accepted.

2. **Negative `remain` → unterminatable state machine (harness `t3`)**: a
   VPD-R item with declared length 1 (smaller than its own 3-byte keyword
   header) drives `remain -= 3` (`pci.c:1189`, `pci.c:1260`) to -2; every
   exit transition tests `remain == 0`, which can never be true again.
   Fed an endless keyword stream (device wraps its VPD data), capped only
   by the simulator:

   ```
   t3: parser stopped ONLY because the simulator capped the
   t3: device at 20000 bytes (vrs.off=20000, config reads=1010001).
   t3: allocations during the capped run: 5010 kmalloc/krealloc, 272014 bytes, largest 131072 bytes
   t3: state machine had not terminated: every keyword kept
   t3: consuming bytes with remain<0; with an uncapped device the
   t3: loop and the allocations grow without bound.
   ```

   20,000 device bytes → 5,010 allocations (272 KB, keyword array doubled
   to 128 KB) in 0.11 s of wall time, and the state machine was still
   cycling — the *device cap*, not parser logic, ended the run. Scaling:
   a device completing VPD reads promptly sustains ~2 MB/s of parse
   throughput here → hundreds of thousands of allocations per second →
   kernel heap exhaustion plus a permanently stalled (M_WAITOK-blocked)
   probing thread. Each new keyword `kmalloc`s a value buffer and the
   keyword array grows by doubling; additionally every new section header
   overwrites `vpd_ros`/`vpd_w`/`vpd_ident` without freeing the previous
   allocations (`pci.c:1109,1120,1128`) — an unbounded leak under the
   non-terminating loop.

## Fix validation

With `fix.diff`, harness `t2a` aborts at the (now effective) bound check
(`vpd_ident NULL`) and `t3` exits at the malformed section header after a
handful of allocations — see `../DF-5276/fix-validation.log`.

## Caveats

Same harness model as DF-5276: verbatim kernel code, device simulated at
the `PCIB_*` boundary (the guest has no VPD-capable emulated device).
In-kernel non-termination was additionally observed indirectly: the
`vpdoflow.ko` module's ~1 s of `DELAY(1)`-spin per stalled register
(`pci.c:929-933`) shows the poll loop cost that bounds a *slow* device;
a fast malicious device removes that bound entirely.

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