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

Dangling rdev->pm.dpm.ps after kzalloc-failure kfree in rv7xx_parse_power_table (:2308): ignored rv770_dpm_init error -> double free in rv770_dpm_fini at unload/reboot + leak of parsed ps_priv states β€” shared root site of DF-3645/3755/3955/3973, rv770-dispatch instance

Summary

The per-state kzalloc failure path frees dpm.ps without NULLing and without freeing ps_priv[0..i-1]; the ignored init return keeps the driver attached with ps dangling; graceful enable failure leaves it there. At unload/reboot rv770_dpm_fini skips the ps_priv loop (num_ps==0) and kfree(dpm.ps) double-frees the block. Attach-time alloc-failure window (many-state VBIOS widens it); heap double-free at unload. Cleanup + idempotent-fini fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4023 Β· 6 files
FileTypeDescriptionSize
README.md β€” 1.1 KB ↓ raw
VERDICT.md β€” 3.7 KB ↓ raw
env.txt β€” 775 B view raw
fix.diff β€” 846 B view raw
manifest.json β€” 1.3 KB view raw
verdict.json β€” 3.8 KB view raw

DF-4023 evidence pack

rv770_dpm.c dangling rdev->pm.dpm.ps -> double free in rv770_dpm_fini at unload/reboot (+ leak of parsed ps_priv states). Shared root site (rv770_dpm.c:2306-2310) of the DF-3645/3755/3955/3973 family; the rv770-family dispatch consumption (rv770_dpm_init :2368-2370 -> radeon_pm.c:1448 ignored -> graceful enable failure -> rv770_dpm_fini :2550) is new in this finding.

  • No runnable PoC: hardware-gated + needs a kzalloc failure inside the parse loop (boot-time heap-pressure window). Trigger class accepted for the ni/kv twins (DF-3755 certain, DF-3955).
  • VERDICT.md: full chain; env.txt: hardware absence + md5 match.
  • fix.diff: free parsed ps_privs + NULL ps on the error path, idempotent rv770_dpm_fini (git apply --check clean).

Reproduce (on RV770-family hardware): boot with a many-state crafted VBIOS under kernel-memory pressure so the per-state kzalloc at rv770_dpm.c:2306 fails at state i; let attach continue (enable fails gracefully, e.g. no SMC firmware); kldunload/reboot -> kfree of already-freed ps array (double-free panic / heap corruption) + i leaked rv7xx_ps allocations.

VERDICT.md
↓ download raw

DF-4023 β€” Dangling rdev->pm.dpm.ps after rv7xx_parse_power_table alloc failure β†’ double free in rv770_dpm_fini (+ ps_priv leak)

File: sys/dev/drm/radeon/rv770_dpm.c (pass 2) Class: CWE-415 double free (plus CWE-401 leak of parsed ps_priv states) Hardware gate: RV770/RV730/RV710/RV740 GPU, plus a forced kzalloc failure at rv770_dpm.c:2306 during attach (heap-pressure window at boot with a many-state VBIOS). This is the rv770-family instance of the family DF-3645(ci)/DF-3755(ni)/DF-3955(kv)/DF-3973(btc) β€” the shared root site is in this file (rv770_dpm.c:2308), as DF-3973's notes anticipated; the rv770 dispatch (rv770_asic, radeon_asic.c:1216/1285) consumption via rv770_dpm_init/rv770_dpm_fini is new in this finding. Status: source-level certain (mechanism); runtime hardware+OOM-gated β†’ inconclusive per family convention.

Root cause and chain

  1. rv7xx_parse_power_table() (rv770_dpm.c:2271-2329) allocates the state array at :2289-2292 and one struct rv7xx_ps per state at :2306. If the per-state kzalloc fails at state i, the error path at :2307-2310 does kfree(rdev->pm.dpm.ps); without NULLing it and without freeing the ps_priv[0..i-1] already stored, then returns -ENOMEM. rdev->pm.dpm.num_ps is only set after the loop (:2327), so it stays 0.
  2. rv770_dpm_init() propagates the error at :2368-2370 but leaves rdev->pm.dpm.priv = pi allocated (fine β€” fini frees it) and rdev->pm.dpm.ps dangling.
  3. radeon_pm_init_dpm() ignores the init failure (radeon_pm.c:1448) and proceeds to radeon_dpm_enable() at :1453. That call dereferences boot_ps == NULL (see DF-4022) β€” unless it fails earlier and gracefully, which it does whenever * rv770_construct_vddc_table() returns -EINVAL (steps > 32, rv770_dpm.c:1912-1916), or * rv770_upload_firmware() fails (missing SMC ucode, :1951-1955). Then radeon_pm_init_dpm() takes the graceful dpm_failed path (radeon_pm.c:1456/1469-1486) β€” attach completes with dpm_enabled=false and rdev->pm.dpm.ps still dangling.
  4. On driver unload / reboot: rv770_fini() (rv770.c:1996-1998) β†’ radeon_pm_fini() (radeon_pm.c:1721-1724) β†’ radeon_pm_fini_dpm() β†’ radeon_dpm_fini() unconditionally (radeon_pm.c:1705) = rv770_dpm_fini() (rv770_dpm.c:2543-2552): * num_ps == 0 β‡’ the ps_priv[] loop is skipped β‡’ the i already parsed per-state allocations leak permanently; * kfree(rdev->pm.dpm.ps) at :2550 frees an already-freed block β†’ kmalloc freelist corruption (double free), i.e. panic at best, heap-metadata corruption at worst, triggered by detach/shutdown.

Impact

Kernel heap double-free at driver unload/reboot after a memory-pressure window during attach on RV770-family hardware; plus a deterministic leak of i * sizeof(struct rv7xx_ps) (~100 B/state, up to ~25 KB). Primary actor: kernel heap corruption (panic/DoS; heap grooming at kldunload-time is root-gated). Precondition is an allocation failure inside the parse loop β€” same trigger class accepted for DF-3755 (ni) and DF-3955 (kv).

Fix

Free the already-parsed ps_priv states, NULL rdev->pm.dpm.ps on the error path, and make rv770_dpm_fini() idempotent (NULL after kfree). Combined with the DF-4022 fix (propagating dpm.init failure) the dangling publication is unreachable. See fix.diff.

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
baseline no→ patch + rebuild →patched clean

fix.diff applies clean against the read-only tree; cannot be runtime-validated without radeon hardware and a controlled attach-time alloc-failure window.

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

Confirmed kernel references

Detail

Exploit chain

On RV770-family hardware: many-state crafted VBIOS + boot-time kernel memory pressure forcing the kzalloc at :2306 to fail; graceful enable failure keeps the driver attached; kldunload/reboot double-frees the ps array (heap metadata corruption -> panic; root-gated trigger point, so realistic ceiling is DoS at unload/reboot) plus i*sizeof(rv7xx_ps) permanent leak.

Evidence (decisive lines)

VERDICT.md (full chain with path:line); env.txt (hardware absence + md5 match); fix.diff (error-path cleanup + idempotent fini, git apply --check clean)

PoC changes

No seed PoC. No runnable PoC: needs RV770-family hardware plus a kzalloc failure inside the parse loop; pack follows the DF-3973 convention.

Verified recommended fix

Free the already-parsed ps_priv states, NULL rdev->pm.dpm.ps on the parse error path, and make rv770_dpm_fini idempotent (see fix.diff; pair with DF-4022's init-error propagation).

Verdict

Source-level certain mechanism, runtime hardware+OOM-gated (inconclusive per family convention). rv7xx_parse_power_table's per-state kzalloc failure path (rv770_dpm.c:2306-2310) kfree's rdev->pm.dpm.ps WITHOUT NULLing it and WITHOUT freeing the ps_priv[0..i-1] already stored; num_ps is set only after the loop (:2327) so it stays 0. rv770_dpm_init propagates the error (:2368-2370) but radeon_pm_init_dpm IGNORES dpm.init failure (radeon_pm.c:1448) and proceeds to enable; when enable fails GRACEFULLY before the init_smc_table NULL deref (rv770_construct_vddc_table -EINVAL at rv770_dpm.c:1912-1916, or rv770_upload_firmware failure at :1951-1955 with missing SMC ucode) attach completes with dpm_enabled=false and ps dangling. At unload/reboot: rv770_fini (rv770.c:1996-1998) -> radeon_pm_fini (radeon_pm.c:1721-1724) -> radeon_pm_fini_dpm -> radeon_dpm_fini UNCONDITIONALLY (radeon_pm.c:1705) = rv770_dpm_fini (rv770_dpm.c:2543-2552): num_ps==0 skips the ps_priv loop (i parsed states LEAK), then kfree(rdev->pm.dpm.ps) at :2550 DOUBLE-FREES the block freed at :2308 -> kmalloc heap corruption/panic at detach. Shared root site of the DF-3645/3755/3955/3973 family as DF-3973's notes anticipated; the rv770-family dispatch consumption is new. Trigger class identical to the accepted ni/kv twins (alloc-failure window during attach).