# DF-4554 VERDICT — not_reproduced on DFly (dead code, proven); latent uninit-heap disclosure on CIK/VI

## Narrative

`amdgpu_debugfs_gpr_read()` allocates `data = kmalloc_array(1024, 4,
GFP_KERNEL)` (amdgpu_debugfs.c:710) — not zeroed — then fills it only if
the selected bank's callback exists:

* bank == 0 (VGPR): `if (adev->gfx.funcs->read_wave_vgprs)` (line 719)
* bank == 1 (SGPR): `if (adev->gfx.funcs->read_wave_sgprs)` (line 722)

but the copy-out loop (lines 729-742) runs **unconditionally**, putting
`size` dwords of `data` to userspace. Callback existence per asic in this
tree:

| asic family | gfx funcs | read_wave_data | read_wave_sgprs | read_wave_vgprs |
|---|---|---|---|---|
| gfx_v7 (CIK) | gfx_v7_0.c:4283 | yes | yes | **NO** |
| gfx_v8 (VI)  | gfx_v8_0.c:5325 | yes | yes | **NO** |
| gfx_v9 (SOC15) | gfx_v9_0.c:1378 | yes | yes | yes |

So on CIK/VI, a bank-0 (VGPR) read of any size returns **uninitialized
kernel heap** — a 4 KB kmalloc's prior contents (heap addresses, freed
object data). This is *independent of DF-1692*: even with DF-1692's
offset-unit confusion fixed and `size` clamped to 4096 with offset 0, the
disclosure persists, because the fill never happened. (DF-1692 covers the
OOB read/write of this same loop via the byte/dword confusion; the
uninitialized-memory primitive is the part reported here.)

Contrast with the sibling `amdgpu_debugfs_wave_read()` (lines 641-648)
which guards with `if (!x) return -EINVAL` when the callback produced
nothing — the gpr handler has no equivalent guard.

## Why not reproduced on this guest — dead code, three independent proofs

1. **Preprocessor**: `sys/dev/drm/kconfig.h` (the compile-with include used
   by every drm file) defines `CONFIG_DRM_*` options but **not**
   `CONFIG_DEBUG_FS`. The whole handler block amdgpu_debugfs.c:70-932 is
   `#if defined(CONFIG_DEBUG_FS)`; the compiled file is just the `#else`
   stubs (amdgpu_debugfs.c:933-942) plus the always-built
   `amdgpu_debugfs_add_files()` (whose inner `#if` at line 62 is also out).
2. **Shim**: `sys/dev/drm/include/linux/debugfs.h` is an include-only
   header (26-32: includes, no declarations); `debugfs_create_file` (used
   at line 832) does not exist anywhere in the DFly shim — enabling the
   `#if` today would break the build, i.e. the port doesn't exist yet.
3. **Runtime** (run.log, guest 2026-09-21): `nm /boot/kernel/amdgpu.ko`
   shows exactly `amdgpu_debugfs_add_files`,
   `amdgpu_debugfs_regs_init/_regs_cleanup/_init` (the `#else` stubs, and
   unrelated files' stubs) and **no** `*_regs_read/_gpr_read/_wave_read/
   _sensor_read/_regs_pcie_*` symbols; `strings` shows no `amdgpu_regs` /
   `amdgpu_gpr` filenames; `/sys/kernel/debug` does not exist; no AMD GPU
   (`pciconf -l | grep 1002` empty).

## Impact if the code ever activates

Debugfs nodes are created `S_IFREG | S_IRUGO` (amdgpu_debugfs.c:833) under
the card's debugfs root (root-only traversal on Linux), so the realistic
actor is root/local-privileged on a CIK/VI system — an info-leak of
uninitialized heap to a reader, useful to defeat KASLR-style hardening on
kernels that have it. Recorded as Low severity latent hardening gap; the
same defect exists in upstream Linux amdgpu of this vintage.

## Fix

Zero the allocation (`kzalloc`) or, better, mirror `wave_read`'s guard:
return `-EINVAL` when the requested bank's callback is absent, and
validate `size`/`offset` against the 1024-dword buffer (the latter is
DF-1692's fix). See fix.diff.

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