# DF-5276 — VERDICT

**Status: reproduced** (impact: kernel heap memory corruption; confidence: certain)

## What was run

1. **Userspace verbatim harness** (`vpd_harness`, guest `/tmp/df5276`, built
   from `vpd_extracted.h` pulled byte-identical from the guest's
   `/usr/src/sys/bus/pci/pci.c`, sha256
   `198a32bea84f635ab6c2b674094f2d19ed7099ee78efe4f1f7d74c5aa8220aeb`,
   identical to the audit host's `sys/`). The only shims are at the
   hardware/API boundary: a guard-page allocator and a simulated malicious
   PCI device that completes VPD transfers instantly and serves attacker
   bytes. `run.t1.log` / `run.t1.2.log` (two runs, deterministic):

   ```
   *** SIGSEGV writing 0x800476000 during phase 't1: zero-length VPD identifier string (DF-5276)' ***
       faulting allocation: base=0x800475000 kmalloc size=1 (guard page at +4096)
       => 4096 bytes were written past a 1-byte kmalloc allocation
   ```

   The parser was fed `82 00 00` (String item, length 0) + 8192 payload
   bytes; it `kmalloc(1)`s the identifier and writes every payload byte
   sequentially. Exit 77 both runs; the write would have continued to 8192
   bytes and beyond (the device controls the length).

2. **Kernel-context proof with the REAL kernel allocator** (`kmod/vpdoflow.ko`,
   `dmesg-kmod.txt`): the same verbatim parser compiled into a KLD, using
   the kernel's real `kmalloc()`. Result (live dmesg):

   ```
   DF5276: parser kmalloc(1) -> 0xfffff8004f0c28d8 (real slab chunk: 8 bytes)
   pci0:2:3:1: failed to read VPD data.
   DF5276: payload pattern found at offsets 8..1023 of the freed chunk (1016/1016 bytes match); chunk size 8
   DF5276: KERNEL HEAP OVERFLOW REPRODUCED: device-controlled bytes verified 1024 bytes deep into a 8-byte kernel heap chunk (allocation size 1)
   ```

   1024 device-controlled bytes landed contiguously in kernel memory
   starting at a 1-byte `kmalloc` (8-byte slab chunk), i.e. ~128 adjacent
   M_DEVBUF chunks were smashed. The chunk pointer is recorded by a thin
   wrapper over the real allocator because the parser's own error cleanup
   frees+NULLs `vpd_ident` before returning (itself DF-5277's precondition).
   The guest survived this run (corruption was latent), which is why the
   pattern could be verified post-hoc.

## Why this is a real primitive

- Content AND length are fully attacker (device) controlled: each VPD byte
  read is copied 1:1 into `vpd_ident[i++]` (`sys/bus/pci/pci.c:1139-1145`).
- No bound exists anywhere between the `kmalloc(remain + 1)` at
  `pci.c:1109` (with `remain == 0` from the two device-supplied length
  bytes) and the writes; the only exit (`remain == 0`) can never fire once
  remain has gone negative.
- An honest-but-glitched device also triggers it: a stalled VPD read still
  returns data slowly (each 4 bytes costs one 1e6-iteration `DELAY(1)` poll,
  `pci.c:918-933`), so the overflow proceeds at ~bytes/second even without
  a "fast" malicious device.
- IOMMUs do not protect: config-space parsing is host-CPU initiated.

Threat model (per this audit's file brief): malicious/compromised PCIe
device — Thunderbolt hotplug peripheral, VM-emulated device, or a device
whose VPD EEPROM is corrupt. On the no-SMAP/SMEP/KASLR reference guest the
primitive is a textbook slab-groom -> overwrite adjacent object -> control
flow; the device attacker here already equals kernel compromise, but the
same code path is what a *local* attacker with a rented/pass-through PCI
function would target.

## Fix validation

`fix.diff` (8 hunks; shared with DF-5277/5278/5279) applied to the guest's
`/usr/src`; `make nativekernel` + install; rerun of the exact harness
(re-extracted from the patched tree) and of the KLD — see
`fix-validation.log` in this pack: t1 no longer faults (item rejected,
`vpd_ident` NULL), t2/t2a/t3 fixed behaviors, `vpdoflow.ko` prints
"parser never allocated vpd_ident (item rejected -- fixed kernel)".

## Caveats (honesty)

- The QEMU guest has no emulated device with a controllable VPD
  capability, so the *live config-space* entry path could not be driven
  end-to-end; instead the exact kernel code (byte-identical extraction,
  sha-verified) was executed against a device model at the `PCIB_*`
  boundary — in userspace (guard-page proof) and in-kernel (real `kmalloc`
  proof). All defect logic between those boundaries is the kernel's own.
- Exploit chain to `uid=0` was not developed: the attacker in this threat
  model is the device itself (kernel-memory write already equals ring-0
  compromise); no unprivileged-local-user trigger exists for this path in
  the base system (no in-tree driver calls `pci_get_vpd_*` at probe today;
  `/dev/pci` ioctls are `SYSCAP_RESTRICTEDROOT`-gated and have no VPD
  ioctl).

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