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)
PoC verification
Evidence pack
findings/poc/DF-4023 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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.
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
rv7xx_parse_power_table()(rv770_dpm.c:2271-2329) allocates the state array at :2289-2292 and onestruct rv7xx_psper state at :2306. If the per-statekzallocfails at state i, the error path at :2307-2310 doeskfree(rdev->pm.dpm.ps);without NULLing it and without freeing theps_priv[0..i-1]already stored, then returns-ENOMEM.rdev->pm.dpm.num_psis only set after the loop (:2327), so it stays 0.rv770_dpm_init()propagates the error at :2368-2370 but leavesrdev->pm.dpm.priv = piallocated (fine β fini frees it) andrdev->pm.dpm.psdangling.radeon_pm_init_dpm()ignores the init failure (radeon_pm.c:1448) and proceeds toradeon_dpm_enable()at :1453. That call dereferencesboot_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). Thenradeon_pm_init_dpm()takes the gracefuldpm_failedpath (radeon_pm.c:1456/1469-1486) β attach completes withdpm_enabled=falseandrdev->pm.dpm.psstill dangling.- 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β theps_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_testablefix.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
Confirmed kernel references
- sys/dev/drm/radeon/rv770_dpm.c:2289
- sys/dev/drm/radeon/rv770_dpm.c:2306
- sys/dev/drm/radeon/rv770_dpm.c:2308
- sys/dev/drm/radeon/rv770_dpm.c:2327
- sys/dev/drm/radeon/rv770_dpm.c:2368
- sys/dev/drm/radeon/rv770_dpm.c:1912
- sys/dev/drm/radeon/rv770_dpm.c:1951
- sys/dev/drm/radeon/rv770_dpm.c:2543
- sys/dev/drm/radeon/rv770_dpm.c:2550
- sys/dev/drm/radeon/radeon_pm.c:1448
- sys/dev/drm/radeon/radeon_pm.c:1705
- sys/dev/drm/radeon/rv770.c:1996
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).
No comments yet.