# DF-3650 — verdict narrative

## Finding
OOB read in ci_parse_power_table's VCE state fill: clk_idx (ucClockInfoIndex & 0x3f) indexes clockInfo[] without the ucNumEntries bound used everywhere else

## Kernel references
- `sys/dev/drm/radeon/ci_dpm.c:5637`
- `sys/dev/drm/radeon/ci_dpm.c:5654`
- `sys/dev/drm/radeon/ci_dpm.c:5656`
- `sys/dev/drm/radeon/ci_dpm.c:5657`
- `sys/dev/drm/radeon/r600_dpm.c:1127`

## Was it reproduced?
No — not on the kernel path. The audit guest is virtio-only (0 AMD PCI devices, /dev/dri absent, no radeon/drm kld; env.txt), so `sys/dev/drm/radeon/ci_dpm.c` can never execute there. Per the pass-2 instruction for this hardware-gated file, the verdict is **inconclusive** (source-level certainty certain) rather than reproduced.

## Logic-level proof (guest harness)
harness.c models the exact loop/index/division with the exact kernel types and was compiled and executed on the audit guest (`cc -O2 -Wall`, DragonFly 6.5-DEVELOPMENT). Decisive output, stable across 2 runs (run.log, run.2.log):

    clk_idx=63 * entrySize=255 = 16065 bytes past clockInfo[0] (ucNumEntries=1)


## Root cause and reasoning

Source-certain OOB read, CI-specific (the si twin has no VCE DPM states and the amdgpu audit's DF-3619..3625 do not cover it). After the per-state loop, ci_parse_power_table fills rdev->pm.dpm.vce_states[]: clock_array_index = vce_states[i].clk_idx (ci_dpm.c:5656) and clock_info = &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize] (5657-5658). The main loop above bounds the very same index against clock_info_array->ucNumEntries (5637-5638); the VCE loop does not. clk_idx originates from the VBIOS VCE state table masked to 6 bits (r600_dpm.c:1127, ucClockInfoIndex & 0x3f, so 0..63) and ucEntrySize is a raw u8 (up to 255), giving a read up to ~16KB past clockInfo[] inside (or beyond) the heap-allocated ATOM BIOS image copy - harness section [E]: 63*255 = 16065 bytes. Within the allocation the leaked bytes flow into vce_states[].sclk/mclk (power-management only, no userspace exposure found); past its end it is a wild read -> panic. Reachable whenever the VBIOS carries an extended header with a VCE table whose state records reference out-of-range clock info indices. Kernel path unreachable on this GPU-less guest; inconclusive overall.

## Exploitability

Crafted VBIOS with an extended header + VCE state table whose ucClockInfoIndex values point past the (small) clock info array -> up-to-16KB OOB read from the kernel heap bios image during ci_dpm_init; values only feed power management, so realistic impact is a wild-read panic (DoS), info-pollution at best.

## Recommended fix
Apply the same clock_array_index >= clock_info_array->ucNumEntries bound (as at ci_dpm.c:5637) to the VCE state fill loop.
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.
