Unvalidated BIOS-supplied array indices in power table parsing cause OOB heap reads
Summary
radeon_atombios_parse_power_table_4_5 (:2603-2620) and _6 (:2701-2714): BIOS-supplied u8 indices (ucNonClockStateIndex, ucClockStateIndices, nonClockInfoIndex, clockInfoIndex) used to compute offsets into nonClockInfo/clockInfo arrays WITHOUT validating against ucNumEntries. Index=255 -> read ~15KB-65KB past array into kernel heap. Also ucNumDPMLevels per-entry stride at :2734 unvalidated. Leaked data flows to power_state.sclk/mclk/vddc -> DRM debug/sysfs. Fix: compare each index against array ucNumEntries.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1200 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | source-level analysis: BIOS-supplied u8 indices used to compute OOB offsets | 3.6 KB | β raw |
| fix.diff | suggested-fix | validate each index against ucNumEntries / ucNumStates | 2.8 KB | view raw |
| build.sh | build-script | no-op (no userspace PoC; driver-gated) | 444 B | view raw |
| run.sh | run-script | no-op (no AMD GPU on guest) | 264 B | view raw |
| fix_build.log | build-log | cumulative kernel build with all 5 fixes applied, NK_DONE rc=0 | 5.6 MB | β download |
| env.txt | environment | guest uname, PCI topology, target HW required | 1.2 KB | view raw |
| README.md | readme | human reproduce doc | 956 B | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1200 β radeon unvalidated BIOS power-table indices (build/run scripts)
Hardware+config-gated (AMD/ATI GPU + radeon.ko). radeon is not in GENERIC and
no AMD GPU is present on this guest. See VERDICT.md.
Files
VERDICT.mdβ detailed source-level analysisfix.diffβ git-apply-able patch validating u8 indices against ucNumEntriesenv.txtβ guest environment snapshotfix_build.logβ kernel-build output (cumulative batch)
build.sh
#!/bin/sh
echo 'build.sh: no userspace PoC for DF-1200 (driver-gated).'
echo 'To validate the fix, apply fix.diff and build a kernel + modules:'
echo ' cd /usr/src && patch -p1 < fix.diff && make -j6 nativekernel KERNCONF=X86_64_GENERIC'
run.sh
#!/bin/sh
echo 'run.sh: DF-1200 is hardware-gated (AMD/ATI GPU + radeon.ko).'
echo 'radeon is not in GENERIC and no AMD GPU is present on this guest.'
echo 'See VERDICT.md for the source-level confirmation.'
DF-1200 β radeon unvalidated BIOS-supplied power-table indices
Verdict
NOT REPRODUCED (source-confirmed; hardware+config-gated). The bug is real
in both the v1 (parse_power_table_4_5) and v2 (parse_power_table_6) parsers,
but the radeon driver is not in X86_64_GENERIC and no AMD/ATI GPU is
present on this QEMU/KVM guest (vgapci0 is QEMU std VGA, vendor 0x1234). No
runtime trigger; validated by line-level source trace + single-fix kernel
build.
Mechanism
radeon_atombios_parse_power_table_4_5 (sys/dev/drm/radeon/radeon_atombios.c:2570)
and _6 (radeon_atombios.c:2658) parse the VBIOS PowerPlayInfo table. In
both, BIOS-supplied u8 indices are used as direct multipliers against the
per-entry sizes to compute offsets into the nonClockInfo / clockInfo
arrays without any bounds check:
- v1,
radeon_atombios.c:2606:(power_state->v1.ucNonClockStateIndex * power_info->pplib.ucNonClockSize) - v1,
radeon_atombios.c:2619:(power_state->v1.ucClockStateIndices[j] * power_info->pplib.ucClockInfoSize) - v2,
radeon_atombios.c:2701-2703:non_clock_array_index = power_state->v2.nonClockInfoIndex; ...->nonClockInfo[non_clock_array_index] - v2,
radeon_atombios.c:2712-2714:clock_array_index = power_state->v2.clockInfoIndex[j]; ...->clockInfo[clock_array_index * clock_info_array->ucEntrySize]
With u8 indices up to 255 and per-entry sizes of ~40 bytes
(ATOM_PPLIB_NONCLOCK_INFO) and ~16-24 bytes (clock info), a malformed or
hostile VBIOS image can drive a kernel-pointer dereference 15 KB β 65 KB past
the array into the mapped VBIOS shadow / kernel heap. The data read flows
into rdev->pm.power_state[].clock_info[].sclk/mclk/vddc which is then
exposed via DRM debugfs / sysfs β a kernel-memory info leak, and on platforms
where the OOB read lands on an unmapped page, a kernel panic at attach.
The array bounds ARE available: v2 has explicit _StateArray.ucNumEntries,
_ClockInfoArray.ucNumEntries, _NonClockInfoArray.ucNumEntries
(pptable.h:438-464). For v1 the spec mandates the non-clock array carries
one entry per state, so ucNumStates (pptable.h:153) is the natural bound.
Why not triggered on this guest
Two independent reasons:
radeonis not inX86_64_GENERICβgrep radeon sys/config/X86_64_GENERICreturns nothing. Loading it requireskldload radeonwhich needs root, and the only GPU on the guest is QEMU std VGA (vendor 0x1234, not AMD/ATI), so the radeon PCI attachment table would never claim it.- No AMD/ATI GPU is present, so even with
radeon.koloaded there is no radeon device to attach and parse a VBIOS for.
Option (d). To exercise the path at runtime you would need real AMD hardware (or a sufficiently faithful PCI stub presenting a forged ATOM BIOS image).
Recommended fix (in fix.diff)
Validate each index before use:
- v1:
ucNonClockStateIndexanducClockStateIndices[j]must be< ucNumStates. - v2:
nonClockInfoIndexmust be< non_clock_info_array->ucNumEntries;clockInfoIndex[j]must be< clock_info_array->ucNumEntries.
On violation the state is skipped (continue in v1; goto next_state_v6 in
v2 β the v2 goto is necessary because the outer loop advances
power_state_offset by a per-state stride and continue would skip that
advance).
Build validation
Cumulative kernel build with all 5 fixes applied β NK_DONE rc=0. See
fix_build.log. Note: the radeon module is compiled as part of the DRM
modules (not the base kernel), but nativekernel does build every enabled
module; the build log shows the modified radeon_atombios.c compiled without
errors or warnings.
Reproduce
./build.sh # no-op ./run.sh # no-op
Fix verification
not_testablecompile validated
nativekernel rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. radeon power table u8 indices no bounds -> OOB read. radeon not in GENERIC, no AMD GPU.
No comments yet.