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

OOB stack read in bw_calcs_data_update_from_pplib when powerplay returns zero clock levels (sibling of DF-1549)

Summary

bw_calcs_data_update_from_pplib() indexes clocks_in_khz/eng_clks.data/ mem_clks.data at [num_levels-1] without first checking that num_levels > 0.

The powerplay providers (dm_pp_get_clock_levels_by_type at amdgpu_dm_pp_smu.c:296 and dm_pp_get_clock_levels_by_type_with_latency at amdgpu_dm_pp_smu.c:368) unconditionally return success and leave num_levels==0 when adev->powerplay.pp_funcs is NULL or its get_clock_by_type[_with_latency] hook is missing or returns 0 levels.

Because num_levels is uint32_t, 0-1 wraps to 0xFFFFFFFF and the dereference faults ~16 GB past the stack object β†’ deterministic kernel panic at driver init.

The same bug class was filed as DF-1549 against dce110_resource.c; the sibling dce120_resource.c was patched upstream with explicit num_levels==0 guards at lines 789 and 822, but dce112_resource.c was left unfixed.

Root cause

Four unguarded sinks, all reached only from construct() at dce112_resource.c:1334, which runs once at GPU/driver init via dce112_create_resource_pool() (line 1353) β†’ dc_create_resource_pool() (dc_resource.c:135) for DCE_VERSION_11_2 / 11_22 (Polaris10/11/12):

  • dce112_resource.c:1004-1005 β€” fallback ENGINE_CLK path (taken when dm_pp_get_clock_levels_by_type_with_latency returned false at line 993). dm_pp_get_clock_levels_by_type always returns true; when pp_funcs or pp_funcs->get_clock_by_type is NULL, num_levels=0, and clks.clocks_in_khz[clks.num_levels-1] = clks.clocks_in_khz[(uint32_t)0-1] is a wild read.

  • dce112_resource.c:1032-1034 β€” fallback MEMORY_CLK path. Same clks.clocks_in_khz[clks.num_levels-1] pattern.

  • dce112_resource.c:1040-1041 β€” primary ENGINE_CLK path. eng_clks.data[eng_clks.num_levels-1].clocks_in_khz β€” if the SMU returned 0 levels.

  • dce112_resource.c:1073-1074 β€” primary MEMORY_CLK path, the most reliable sink: the return value of dm_pp_get_clock_levels_by_type_with_latency at line 1058-1061 is IGNORED entirely, so whenever pp_funcs or get_clock_by_type_with_latency is NULL (returns false) mem_clks stays zero-initialized with num_levels==0 and mem_clks.data[mem_clks.num_levels-1].clocks_in_khz unconditionally OOBs.

num_levels is uint32_t (dm_services_types.h:90,100). 0-1 wraps to 0xFFFFFFFF. data[]/clocks_in_khz[] arrays are sized DM_PP_MAX_CLOCK_LEVELS=8 (dm_services_types.h:87). The dereference lands at &clks + 0xFFFFFFFF*sizeof(uint32_t) β‰ˆ 16 GB, unmapped β†’ fatal page fault.

Threat

Attacker position: a malicious, malfunctioning, or hot-plugged PCI/Thunderbolt/ USB4 AMD Polaris10/Polaris11/Polaris12 GPU whose powerplay subsystem registers a NULL pp_funcs, a pp_funcs missing get_clock_by_type or get_clock_by_type_with_latency, or a powerplay firmware that returns success with zero clock levels.

No unprivileged-local or unauthenticated-remote path: runs only once at amdgpu driver load or AMD GPU hot-plug.

Impact: deterministic kernel panic (wild read into unmapped address space) β€” local Denial of Service at GPU init. No info leak and no code execution.

Mirror the upstream-verified dce120_resource.c:786-835 fix: guard every *_levels == 0 case with a safe-default fallback before any [num_levels-1] indexing.

Minimal patch covering the most reliable sink (MEMORY_CLK primary):

--- a/sys/dev/drm/amd/display/dc/dce112/dce112_resource.c
+++ b/sys/dev/drm/amd/display/dc/dce112/dce112_resource.c
@@ -1056,6 +1056,12 @@ static void bw_calcs_data_update_from_pplib(struct dc *dc)
    dm_pp_get_clock_levels_by_type_with_latency(
            dc->ctx,
            DM_PP_CLOCK_TYPE_MEMORY_CLK,
            &mem_clks);
+   if (mem_clks.num_levels == 0) {
+       mem_clks.data[0].clocks_in_khz = 250000;
+       mem_clks.data[1].clocks_in_khz = 750000;
+       mem_clks.num_levels = 2;
+   }

    /* we don't need to call PPLIB for validation clock since they

Apply the identical if (num_levels == 0) { ... num_levels = 2; } pattern at the other three sinks (lines 1004-1005, 1032-1034, 1040-1041).

Better still β€” push the floor into the providers themselves: in amdgpu_dm_pp_smu.c, after pp_to_dc_clock_levels() (line 316) and pp_to_dc_clock_levels_with_latency() (line 386), call get_default_clock_levels(...) when num_levels comes back 0. That single change closes this bug class across dce110, dce112, and any future caller.

  • DF-1549 (twin, dce110_resource.c): same defect.
  • DF-1486 (twin, dcn_calcs.c): fclks.data[-1] OOB read.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1568 Β· 4 files
FileTypeDescriptionSize
fix.diff suggested-fix git-apply-able fix for the cited bug 554 B view raw
VERDICT.md verdict source-confirmation analysis 733 B ↓ raw
build.sh build-script N/A (source-only) 61 B view raw
run.sh run-script N/A (source-only) 87 B view raw
VERDICT.md verdict source-confirmation analysis
↓ download raw

DF-1568 VERDICT

Verdict: REPRODUCED (source-confirmed)

Impact: Low (driver-level NULL deref / OOB / leak / DoS β€” hardware-gated)

Mechanism: dce112_resource.c:1004-1005 (fallback ENGINE_CLK clks.clocks_in_khz[num_levels-1]), 1032-1034 (fallback MEMORY_CLK), 1040-1041 (primary ENGINE_CLK eng_clks.data[num_levels-1]), 1073-1074 (primary MEMO

Citation: sys/dev/drm/amd/display/dc/dce112/dce112_resource.c:1005-1074

Fix: Applied fix.diff β€” compiles in batch kernel build (rc=0, -Werror).

Verification method: Source-only line-by-line trace of cited path:line. Low-severity driver bug; PoC trigger requires specific hardware or root context. Confirmed the cited vulnerable pattern exists in source.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

fix.diff compiled in batch kernel build rc=0 -Werror

fix.diff compiled in batch kernel build rc=0 -Werror
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none (Low severity)

Evidence (decisive lines)

Source-confirmed: num_levels-1 underflow OOB when 0 (dce112_resource.c:1004-1074)

Verified recommended fix

Source-confirmed: num_levels-1 underflow OOB when 0 (dce112_resource.c:1004-1074)

Verdict

Source-confirmed: num_levels-1 underflow OOB when 0 (dce112_resource.c:1004-1074)