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

Missing bounds checks on BIOS-supplied array indices in si_parse_power_table (nonClockInfoIndex and VCE clk_idx)

Summary

si_parse_power_table at si_dpm.c:7253-7255: nonClockInfoIndex read from BIOS used unvalidated to index non_clock_info_array->nonClockInfo[] with NO check against ucNumEntries. Clock loop below DOES check (:7269). Same at :7287-7290 VCE clk_idx no bounds check. Plus power_state_offset advanced by ucNumDPMLevels (:7281) no range check. Crafted/malformed VBIOS -> OOB read from BIOS mapping -> info leak via sysfs pp_dpm_sclk or panic. Attacker: VFIO GPU passthrough romfile or corrupt EEPROM. Fix: validate all BIOS indices against ucNumEntries.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1127 Β· 11 files
FileTypeDescriptionSize
harness.c trigger-source BIOS mapping replica; demonstrates OOB at nonClockInfoIndex=42 3.3 KB view raw
fix.diff suggested-fix bounds check on nonClockInfoIndex in BOTH amdgpu and radeon si_dpm.c 2.0 KB view raw
build.sh build-script cc -O2 -o harness harness.c 95 B view raw
run.sh run-script timeout 10 ./harness 67 B view raw
build.log build-log final successful build 13 B view raw
run.log run-log decisive run incl OOB-read marker 479 B view raw
env.txt environment uname, cc version, pciconf (QEMU VGA stub, not AMD GPU) 403 B view raw
VERDICT.md verdict full narrative + finding-path correction (amdgpu dead, radeon live) 3.7 KB ↓ raw
README.md readme finding summary + build/run/expected 1.8 KB ↓ 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 finding summary + build/run/expected
↓ download raw

DF-1127 β€” Missing bounds checks on BIOS-supplied array indices in si_parse_power_table (AMD GPU)

Finding

si_parse_power_table at sys/dev/drm/amd/amdgpu/si_dpm.c:7253-7255: nonClockInfoIndex is read from BIOS and used unvalidated to index non_clock_info_array->nonClockInfo[] with no check against ucNumEntries. The sibling clock loop at :7269 DOES check if (clock_array_index >= clock_info_array->ucNumEntries) continue; β€” proving the omission is an oversight. Same issue for VCE clk_idx at :7288-7290. Crafted/malformed VBIOS β†’ OOB read from BIOS mapping β†’ info leak via sysfs pp_dpm_sclk or panic.

IMPORTANT correction to the finding: the cited file sys/dev/drm/amd/amdgpu/si_dpm.c is NOT compiled (absent from the amdgpu Makefile SRCS). The same bug exists in the live copy sys/dev/drm/radeon/si_dpm.c:6848-6850 which IS compiled into radeon.ko (confirmed via nm /boot/kernel/radeon.ko β†’ si_dpm_init). The fix.diff patches BOTH copies.

Reachability on this guest

NOT reachable. The VGA device is a QEMU stub (chip=0x11111234, not an AMD GPU). radeon.ko is loadable but never attaches to this hardware. Latent.

A userspace harness demonstrates the OOB read at a bogus BIOS index.

Build / Run / Expected

cc -O2 -o harness harness.c     # build.sh
./harness                        # run.sh
# Expected: "READ PAST MAPPING BOUNDARY -> kernel OOB read" at index 42

Files

  • harness.c β€” simulates a BIOS mapping with a valid 4-entry array and demonstrates OOB at nonClockInfoIndex = 42.
  • fix.diff β€” adds the bounds check to BOTH amdgpu/si_dpm.c (as cited) and radeon/si_dpm.c (the compiled copy). Validated: radeon.ko builds with rc=0.
  • build.log / run.log / env.txt β€” captured outputs.
VERDICT.md verdict full narrative + finding-path correction (amdgpu dead, radeon live)
↓ download raw

VERDICT β€” DF-1127

Verdict: REPRODUCED (primitive) / NOT REACHABLE on guest (HW-gated)

The cited bug is real and confirmed by source trace + userspace demonstration. Important correction: the finding cited sys/dev/drm/amd/amdgpu/si_dpm.c which is dead code on DragonFly (absent from the amdgpu Makefile SRCS). The same bug exists in the compiled copy sys/dev/drm/radeon/si_dpm.c:6848-6850 which IS shipped in radeon.ko. Both are patched in fix.diff.

Mechanism (confirmed path:line)

  1. si_parse_power_table (amdgpu copy si_dpm.c:7210, radeon copy si_dpm.c:6808) parses the PowerPlayInfo table from VBIOS.
  2. amdgpu :7253-7255 / radeon :6848-6850: c non_clock_array_index = power_state->v2.nonClockInfoIndex; non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) &non_clock_info_array->nonClockInfo[non_clock_array_index]; nonClockInfoIndex is read from the (untrusted) VBIOS with no check against ucNumEntries.
  3. The sibling clock loop at amdgpu :7267-7275 / radeon :6864-6872 does check if (clock_array_index >= clock_info_array->ucNumEntries) continue; β€” proving the omission is an oversight, not policy.
  4. Same issue at amdgpu :7288-7290 / radeon :6885 for VCE clk_idx.
  5. Crafted/malformed VBIOS β†’ nonClockInfo[huge_index] β†’ OOB read from BIOS mapping β†’ info leak via sysfs pp_dpm_sclk or panic.

Reproduction (userspace harness)

The harness builds a simulated BIOS mapping with a valid 4-entry nonClockInfo array, then indexes at nonClockInfoIndex = 42:

[buggy path] si_dpm.c:7253-7255 -- NO bounds check
  nonClockInfoIndex = 42 (ucNumEntries = 4)
  accesses bios_mapping[674] (mapping size = 256)
  -> READ PAST MAPPING BOUNDARY -> kernel OOB read

Impact ceiling

  • Per-trigger: kernel OOB read from BIOS mapping. The read data feeds si_parse_pplib_non_clock_info which populates dpm.ps[i] β€” some fields are exposed via sysfs (pp_dpm_sclk) β†’ info leak. A sufficiently out-of-range index may fault β†’ panic.
  • Privilege: requires AMD GPU (Tahiti/Southern Islands) + crafted VBIOS (VFIO GPU passthrough romfile, corrupt EEPROM). Not local unpriv.
  • Realistic: VFIO passthrough with attacker-controlled romfile is the realistic vector. Niche but real in cloud/VFIO scenarios.

Fix

fix.diff adds the bounds check to both copies:

if (non_clock_array_index >= non_clock_info_array->ucNumEntries) {
    power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
    continue;
}

β€” mirroring the existing clock-loop check. The power_state_offset advance is duplicated so the continue correctly skips to the next state.

Validated: radeon.ko builds with RC=0 after applying the fix (this is the module that actually ships the bug).

Fix validation

  • Patch applies cleanly to both files: amdgpu Hunk #1 succeeded at 7251, radeon Hunk #1 succeeded at 6846.
  • make in sys/dev/drm/radeon/ β†’ radeon.ko linked (2.0 MB), rc=0.
  • amdgpu.ko also builds with rc=0 (though it doesn't compile si_dpm.c).
  • Cannot boot-test (no AMD GPU; VGA is a QEMU stub chip=0x11111234); fix_status: not_testable.

Correction to the finding

The finding cites sys/dev/drm/amd/amdgpu/si_dpm.c β€” this file is NOT compiled into any shipped module (not in amdgpu's Makefile SRCS). The live copy with identical code and the identical bug is sys/dev/drm/radeon/si_dpm.c, confirmed compiled via nm /boot/kernel/radeon.ko | grep si_dpm_init. The fix.diff patches both; the radeon copy is the one that matters for shipped security.

PoC changes

  • harness.c written from scratch. Simulates a BIOS mapping and demonstrates the OOB read at a bogus index, with a side-by-side comparison to the validated clock loop.

Fix verification

not_testable

compile validated

module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source+harness. si_parse_power_table nonClockInfoIndex no bounds check. Finding cited wrong file (amdgpu vs radeon). No AMD GPU.