# DF-4544 — vega20 OD count clamps are one-sided: VBIOS-sized allocations vs driver-enum indices

**Severity: Medium (heap OOB read + filtered heap info leak via sysfs) | Confidence: certain**
**File: `sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:823-846`**

## What

`init_powerplay_table_information()` clamps the VBIOS-provided
`OverDrive8Table.ODFeatureCount` / `ODSettingCount` from **above only**
(lines 823-833):

```c
od_feature_count = (le32_to_cpu(...ODFeatureCount) >
		    ATOM_VEGA20_ODFEATURE_COUNT) ? ATOM_VEGA20_ODFEATURE_COUNT : ...;
od_setting_count = (le32_to_cpu(...ODSettingCount) >
		    ATOM_VEGA20_ODSETTING_COUNT) ? ATOM_VEGA20_ODSETTING_COUNT : ...;
```

and then sizes the heap copies by those counts
(`copy_overdrive_feature_capabilities_array` :694-695,
`phm_copy_overdrive_settings_limits_array` smu_helper.c:73-74). A table
that declares `ODSettingCount = 1` therefore gets **4-byte**
`od_settings_max` / `od_settings_min` allocations and a **1-byte**
`od_feature_capabilities` allocation (ODFeatureCount = 1).

But the consumers in `vega20_hwmgr.c` index those arrays with the fixed
driver-side enums, unconditionally, at driver init:

- `vega20_od8_set_feature_capabilities` reads
  `od_settings_max/min[OD8_SETTING_*]` for indices **1..13**
  (vega20_hwmgr.c:906-976; `OD8_SETTING_ID` vega20_hwmgr.h:398-417)
  and `od_feature_capabilities[1..9]` (:913-975)
- the fill loop (vega20_hwmgr.c:1216-1227) copies `od_settings_min/max[i]`
  into `od8_settings_array[i].min/max`
- `vega20_print_clock_levels(OD_RANGE)` (vega20_hwmgr.c:2861-2905)
  **prints those values to the world-readable pp_od_clk_voltage sysfs
  node**.

With `ODSettingCount = 1` the first OOB read is
`od_settings_max[OD8_SETTING_GFXCLK_FMAX = 1]` (vega20_hwmgr.c:907) —
bytes 4..7 past a 4-byte kmalloc object; the farthest consumer index (13)
reads at +52 B. A crafted VBIOS (or malicious vGPU ROM) therefore gets a
**heap out-of-bounds read window of up to ~52 bytes past a small kernel
heap object, with the leaked u32s filtered only by the OD range gates and
then exposed to unprivileged userspace through the OD_RANGE sysfs dump**.
Distinct from the knowns: DF-1689/DF-1690 are OOB reads of the VBIOS blob
itself (no size validation); this finding is about the *driver-side heap
copies* being sized by firmware counts that the clamps only bound from
above.

## Reproduce (lifted-copy harness, DF-1179/DF-4395 convention)

The in-kernel path needs a Vega20 ASIC with a crafted VBIOS (env.txt shows
the guest has only the QEMU std VGA — no AMD GPU), so the harness
replicates the exact struct layouts (vega20_pptable.h, pack(1)), the exact
clamp logic, the kmalloc-sized arrays with a marker-filled "adjacent slab"
tail, and the exact consumer condition sequence.

```
./build.sh     # cc -O2 -Wall (guest cc 8.3)
./run.sh
```

Expected (run.log):
- CONTROL full counts (10/14) → 0 OOB reads, normal OD_RANGE output
- CRAFTED ODFeatureCount=10 / ODSettingCount=1 → `od_settings_max`
  4-byte object: **21 OOB reads, farthest +48 B past**; `od_settings_min`
  16 OOB reads; OD_RANGE prints `SCLK: 800Mhz 2779054081Mhz` —
  2779054081 = 0xA5A50001, the marker planted at the first u32 **past**
  the object → heap leak to sysfs demonstrated
- CRAFTED ODFeatureCount=1 → `od_feature_capabilities` 1-byte object:
  9 OOB reads (indices 1..9)
- PATCHED (fix.diff: allocations floored at the driver counts) → same
  crafted input, **0 OOB reads**, no markers in the output

## Fix validation

- `fix.diff` — `git apply --check` OK; allocations floored at
  ATOM_VEGA20_ODFEATURE_COUNT / ATOM_VEGA20_ODSETTING_COUNT, copies bounded
- full `amdgpu.ko` module rebuilt with the patch applied under the real
  kernel toolchain (**-Werror**): RC=0 (fix_build.log); guest /usr/src
  restored to pristine afterwards
- harness PATCHED control: 0 OOB reads for the same crafted input

## Files
- `od_count_oflow.c` — the harness (sha256
  a55b58d859d8addc4358bea67a58bcba7faff0150d0f38c999bf4a2c3854f1a2)
- `build.sh` / `run.sh`, `build.log` / `run.log` / `run.2.log` / `fix_run.log`
- `fix.diff`, `fix_build.log`, `env.txt`, `VERDICT.md`, `manifest.json`, `verdict.json`
