# DF-3858 VERDICT — vega20_get_sclk_od/get_mclk_od count-1 underflow + div0

**Status: not_reproduced (kernel reach hardware-gated) — both arithmetic
facts proven on the guest (SIGFPE 3/3; wild +34 GiB index proven in
disassembly), source chain traced end-to-end.**

## Root cause (path:line, sys/dev/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
unless noted)

`vega20_get_sclk_od()` `:1318-1335` (mem twin `:1364-1381`):

- `:1326` `sclk_table->dpm_levels[sclk_table->count - 1].value`
- `:1327-1328` `golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value`
- `:1332` `value = DIV_ROUND_UP(value * 100, golden_value);`

No zero-count and no zero-divisor guard.

### Case A — golden_value == 0 (DF-3707 twin)

1. `vega20_setup_gfxclk_dpm_table()` `:538-547`: when
   `data->smu_features[GNLD_DPM_GFXCLK].enabled` is false,
   `dpm_table->count = 1; dpm_table->dpm_levels[0].value =
   data->vbios_boot_state.gfx_clock / 100;` (`:545-546`).
2. `data->vbios_boot_state.gfx_clock = boot_up_values.ulGfxClk`
   (`vega20_hwmgr.c:745`), where `ulGfxClk = fw_info->bootup_sclk_in10khz`
   (`ppatomfwctrl.c:523` and `:556`) — raw VBIOS u32, **no zero check**
   anywhere on this path.
3. `vega20_setup_default_dpm_tables()` `:713-715` memcpy's the whole table
   into `golden_dpm_table`, so the golden top (and only) value is 0.
4. First `cat pp_sclk_od` after boot ⇒ `DIV_ROUND_UP(0*100, 0)` ⇒ #DE ⇒
   panic. (Values in the *enabled* path are protected:
   `vega20_get_dpm_frequency_by_index()` `:499-501` asserts `*clk != 0` —
   the developer bounded the SMU path but not the VBIOS boot-clock path.)

### Case B — count == 0 wild read (DF-3686 twin)

1. `vega20_setup_default_dpm_tables()` `:588` `memset(&data->dpm_table, 0,
   sizeof(data->dpm_table))`; each sub-table is filled only if its SMU
   feature is enabled and every SMU message succeeds
   (`:592-711`, `PP_ASSERT` at `:594/:541/:562/...` return early).
2. Boot-time failure is torn down: `hwmgr_hw_init` (hwmgr.c:246-256) goes
   `err2` → `backend_fini` (kfree backend) → `pp_hw_init`
   (amd_powerplay.c:118-130) returns the error → the powerplay IP block
   init fails → the device never probes (no sysfs). **The live degraded
   state is resume**: `hwmgr_resume` (hwmgr.c:308-330) calls
   `phm_enable_dynamic_state_management` with **no cleanup on failure**
   (no backend free, pm_en untouched), leaving the driver attached with the
   memset-zero dpm tables whenever SMU re-enable fails after suspend
   (flaky SMU / VF passthrough states).
3. `hwmgr->pm_en` was set at powerplay-create time (`amd_powerplay.c:51`)
   and is *not* cleared on the resume failure; the sysfs nodes exist for the
   lifetime of the device (`amdgpu_device.c:2664`, `amdgpu_pm.c:1019-1022`)
   and `pp_dpm_get_sclk_od` (amd_powerplay.c:757-763) checks only
   `!hwmgr || !pm_en` — so a world-readable `cat pp_sclk_od` still
   dispatches into `vega20_get_sclk_od`.
4. `golden_dpm_table.count == 0` ⇒ `count - 1 == 0xFFFFFFFF` (uint32
   `count`, `vega20_hwmgr.h:156-159`) ⇒ `dpm_levels[0xFFFFFFFF].value`:
   element size 8 ⇒ effective read offset **34,359,738,360 bytes** past
   `dpm_levels[0]` in the kzalloc'd `struct vega20_hwmgr` backend
   (`vega20_hwmgr_backend_init()` `:383`).
5. Wild read outcome: unmapped ⇒ page-fault panic; mapped garbage ⇒
   `value - garbage` then divided by garbage — and if the garbage golden
   value is 0, case A's #DE. (DF-1253 covers the analogous underflow inside
   `vega20_apply_clocks_adjust_rules()` `:3095-3104` with the
   `mclk_latency_table.count` loop; this finding is the distinct
   get_sclk_od/get_mclk_od sink, checklist item (c) "OD table getters,
   get_max_sclk/mclk consumers".)

### Why `vega20_get_profiling_clk_mask`/`force_clock_level` are NOT affected

`:2229-2231` (`count - 1` into a mask) and `:2249/:2276`
(`soft_max_level >= count` reject) were traced: even with count==0 the mask
path shifts by 0xFFFFFFFF (x86 `shl` masks to 31) ⇒ `soft_max_level = 31 ≥
count` ⇒ `-EINVAL`. No OOB.

## What ran on the Guest

`od_div0.c` replicates the exact struct layout (`vega20_hwmgr.h:140-171`)
and the verbatim `get_sclk_od` body.

- Case A (`count=1, value=0`): **SIGFPE, exit=136, 3/3 runs** (`run.log` ×2,
  `run.2.log`), dmesg `pid NNNN (od_div0), uid 0: exited on signal 8`.
- Case B (`count=0`): prints index `4294967295` and effective offset
  `34359738360` bytes; the verbatim access executed (objdump on the guest:
  `mov 0x401ef8(,%rax,8),%ecx` with `rax=0xFFFFFFFF`) and landed on an
  unrelated mapped page in this userland process (returned 0) — in-kernel
  the same address relative to a kmalloc'd backend is a wild read.

## Why not `reproduced`

Audit guest has 0 AMD PCI devices, no `/dev/dri` (`env.txt`); vega20_hwmgr
code cannot execute. Kernel reach needs a Vega20 board in state A (crafted
VBIOS zero boot clock with GFXCLK DPM feature disabled) or state B (failed
DPM bring-up). Impact ceiling: **unprivileged local DoS via panic** +
wild OOB read.

## Fix validation

`fix.diff` makes `vega20_get_sclk_od/get_mclk_od` return 0 when either table
count is 0 or the golden top value is 0 (mirroring the DF-3707 fix), and
zero-guards the VBIOS boot-up clocks in `vega20_init_smc_table` (floor to
existing `smu_features`-independent 10 kHz minimum like other consumers).
git-apply-clean against the read-only `sys/` tree; a patched kernel cannot
be exercised on this guest (no Vega20 ASIC), so fix validation is
**not_testable**.

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