# DF-4388 — V1 PowerPlay parser has no central bounds-validation layer (central fix for the powerplay count family, V1 side of DF-4247)

**Status: REPRODUCED (verbatim-rig evidence class, DF-4165/4247/4396). Fix
A/B-validated in the same rig: fixed.** See `VERDICT.md` for the full story.

`sys/dev/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c` parses the
Tonga-format VBIOS PowerPlay table for **Tonga, Fiji, Polaris10/11/12,
VegaM** (`hwmgr.c:86` default `PP_TABLE_V1`; `smu7_hwmgr.c:5144` binds
`pptable_v1_0_funcs`). Every firmware USHORT sub-table offset, UCHAR entry
count and UCHAR index is consumed **with zero validation against
`hwmgr->soft_pp_table_size`** and (for counts) zero clamping to destination
array capacities.

## How it was verified (no AMD GPU needed — proven absent, `hw_probe.log`)

The **entire production parser** (`process_pptables_v1_0.c:34-1337` — every
helper, every parse function, the exported entry getters, the
`pptable_v1_0_funcs` vtable) was compiled into a guest-side harness:

- `rig/gen_rig.sh` machine-extracts the parser slice plus 24 header slices
  (pptable_v1_0.h firmware structs, hwmgr/hwmgr_ppt/hardwaremanager/
  power_state/kgd/atombios type blocks, the PP_ASSERT_WITH_CODE and
  GET_FLEXIBLE_ARRAY_MEMBER_ADDR macros).
- `rig/check_verbatim.sh` re-verified all 24 blocks **byte-identical on the
  guest against `/usr/src/sys` — the tree its running kernel was compiled
  from** (`build.log`: `ALL VERBATIM CHECKS PASSED`; guest↔host MD5s of all
  ten sliced sources in `env.txt`).
- The fixed build is the same slice **+ exactly this pack's `fix.diff`**
  (applied to a copy of the pristine full file with `patch --fuzz=0`;
  proven bidirectionally: forward apply succeeds, reverse apply restores
  the pristine file byte-exactly).
- Shims (`rig/rig_shims.h`) touch no defect path: identity
  `le16_to_cpu`/`le32_to_cpu`, **guard-backed end-anchored `kzalloc`**
  (the first byte past any allocation is a PROT_NONE page — this is what
  turns the decisive *destination heap OOB WRITE* into a fault at a
  computable offset), a minimized `struct pp_hwmgr` with production
  names/types for every field the parser and the fix consult.
- Crafted tables mimic real Tonga-format PowerPlay tables; the image ends
  at a guard page (OOB source reads fault at a computable offset), or at a
  patterned victim region (leak mode), or starts after a PROT_NONE page
  (negative-index underflow mode).

## Observed (`run.log`; identical in `run.2.log` / `run.3.log`, md5-equal)

**Baseline — 13 SIGSEGVs + 1 byte-exact leak + 2 behavioral proofs, every
one at the exact predicted verbatim site:**

| mode | observation | verbatim site |
|---|---|---|
| vddc-lookup-255-write | **DEST heap OOB WRITE**: first OOB byte at kzalloc(164)+0; loop bound ucNumEntries=255 vs 16-record allocation; source records all in-image | `get_vddc_lookup_table` :176-190 |
| statearray-offset-oob | source OOB read at image+65281 (0xFF00) | :1042-1053 |
| thermal-offset-oob | image+65281 | :891-897 |
| fan-offset-oob | image+65280 | :922-929 |
| sclkdep-offset-oob | image+65280 | :781-783,420 |
| ppm-offset-oob | image+65281 | :301-305,214-215 |
| vce-offset-oob | image+65281 | :1219-1222 |
| sclk-count-255 | image+131 (first byte past a 131B image) | :438-451 |
| **leak-canary** | **175/175 field values byte-exact** with victim bytes past the image (mm count=40 vs 5 records) | :710-724 |
| vce-sclk-index-255 | image+2914 (`entries[0xFF]` = sclk_table+2+255*11) | :1249-1252,1261 |
| vce-mclk-underflow | **image−2** (`entries[ucNumEntries-1]` = `entries[-1]` with count 0) | :1263-1267 |
| entry-offbyone | image+178 (`<=` guard accepts entry_index==ucNumEntries) | :1310-1321 |
| pcie-null-vdddepsclk | **NULL DEREF** at NULL+0 (`vdd_dep_on_sclk->count`, sclk offset 0) | :522 |
| gpio-null-vdddepsclk | **NULL DEREF** at NULL+0 | :745 |
| gpio-gate-zero-offset | GPIO "parsed" from the PowerPlay header itself when usGPIOTableOffset==0 (gate tests the never-NULL pointer) | :790-792,843-845 |
| vce-count-unclamped | `hwmgr->num_vce_state_tables` stored = 200 (array holds 6) | :1328 |

**Regression control (clean-valid):** a fully valid table parses identically
in both builds (all counts, limits, fan params, 2 states, 2 VCE states,
rc=0) — the fix changes nothing on valid firmware.

**Fixed build, identical inputs:** 0 SIGSEGVs. Lookup count clamped 255→16,
dep counts clamped to the records that fit (255→3, 40→5), oversized offsets
rejected (rc=-1/-22), VCE indices clamped to 0, `entries[-1]` gone, entry
`<` guard rejects one-past (rc=-1), NULL guards on PCIE/GPIO (rc=-22),
GPIO gate moved to the offset (skipped), VCE count clamped 200→6.

## Build / run / verify (on the audit guest)

```
cd findings/poc/DF-4388
./build.sh /usr/src     # verbatim check vs /usr/src + rig_base + rig_fixed
./run.sh                # baseline (verbatim) vs fixed A/B over 18 modes
```

Layout mode prints the exact verbatim struct geometry (PP table 77B, Tonga
state 20B, SCLK rec 11B, MM rec 23B, lookup rec 8B → `get_vddc_lookup_table`
allocates 4+10*16=164 bytes for up to 255 firmware entries).

Historical artifacts from the pre-verification run (compile-validation and
clamp unit test): `syntax_check_guest.txt`, `unit_test_guest.log`,
`fix_build.log`, `fix_run.log`, `v1_clamp_test.c`,
`process_pptables_v1_0_fixed.c` (byte-equal to pristine+fix.diff, proven in
`build.log`).
