# DF-4248 — V1-branch state-array off-by-one (`entry_index > ucNumStates`)

## Where
`sys/dev/drm/amd/powerplay/hwmgr/processpptables.c:937` (V1 branch of
`pp_tables_get_entry`, tables with `ucTableFormatRevision < 6`), twin of the
known V2 off-by-one covered by DF-1472 at :913 (this site was *not* in
DF-1472's listed scope).

## What
```c
if (entry_index > powerplay_table->ucNumStates)   /* off by one: == passes */
        return -1;
pstate_entry = (ATOM_PPLIB_STATE *)...usStateArrayOffset +
                entry_index * powerplay_table->ucStateEntrySize;
```
`entry_index == ucNumStates` walks one `ATOM_PPLIB_STATE` past the array and
then iterates `ucStateEntrySize-1` clock indices read from out-of-bounds
bytes, feeding garbage `ucClockStateIndices[]` to the callback. Reads only
(sink indices are re-derived), so impact = OOB read of the VBIOS heap image.

## Trigger
Same as the VBIOS/pp_table family: crafted PowerPlay table (format rev < 6)
on smu7/smu8-class hardware, or root pp_table sysfs swap racing a state
enumeration between `pp_tables_get_num_of_entries` and `pp_tables_get_entry`
(the two calls are separately locked in amd_powerplay.c, so a table swap in
between makes `entry_index == ucNumStates` reachable).

## Fix
Included in DF-4247's central `fix.diff` (`>` -> `>=`); minimal hunk:

```diff
-		if (entry_index > powerplay_table->ucNumStates)
+		if (entry_index >= powerplay_table->ucNumStates)
 			return -1;
```

## Build/run
Hardware-gated (needs an AMD pre-Vega ASIC attached); see env.txt.
`./run.sh` documents the absence of AMD hardware on the audit guest.
