# DF-2223 — VERDICT

**REPRODUCED — verbatim-code, kernel-compiled rig driving the real function
over REAL guest PCI devices through the fully real predicate chain; fix
A/B-validated in the same kernel.** Impact: uninitialized-stack word consumed
by return-value-ignoring consumers → driver misbehavior / possible hw
mis-programming (DoS class). No direct info leak to userspace, no write
primitive, no privesc route (see below).

## Rig class and why

Same batch harness kernel as DF-2216 (`DFV22X`); the production
`sys/dev/drm/linux_pci.c` is `#include`d **verbatim** (sha-gated `314205e0...`),
bringing the real `linux/pci.h` inline predicate chain with it.

## Line-accurate confirmation

`linux_pci.c:35-36`:
```c
	if (!pcie_capability_reg_implemented(dev, pos))
		return -EINVAL;			/* *dst never written */
```
Upstream Linux does `*val = 0; return 0;` on this path. Consumers ported from
upstream follow the upstream contract: `pcie_get_width_cap()`
(`linux/pci.h:608-617`) ignores the return and reads `lnkcap` unconditionally;
the amdgpu call sites cited by the finding (`amdgpu_device.c:3550`,
`amdgpu/cik.c:1634`) inherit that. The predicate is genuinely reachable-false:
its first check is `pci_is_pcie(dev->dev.bsddev)` (`linux/pci.h:475`, real ivar
read — false for every classic-PCI device), and for PCIe devices
`pcie_cap_has_lnkctl()` (:445-453) is false for LNKCAP/LNKCTL/LNKSTA whenever
the capability version is 1 and the type is not ROOT_PORT/ENDPOINT/LEG_END
(v1 switch ports, bridges).

## Mechanical reproduction (decisive run, run.log)

The rig wraps each of the 7 real guest PCI device_t's in a zeroed
`struct pci_dev` (`dev.bsddev` = real device) and calls the real function with
a canary-loaded `*dst` (`0xA5A5A5A5`):

| variant | result (all 7 devices) |
|---|---|
| BASELINE `PCI_EXP_LNKCAP` | `rc=-22`, `dst=0xa5a5a5a5` **UNTOUCHED-CANARY** — bit-identical to pre-call |
| BASELINE consumer emulation (mirrors `pcie_get_width_cap` exactly: rc ignored, lnkcap consumed) | `lnkcap=0xa5a5a5a5 → width=0x1a` — **derived from uninitialized stack** |
| FIXED `PCI_EXP_LNKCAP` | `rc=-22`, `dst=0x00000000` — **WRITTEN-0 (upstream contract)** |
| FIXED consumer | `width=0xff` (`PCIE_LNK_WIDTH_UNKNOWN`) — deterministic |

`width=0x1a` is exactly `(0xA5A5A5A5 & 0x3F0) >> 4` — the canary flows through
the consumer arithmetic, proving the uninitialized word is genuinely consumed,
not merely hypothetical. Baseline vs fixed differ in precisely the filed way:
one zeroing store.

**Honest limitation (also visible in the log):** the `PCI_EXP_FLAGS_TYPE`
control was intended to exercise the *implemented* path (real config read at
line 38), but on this i440fx guest every device is classic PCI, so the
predicate's `pci_is_pcie` first-check is false for every register and the
control also returns `-EINVAL`. The implemented path is not exercisable on
this hardware; the finding concerns only the not-implemented path, which is
fully exercised.

## Fix validation (A/B, same kernel)

`fix.diff` adds `*dst = 0;` on the not-implemented branch (keeping the
`-EINVAL` return, the conservative minimal divergence-fix; upstream also
returns 0 there — noted in the finding). The patched function
(`pcie_capability_read_dword_fixed`, extracted from fix.diff-applied bytes)
compiled into the same kernel writes the deterministic zero on every device.
`fix_status: fixed`.

## Reachability (who can actually trigger this)

Two realistic flavors: (a) any non-PCIe device passed to the shim (every
predicate bit false); (b) the filed amdgpu flavor — a PCIe v1 GPU behind a
switch/bridge port where `pcie_cap_has_lnkctl()` is false for LNKCAP. The
amdgpu consumers then program `adev->pm.pcie_mlw_mask` and gate
`clk_req_support` from an uninitialized stack word. Requires AMD GPU hardware
(absent on the audit guest); the code-level divergence itself is proven on 7
real devices. The uninitialized bits are masked (`& 0x3F0 >> 4` etc.) before
use and never cross the user boundary → no userspace info leak; the ceiling is
wrong register programming / misbehavior — a local DoS / incorrect-function
class consistent with the filed Low severity.

## Privesc assessment (standing directive)

None. The defect mis-initializes one caller-owned `u32`; there is no OOB
access (the early return skips the config read), no write to kernel memory the
caller did not pass, no disclosure of the raw uninitialized word to userspace.
No primitive → no escalation chain.

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