β¬’ 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 1.1 KB ↓ raw
build.sh build-script N/A (source-only) 65 B view raw
run.sh run-script N/A (source-only) 89 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 status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

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)