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

uint32 underflow in vega20_apply_clocks_adjust_rules: count-1 wraps to UINT32_MAX causing ~4B-iteration heap OOB read

Summary

vega20_apply_clocks_adjust_rules at vega20_hwmgr.c:3095-3104: if(disable_mclk_switching) { for(i=0;i<data->mclk_latency_table.count-1;i++) {...} }. count is uint32, kzalloc default 0. count-1 wraps to UINT32_MAX -> entries[i]/dpm_levels[i] read OOB past [16] arrays. disable_mclk_switching=true on multi-monitor out-of-sync. Triggered on display config change before vega20_get_memclocks runs. Sibling of DF-1179 (vega10). Fix: check count>0 before loop, guard dpm_table->count==0.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1253 Β· 8 files
FileTypeDescriptionSize
VERDICT.md verdict source trace, underflow mechanism, impact ceiling, fix rationale 3.1 KB ↓ raw
fix.diff suggested-fix guard count==0 + rewrite loop bound as i+1<count with MAX_REGULAR_DPM_NUMBER cap 1.4 KB view raw
fix_build.log build-log vega20_hwmgr.o compiled with fix, RC=0, no warnings 3.9 KB view raw
build.sh build-log repro: apply-check + note 382 B view raw
run.sh run-log no runtime trigger (no HW) 367 B view raw
env.txt environment uname, cc version, module state 359 B view 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
VERDICT.md verdict source trace, underflow mechanism, impact ceiling, fix rationale
↓ download raw

DF-1253 β€” uint32 underflow in vega20_apply_clocks_adjust_rules (count-1 wraps to UINT32_MAX)

Verdict

SOURCE-CONFIRMED (real bug), INCONCLUSIVE at runtime β€” the amdgpu.ko module is not loaded (no AMD Vega20 GPU in the QEMU guest) and is not in the GENERIC kernel, so the underflow path is dormant. Fix authored and compile-validated.

Mechanism (source trace)

vega20_apply_clocks_adjust_rules() iterates over the MCLK latency table using a uint32_t count - 1 loop bound with no zero-guard:

  • sys/dev/drm/amd/powerplay/hwmgr/vega20_hwmgr.c:3095-3104: if (disable_mclk_switching) { dpm_table->dpm_state.hard_min_level = dpm_table->dpm_levels[dpm_table->count - 1].value; for (i = 0; i < data->mclk_latency_table.count - 1; i++) { /* <- UNDERFLOW */ if (data->mclk_latency_table.entries[i].latency <= latency) { if (dpm_table->dpm_levels[i].value >= ...) { ... } } } }
  • data->mclk_latency_table.count is uint32_t (vega20_hwmgr.h:267), default 0 after kzalloc. It is set non-zero only by vega20_get_memclocks() (vega20_hwmgr.c:2435). If vega20_apply_clocks_adjust_rules runs before vega20_get_memclocks populates the table, count - 1 wraps to 0xFFFFFFFF β‡’ the loop iterates ~4 billion times, reading entries[i] and dpm_levels[i] OOB past their MAX_REGULAR_DPM_NUMBER=16 arrays (vega20_hwmgr.h:159,268).
  • disable_mclk_switching is true when 1 < num_display && !multi_monitor_in_sync (multi-monitor out of sync) or vblank_too_short (vega20_hwmgr.c:3037-3039) β€” a plausible state on a multi-monitor Vega20 system when display config changes.
  • The same count - 1 OOB pattern also appears at lines 3045/3047/3069/3071/3096/3108 for dpm_table->count, though those are guarded by vega20_setup_default_dpm_tables setting count >= 1 (vega20_hwmgr.c:598).

Why not reproduced at runtime

  • amdgpu.ko is not loaded on the guest and is not in X86_64_GENERIC.
  • Requires an AMD Vega20 GPU (Radeon VII / Vega 20); QEMU guest has no AMD GPU.
  • On real hardware, an unprivileged user changing display config (hotplug, mode set) could influence num_display/multi_monitor_in_sync and trigger the underflow if the power-management state machine calls apply_clocks_adjust_rules before get_memclocks.

Fix (fix.diff, compile-validated)

  1. Guard dpm_table->count == 0 before the count - 1 accesses in the disable_mclk_switching and nb_pstate_switch_disable blocks (skip via a goto skip / early return).
  2. Rewrite the latency loop bound as i + 1 < data->mclk_latency_table.count (no underflow when count is 0) and add a hard i < MAX_REGULAR_DPM_NUMBER cap as defense-in-depth.

The patched vega20_hwmgr.c compiles cleanly with gcc 8.3, -Werror, no warnings.

Realistic impact ceiling

Kernel heap OOB read (entries[]/dpm_levels[] past [16]) of up to ~4B iterations on a Vega20 system when MCLK switching is disabled before the latency table is populated β€” a DoS (hang/panic from OOB read or page fault) and potential info leak. On this guest: not reachable (no AMD GPU).

Fix verification

not_testable

compile validated

module/object build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. vega20 count-1 uint32 underflow -> 4B OOB loop. amdgpu not in GENERIC, no Vega20 GPU.