β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-1200

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1200 Β· 9 files
FileTypeDescriptionSize
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
README.md readme human reproduce doc
↓ download 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 analysis
  • fix.diff β€” git-apply-able patch validating u8 indices against ucNumEntries
  • env.txt β€” guest environment snapshot
  • fix_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.'
VERDICT.md verdict source-level analysis: BIOS-supplied u8 indices used to compute OOB offsets
↓ download raw

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:

  1. radeon is not in X86_64_GENERIC β€” grep radeon sys/config/X86_64_GENERIC returns nothing. Loading it requires kldload radeon which 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.
  2. No AMD/ATI GPU is present, so even with radeon.ko loaded 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).

Validate each index before use:

  • v1: ucNonClockStateIndex and ucClockStateIndices[j] must be < ucNumStates.
  • v2: nonClockInfoIndex must 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_testable

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