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

btc_dpm_get_sclk/btc_dpm_get_mclk dereference eg_pi->requested_rps.ps_priv (NULL) before the first power-state change β€” NULL-page read panic from display bandwidth updates

Summary

requested_rps.ps_priv is NULL from the kzalloc until the first pre_set_power_state; change_power_state's early returns can defer that indefinitely (headless or single-state VBIOS), while evergreen watermark code calls get_sclk directly from display commit whenever dpm_enabled. Boot headless then hotplug a monitor -> NULL read -> panic. Prime-at-enable fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3975 Β· 5 files
FileTypeDescriptionSize
README.md β€” 2.2 KB ↓ raw
VERDICT.md β€” 2.3 KB ↓ raw
verdict.json β€” 2.4 KB view raw
manifest.json β€” 697 B view raw
fix.diff β€” 296 B view raw

DF-3975 β€” btc_dpm_get_sclk/btc_dpm_get_mclk dereference eg_pi->requested_rps.ps_priv before the first power-state change (NULL-page read panic)

Build / Run / Expected

Hardware-gated (Barts/Turks/Caicos display path). Trigger: any radeon_bandwidth_update() that runs after btc_dpm_enable() succeeds but before the first btc_dpm_pre_set_power_state() β€” e.g. the first display hotplug/modeset on a headless or single-state system.

Root cause

  1. eg_pi = kzalloc(...) (btc_dpm.c:2556) β†’ eg_pi->requested_rps.ps_priv == NULL initially. The ONLY writer is btc_update_requested_ps() (btc_dpm.c:2266-2275), reached solely from btc_dpm_pre_set_power_state() (2293).
  2. btc_dpm_get_sclk() / btc_dpm_get_mclk() (2802/2813): c struct rv7xx_ps *requested_state = rv770_get_ps(&eg_pi->requested_rps); /* NULL */ return requested_state->low.sclk; /* read at ~NULL β†’ page fault */
  3. Consumers: evergreen_update_mc_scaling/watermark β€” evergreen.c:2162-2197 β€” call radeon_dpm_get_sclk/get_mclk(rdev, ...) whenever pm_method == PM_METHOD_DPM && dpm_enabled. radeon_bandwidth_update() is invoked directly from display commit paths (atombios_crtc.c:1448, atombios_crtc.c:1642), NOT only from the power-state machine.
  4. The window survives normal boot: radeon_dpm_change_power_state_locked() (radeon_pm.c:1053) returns early at 1080/1105-1107 when the picked state equals current_ps (set to boot_ps at radeon_pm.c:1449) and the active-CRTC set is unchanged β€” e.g. headless boot, or a single-state power table β€” without ever calling pre_set_power_state. The next display event then runs the watermark computation with ps_priv still NULL β†’ NULL-page kernel read β†’ panic. (The ioctl path is safe: RADEON_INFO_CURRENT_GPU_SCLK (radeon_kms.c:567-580) uses get_current_sclk, and current_rps is populated by btc_update_current_ps at the end of a successful btc_dpm_enable (2485).)
VERDICT.md
↓ download raw

VERDICT β€” DF-3975

status: inconclusive (hardware-gated; source-level certainty: likely)

Trace

boot/attach: radeon_pm_init_dpm
  1448  radeon_dpm_init  (btc_dpm_init: eg_pi kzalloc'd, requested_rps.ps_priv == NULL)
  1453  radeon_dpm_enable β†’ btc_dpm_enable succeeds (2485 sets current_rps only)
  1457  dpm_enabled = true
late_init/first compute_clocks:
  radeon_pm_compute_clocks_dpm (radeon_pm.c:1810)
   └─ radeon_dpm_change_power_state_locked (1053)
       1080 current_ps == requested_ps (both = boot_ps, set at radeon_pm.c:1449)
       1105-1107 new_active_crtcs == current_active_crtcs (headless/single-display steady state)
       β†’ return; pre_set_power_state NEVER called β†’ requested_rps.ps_priv stays NULL
first display event (hotplug worker β†’ modeset, or CRTC commit):
  atombios_crtc.c:1448/1642 radeon_bandwidth_update
   └─ evergreen.c:2162 dpm_enabled β†’ radeon_dpm_get_sclk(rdev, false)
       └─ btc_dpm_get_sclk (btc_dpm.c:2802)
           └─ rv770_get_ps(&eg_pi->requested_rps) == NULL
               └─ requested_state->low.sclk β†’ read at 0x0+offset β†’ kernel page fault

Exact structural twin of the second half of DF-3953 (kv_dpm_get_sclk ps_priv-NULL window, rated Medium). DF-3754 (ni) also documents the same window for its file. Not a duplicate: different asic-op table entry (btcd.h wires btc_dpm_get_sclk for Barts/Turks/Caicos).

Why "likely" and not "certain": the code shape is certain; the runtime window depends on display event ordering that I could not exercise (no radeon GPU in the guest β€” env.txt). The early-return conditions that keep the window open (state==boot state, crtcs unchanged) are ordinary for headless or single-state-VBIOS systems, and display hotplug is an unprivileged physical action.

Fix validation

not_testable (no hardware). fix.diff: initialize requested_rps/current_rps from boot_ps at the end of btc_dpm_enable (mirroring btc_update_current_ps at 2485), which closes the window for every consumer.

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

not_testable

fix.diff authored (prime requested_rps at enable); not build-testable without the hardware.

findings/poc/DF-3975/fix.diff
↓ fix.diffper-fix-DF-3975

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md boot→hotplug trace; the ioctl path (radeon_kms.c:567-580) uses get_current_sclk and is safe — the vulnerable consumer is the display-bandwidth path

PoC changes

No PoC: hardware-gated (needs real Barts-class display pipeline to order the events).

Verified recommended fix

Prime requested_rps from boot_ps at the end of btc_dpm_enable (btc_update_requested_ps at 2485) so get_sclk/get_mclk never observe ps_priv==NULL.

Verdict

btc_dpm_get_sclk/btc_dpm_get_mclk (btc_dpm.c:2802-2822) unconditionally dereference eg_pi->requested_rps.ps_priv, which stays NULL from the eg_pi kzalloc (btc_dpm.c:2556) until the first btc_dpm_pre_set_power_state (2293); radeon_dpm_change_power_state_locked's early returns (radeon_pm.c:1080,1105-1107) can defer that first call indefinitely on headless/single-state systems, while evergreen watermark code calls radeon_dpm_get_sclk directly from display commit (evergreen.c:2162-2197 via atombios_crtc.c:1448/1642) whenever dpm_enabled. First display event in the window β†’ NULL-page read β†’ panic. Structural twin of DF-3953's ps_priv window for the btc asic ops; hardware-gated (no radeon GPU in guest) so inconclusive with source-level likelihood.