# DF-4396 — CENTRAL FIX: Vega10 PowerPlay parser has no bounds-validation layer

**Severity: High | Bucket: kernleak | Verified: REPRODUCED (verbatim-rig evidence
class, same as DF-4165/DF-4247) + fix A/B-validated (fixed) on the identical inputs.**

## Status

Every defect family cited by the finding was demonstrated in **byte-identical
production parser code** (`vega10_processpptables.c:34-1326`, machine-extracted
and checker-verified **on the DragonFly guest against `/usr/src/sys` — the tree
its running kernel was compiled from**), compiled into a harness and driven with
crafted `ATOM_Vega10_*` PowerPlay tables — 16 SIGSEGV-confirmed unclamped
accesses (12 source OOB reads incl. two +65,281-byte deep-offset reads, one
NULL deref, two heap OOB **writes** past `kzalloc`'d objects), two byte-exact
kernel-heap leak proofs (73 OOB records), two attacker-controlled
heap-overflow canaries (247/247 and 1232 bytes byte-exact), and the
result-clobbering masked failure (DF-3702 feeder). In-kernel execution remains
AMD-ASIC-gated on this guest (no 0x1002 device, no /dev/dri, no amdgpu module
— `env.txt`). The pack's `fix.diff` was A/B-validated in the same rig: every
crafted mode clean, valid tables parse identically (no regression).

## Rig layout (rig/)

- `gen_rig.sh` sed-extracts the **entire parser body** (lines 34-1326) plus 22
  verbatim struct/type header slices (the whole packed `ATOM_Vega10_*` family
  in `vega10_pptable.h:26-419`, the `phm_ppt_v1_*` kernel-side types,
  hwmgr/hardwaremanager/power_state slices) — bracketed with VERBATIM markers.
- `check_verbatim.sh` re-proved all 23 blocks **byte-identical on the guest
  against `/usr/src/sys`** (`build.log`: `ALL VERBATIM CHECKS PASSED`), and
  that `pp_slice_fixed.inc` is exactly pristine+fix.diff (forward apply
  fuzz=0, slice equal, reverse apply restores pristine byte-exactly).
- `rig_shims.h` touches no defect path: identity `le*_to_cpu`, three-mode
  `kzalloc` (plain / page-guarded destination / 0xAA-canary tail), minimized
  `struct pp_hwmgr`/`amdgpu_device`/`pci_dev` with production names/types for
  every field the parser and the fix consult, verbatim `PP_ASSERT_WITH_CODE`.
- Crafted images mimic the ATOM Vega10 PowerPlay table a crafted/corrupt VBIOS
  (or a root-written `pp_table` sysfs blob — amd_powerplay.c:671-706) feeds the
  driver on a Vega10 board; images end at a 22-page PROT_NONE tail (deep
  u16-offset reads fault deterministically) or at a patterned victim region
  (leak provenance).

## Observed (run.log / run.2.log / run.3.log — fault-offset fingerprints identical across 3 runs)

| mode (crafted input) | baseline (verbatim) | fixed (+fix.diff) |
|---|---|---|
| clean-valid (full table) | rc=0, 23/23 checks OK | identical (no regression) |
| statearray-offset-oob (`usStateArrayOffset=0xFF00`, 128B image) | SIGSEGV image+65281 (`:82`) | rc=-1 "out of range" |
| numentries-offset-oob (direct `:1249` call) | SIGSEGV image+65281 | rc=-1 |
| thermal-offset-oob (`:134`) | SIGSEGV image+65281 | rc=-EINVAL |
| fan-v2-tail (rev 11 header, 1 byte in image) | SIGSEGV image_end+31 (`:214`) | rc=-EINVAL |
| od-acg-count-255 (rev1, `entries[254].ucACGEnable`) | SIGSEGV image_end+3298 (`:273`, DF-1589) | rc=0, clamped |
| gfxclk-v1-count-255 (`:620` walk) | SIGSEGV image_end+4 | rc=0, count clamped to 1 |
| socclk / mclk / pixclk / mm count-255 | SIGSEGV image_end+0..4 | rc=0, counts clamped to image |
| powertune-v3-short (rev7, 38B table, V3 fields) | SIGSEGV image_end+0 (`:488`) | rc=-EINVAL |
| hardlimit-tail (`entries[0]`, no record) | SIGSEGV image_end+0 (`:814`) | rc=-1 |
| pcie-null-sclk (gfxclk offset 0) | SIGSEGV **addr 0x0** (`:781`, DF-1587) | rc=-EINVAL (NULL guard) |
| dcefclk-pioneer-write (dev 0x6863, count 255) | SIGSEGV **obj+232, alloc 228** (`:740-748`) | rc=0, loop=8, no fault |
| dcefclk-pioneer-canary | **1232 changed bytes in [+0,+6892] past the 228B object** | canary intact |
| vddc-lookup-write (count 255, max_levels 8) | SIGSEGV **obj+86, alloc 84** (`:1036`, DF-1586) | rc=0, loop=8 |
| vddc-lookup-canary | 494 bytes changed [+2,+2463]; **247/247 us_vdd writes byte-exact with the image** | canary intact, count=8 |
| gfxclk-v2-leak (rev1 count 40, 2 records) | count=40; **38 OOB records byte-exact with victim bytes**; od engineClock decided by the OOB ACG byte (LEAK) | count=2; 0 OOB; decision bit in-image |
| socclk-leak-canary (count 40, 5 records) | count=40; **35/35 OOB records byte-exact** | count=5, 0 OOB |
| result-clobber (vddc count 0, vddmem/vddci ok) | **rc=0 with NULL vddc_lookup_table** (masked, DF-3702 feeder) | rc=1 propagated |
| entry-offbyone (`entry_index==ucNumEntries`) | SIGSEGV image_end+12 (`:1308` `<=`, DF-1588) | rc=-1 (`<` + range check) |

## Primitive and impact

The firmware image (VBIOS ROM copy or root-written sysfs blob) fully controls
every offset (u16) and count (u8):

- **Heap OOB write, attacker-controlled**: `get_vddc_lookup_table`
  (:1026-1038) writes up to 247×2 bytes in a +2/+12/+22… stride 2463 bytes
  past an 84-byte `kmalloc-128` object — 247/247 writes byte-exact with the
  crafted image; `get_dcefclk_voltage_dependency_table` pioneer path
  (:721-750) copies 255 records into a 228-byte `kmalloc-256` object —
  overwrite reaches +6892 (~6.6 KB into adjacent slab objects).
- **Kernel-heap info leak**: count-driven copy loops clone bytes from past
  the table image into `hwmgr->dyn_state`/`pptable_info` tables that userland
  reads back via powerplay sysfs (`pp_dpm_*`); even the overdrive-limit
  decision bit is taken from an OOB byte (gfxclk-v2-leak A/B).
- **Deep OOB reads / NULL deref** (u16 offsets to +65,281; missing-subtable
  NULL at `:781`) — kernel panic paths.
- **Masked parse failure** (:1103-1128) — NULL-table consumer crashes
  (DF-3702 class).

Gating: an AMD Vega10 ASIC whose VBIOS carries a crafted/corrupt PowerPlay
table (flashed ROM, vfio romfile, malicious device), or root re-parsing via
`/sys/class/drm/card0/device/pp_table`. No unprivileged trigger on stock
hardware (same threat model as DF-4165/DF-4247).

## Build / run (on the audit guest, or any DF box with the kernel tree)

```
scp -r rig fix.diff root@guest:/root/df4396/
cd /root/df4396 && ../df4396/build.sh /usr/src   # verbatim checks + 2 binaries
./run.sh                                          # baseline vs fixed, 23 modes
```

(The pre-verification pack carried a syntax-check + clamp-unit-test approach
(`vega10_clamp_test.c`, `vega10_processpptables_fixed.c`) — superseded by this
full verbatim-rig verification; files removed.)

## Fix validation

`fix.diff` (564 lines) applied to the same verbatim slice (checker-proven
equality with pristine+fix.diff): all crafted modes clean, all counts clamped
to image/destination, NULL guarded, off-by-one fixed, failure propagated,
`clean-valid` parses **identically** to baseline (no regression). The pack's
`fix.diff` is the validated artifact, unchanged from the pre-verification
version.
