# VERDICT — DF-4951

## Reproduced: YES (lifted-copy harness on the audit guest; in-kernel path hardware-gated — no Vega12 ASIC)

## Root cause (proven, path:line)

- **vega12_processpptables.c:296-301** — `vega12_pp_tables_uninitialize()`
  takes `pp_table_info = hwmgr->pptable` and dereferences it at the very
  first statement (`kfree(pp_table_info->power_saving_clock_max)`) with no
  NULL guard.
- **hwmgr.c:222-231 + 257-262** — `hwmgr_hw_init()` calls
  `pptable_init` (:222) then `backend_init` (:229); on backend/power-state/
  asic/DSM failure it jumps to `err1:`/`err2:` which **already call
  `pptable_fini()`** before returning the error. After such a failure
  `hwmgr->pptable == NULL`.
- **hwmgr.c:216-219** — `pm_en` is *not* cleared on these error paths (only
  when pptable/hwmgr funcs are missing); **amdgpu_pm.c / amd_powerplay.c**
  never clear `pm.dpm_enabled` on the error path either.
- **amd_powerplay.c:676-707** — `pp_dpm_set_pp_table` (sysfs `pp_table`
  write, root-only) gates only on `hwmgr && hwmgr->pm_en` (:680-681) and
  calls `amd_powerplay_reset` (:697) → `hwmgr_hw_fini` (:665) →
  `pptable_fini` (hwmgr.c:280-281) → **NULL deref** at
  `phm_ppt_v3_information+16` (hwmgr.h:579-594 layout, x86-64).
- The pp IP block's `status.hw` (amdgpu_device.c:1572) was set at boot and
  is not cleared by `amd_powerplay_reset`, so driver teardown after a
  failed reset re-enters the same path.

## Harness result (run.log)

- CASE 1 BOOT-OK: healthy init + healthy pp_table reset → clean.
- CASE 2 BUG: boot OK → root pp_table write#1 whose re-init fails at
  backend_init (err1 → fini inside init, `hwmgr->pptable = NULL`) →
  root pp_table write#2 (or teardown) → `hwmgr_hw_fini` →
  `vega12_pp_tables_uninitialize` → **SIGSEGV at fault address 0x10**
  (`power_saving_clock_max` = pptable+16) — the exact kernel-equivalent
  fault (`kfree(NULL->power_saving_clock_max)`).
- CASE 3 CONTROL: write#1 failing at the header check takes the `err:`
  path (hwmgr.c:223-224) which does NOT fini — write#2 then frees the
  leaked (non-NULL) pptable cleanly. This isolates the crash to the
  err1/err2 failure mode and simultaneously evidences the secondary
  defect: **hwmgr->pptable (kzalloc'd at vega12:272) leaks on every
  pptable_init failure** (the `err:` path never finis); a root loop of
  pp_table writes with a header-invalid table grows kmem unboundedly
  (~small alloc per iteration). Not separately verified as a finding —
  noted here and folded conceptually into the fix discussion.
- PATCHED (fix.diff NULL guard, `-DPATCHED`): CASE 2/4 survive, no fault
  (fix_run.log).

## Impact

Kernel NULL-deref panic (CWE-476), fixed fault offset (+16 read then kfree
of garbage-free NULL — no data control). Precondition: one failed powerplay
re-init (SMU/backend hiccup after a pp_table upload or during runtime
re-init) plus a second root-triggered fini (pp_table write, rmmod/teardown,
GPU reset). Root-or-failing-hardware gated → **Low**, consistent with the
DF-4546 convention for pre-conditioned panics. No unprivileged path: the
sysfs node is 0644 root-writable, and the failing re-init itself requires
either root's table or an SMU failure.

## Fix validation

- `git apply --check` OK on the read-only host tree and on guest `/usr/src`.
- Full `amdgpu.ko` rebuild with fix.diff applied, real kernel toolchain,
  **-Werror**, `-j8`: **RC=0**, 0 diagnostics, 24 s
  (fix_build.log; vega12_processpptables.o at :346-347).
- Harness PATCHED control: the previously-fatal CASE 2 sequence survives.
- Guest `/usr/src` restored (`git apply -R`), md5-verified against the
  pristine tree (085571ce1e8f9c2fafdd71e4e3b2cc95), module objdir cleaned.

## Recommended fix

`fix.diff`: early-return NULL guard in `vega12_pp_tables_uninitialize`
(11 added lines, no behavior change for the healthy path). Longer term,
hwmgr.c's `err:` path should also fini (or init's early returns should
free) to close the leak — out of this file's scope, noted for the
maintainer.

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