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

dcn10 construct() OOM error path: dcn10_pp_smu_create() result never checked β€” NULL pp_smu dereferenced via dcn_bw_notify_pplib_of_wm_ranges (probe-time panic); dcn10_create_resource_pool leaks the pool allocation on construct failure

Summary

pp_smu is the only unchecked allocation in construct() whose result is dereferenced during construction: the production default takes the path that calls dcn_bw_notify_pplib_of_wm_ranges which does 'if (!pp->set_wm_ranges)' on the NULL - panic under memory pressure instead of the graceful goto-fail every sibling allocation (15 of them) has. Separately, create_resource_pool never kfrees the several-KB pool when construct fails. NULL-check + kfree-on-fail fix (the DF-4215 dc.c twin).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4313 Β· 6 files
FileTypeDescriptionSize
README.md β€” 2.6 KB ↓ raw
VERDICT.md β€” 3.0 KB ↓ raw
fix.diff β€” 497 B view raw
env.txt β€” 694 B view raw
verdict.json β€” 3.2 KB view raw
manifest.json β€” 1.5 KB view raw

DF-4313 β€” dcn10 construct() OOM error path: unchecked pp_smu (NULL-deref panic) + leaked pool

What (two defects in the dcn10_resource_pool construction error path):

  1. dcn10_resource.c:1350 pool->base.pp_smu = dcn10_pp_smu_create(ctx); The return value is NOT checked (dcn10_pp_smu_create, dcn10_resource.c:858-867, returns NULL on kzalloc failure). Construction continues, and at dcn10_resource.c:1355-1357:

    if (!dc->debug.disable_pplib_wm_range) {
            dc->res_pool = &pool->base;
            dcn_bw_notify_pplib_of_wm_ranges(dc);
    }
    

    debug_defaults_drv.disable_pplib_wm_range = false (dcn10_resource.c:536), so on every production driver dcn_bw_notify_pplib_of_wm_ranges() runs and dereferences dc->res_pool->pp_smu unconditionally:

    dcn_calcs.c:1399  struct pp_smu_funcs_rv *pp = dc->res_pool->pp_smu;
    dcn_calcs.c:1403  if (!pp->set_wm_ranges)      <-- NULL deref
    

    => GPU-probe-time NULL-deref panic under memory pressure instead of the graceful fallback every other allocation in construct() has. (dcn10_hw_sequencer.c:2303 calls the same function later, same deref.)

  2. dcn10_resource.c:1482-1497 dcn10_create_resource_pool(): when construct() fails (any of its many goto fail paths), destruct(pool) frees the pool MEMBERS but the pool allocation itself (kzalloc at :1486, sizeof(struct dcn10_resource_pool), several KB) is leaked - kfree(dcn10_pool) exists only in dcn10_destroy_resource_pool (:1127).

Class/impact: OOM-gated boot/probe-time kernel NULL-deref panic (CWE-476) + memory leak (CWE-401). Identical class to DF-4215 (dc.c construct() OOM path, rated Low). Requires allocation failure during amdgpu DC pool construction on a Raven-class APU - not attacker-steerable timing, severity Low (robustness).

Reachability on this audit guest: NOT reachable - no AMD GPU (env.txt), amdgpu never attaches. Verdict: inconclusive (code-certain, hardware-gated).

Files: fix.diff both one-line fixes (checked pp_smu -> goto fail; kfree(pool) on create failure); git apply --check clean env.txt guest hardware-absence evidence verdict.json machine verdict

Manual validation path for maintainers with a Raven APU: fail-inject kzalloc in dcn10_pp_smu_create (or boot with artificially constrained kmem): pre-fix kernel panics in dcn_bw_notify_pplib_of_wm_ranges; post-fix amdgpu probe fails gracefully ("DC: failed to create pp_smu!") and the pool allocation is freed.

VERDICT.md
↓ download raw

VERDICT β€” DF-4313 dcn10 construct() OOM error-path defects

STATUS: inconclusive (code-certain defect; trigger requires DCN1 ASIC + allocation failure at pool construction - neither available on the audit guest) REPRODUCED: 0

ANALYSIS CONFIRMATION (static, line-accurate):

  1. NULL-deref path: - dcn10_resource.c:1350 assigns pool->base.pp_smu without a NULL check; dcn10_pp_smu_create (dcn10_resource.c:858-867) returns NULL when kzalloc(sizeof(struct pp_smu_funcs_rv)) fails. - Every other allocation in construct() is checked and goto fail (clock sources :1277-1283, dccg :1286, dmcu :1296, abm :1306, irqs :1364, hubps/ipps/dpps/opps/tgs :1378-1416, aux/hw_i2c :1421-1435, mpc :1449, hubbub :1456). pp_smu is the ONE unchecked allocation whose result is dereferenced during construction. - Production defaults take the call: debug_defaults_drv (dcn10_resource.c:522-548) has .disable_pplib_wm_range = false (line 536); construct() then executes dc->res_pool = &pool->base; dcn_bw_notify_pplib_of_wm_ranges(dc); (dcn10_resource.c:1355-1357). - dcn_calcs.c:1399 pp = dc->res_pool->pp_smu; and :1403 if (!pp->set_wm_ranges) dereference pp before any NULL check - there is none in the function. dm_pp_get_funcs_rv (amdgpu_dm_pp_smu.c:565-574) fills set_wm_ranges unconditionally, so a successful alloc is fine; only the NULL case faults. - Same pattern reachable later from dcn10_hw_sequencer.c:2303.

  2. Leak path: - dcn10_create_resource_pool (dcn10_resource.c:1482-1497): on construct() failure the function returns NULL after destruct(pool); destruct (dcn10_resource.c:869-961) frees members only; the pool allocation leaks. kfree(pool) exists solely in dcn10_destroy_resource_pool (dcn10_resource.c:1122-1129).

Severity rationale: OOM-gated NULL-deref panic at GPU probe + small one-shot leak; requires memory exhaustion during amdgpu DC bring-up on a Raven-class APU. Low (robustness), same class/severity as DF-4215.

Guest testability: none (no AMD PCI device on the audit guest, env.txt); amdgpu.ko never probes, construct() never runs. No PoC authored; the defect is proven by code path enumeration above.

Fix (fix.diff, git apply --check clean): - check pool->base.pp_smu == NULL -> dm_error + goto fail (consistent with the 15 other checked allocations in construct()); - kfree(pool) in dcn10_create_resource_pool's failure branch. Both validated by inspection: goto fail runs destruct() which handles a NULL pp_smu (kfree(NULL) safe per linuxkpi slab.h:39-43) and all partially-created members.

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

not_applicable: no DCN1 hardware available for a live baseline/patched run; fix verified by inspection against destruct()'s NULL-safe teardown and by git apply --check.

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

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (line-accurate path enumeration: 1350 -> 1355-1357 -> dcn_calcs.c:1399/1403; leak at 1486/1493), README.md (fail-inject manual test for maintainers), env.txt (guest amd-gpu-count=0), fix.diff

PoC changes

No executable PoC authored: requires DCN1 silicon plus a kzalloc failure inside construct(); the audit guest has no AMD PCI device. Static proof documented instead.

Verified recommended fix

NULL-check dcn10_pp_smu_create() (goto fail) and kfree(pool) when construct() fails in dcn10_create_resource_pool (see fix.diff).

Verdict

Two code-certain defects in the dcn10 pool construction error path, neither executable on the audit guest (no AMD GPU; construct() requires amdgpu attach to a Raven-class DCN1 ASIC): (1) dcn10_resource.c:1350 assigns pool->base.pp_smu from dcn10_pp_smu_create() without a NULL check - it is the only unchecked allocation in construct() whose result is dereferenced during construction; with production defaults (disable_pplib_wm_range=false, dcn10_resource.c:536) construct() calls dcn_bw_notify_pplib_of_wm_ranges (dcn10_resource.c:1355-1357) which dereferences pp_smu at dcn_calcs.c:1399/1403 (if (!pp->set_wm_ranges)) with no NULL guard - an allocation failure turns into a kernel NULL-deref panic at GPU probe instead of the graceful fallback all sibling allocations have; (2) dcn10_create_resource_pool (dcn10_resource.c:1493-1496) leaks the several-KB pool allocation when construct() fails, since destruct() frees only the members. Same class as DF-4215 (Low, OOM-gated). fix.diff adds the missing check (goto fail, consistent with the other 15 checks) and kfree(pool) on the failure branch; git apply --check clean; destruct() is NULL-safe for pp_smu (linuxkpi kfree checks NULL, slab.h:39-43).