construct() OOM failure path dereferences NULL current_state / NULL dc->ctx in destruct() β GPU-probe NULL-deref panic instead of graceful fallback
Summary
Every one of construct's six allocation failures goes to fail -> destruct, but destruct unconditionally calls dc_release_state(dc->current_state) - kref_put through NULL when dc_create_state failed - and dereferences dc->ctx when the ctx kzalloc failed. ASSERT is WARN_ON-only so nothing arrests the deref; a single failed kzalloc during amdgpu DC probe panics where dc_create is designed to return NULL. OOM-gated boot/probe DoS. Guard-the-unconstructed fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4215 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.3 KB | β raw | |
| VERDICT.md | β | 1.5 KB | β raw | |
| env.txt | β | 992 B | view raw | |
| fix.diff | β | 629 B | view raw | |
| manifest.json | β | 758 B | view raw | |
| verdict.json | β | 1.9 KB | view raw |
DF-4215 β dc construct() OOM failure path dereferences NULL current_state / NULL ctx in destruct() β probe-time panic
- File:
sys/dev/drm/amd/display/dc/core/dc.c - Lines: 519β538 (destruct), 569β620 (construct allocs +
goto fail), 686β689 - Class: NULL pointer dereference on error path (CWE-476/CWE-755)
- Hardware gate: AMD GPU (amdgpu DC core); trigger = allocation failure during
dc_create()
Root cause
construct() performs six kzallocs in sequence (bw_dceip 569, bw_vbios
577, dcn_soc 585, dcn_ip 593, dc_ctx 602, current_state =
dc_create_state() 615). Every failure does goto fail β
destruct(dc) (686β689). But destruct() unconditionally dereferences the
very things that may not exist yet:
static void destruct(struct dc *dc)
{
dc_release_state(dc->current_state); /* 521: NULL if 615 failed */
...
if (dc->ctx->gpio_service) /* 528: NULL ctx if 602 failed */
dal_gpio_service_destroy(&dc->ctx->gpio_service);
if (dc->ctx->i2caux) /* 531 */
...
- If the
dc_ctxallocation (602β606) fails,dc->ctx == NULLβ yet 521 runs first:dc_release_state(NULL)βkref_put(&context->refcount, β¦)(dc.c:1103-1106) reads through the NULL pointer β page-fault panic. - If
dc_create_state()fails (615β620),dc->current_state == NULLβ same panic at 521. - Even if current_state were guarded,
dc->ctx->gpio_service(528),dc->ctx->i2caux(531),dc->ctx->created_bios(534),kfree(dc->ctx)(537) all deref a possibly-NULLdc->ctx.
dc_create() (729β775) then propagates: construct_fail: kfree(dc); return
NULL β so with the guards in place the driver degrades gracefully to
"Display Core failed to initialize" (amdgpu_dm.c:440-444); without them, a
single failed kzalloc during GPU probe panics the kernel.
Note ASSERT_CRITICAL/ASSERT are WARN_ON-only in this tree
(os_types.h:59-77), so nothing else arrests the path first.
Verification status
untested β hardware-gated (guest's only GPU is the bochs VGA 1234:1111,
env.txt; amdgpu DC never initializes, and an OOM at exactly this probe
window would additionally be needed). Source-certain.
Fix
fix.diff: NULL-guard dc->current_state and every dc->ctx deref in
destruct().
VERDICT β DF-4215
status: untested (hardware-gated) Β· impact: panic (probe-time, OOM-gated) Β· confidence: certain (source)
Not reproducible on the audit guest: no AMD GPU (env.txt β only bochs
1234:1111), so dc_create()/construct() never execute; the trigger
further requires one of six kzallocs to fail during probe.
Source proof:
- construct() order: dceip (569) β vbios (577) β dcn_soc (585) β dcn_ip
(593) β ctx (602) β current_state (615); every failure goto fail β
destruct(dc) (dc.c:686-689).
- destruct() line 521: dc_release_state(dc->current_state) with
current_state == NULL when 615-620 failed, or when any earlier alloc
failed (kzalloc'd dc still zero) β kref_put(&context->refcount, β¦)
(dc.c:1103-1106) is an inline read at NULL+offset β page fault.
- Even bypassing that, dc->ctx may be NULL (602-606 failure) and is
deref'd at 528/531/534/537.
- ASSERT/ASSERT_CRITICAL are WARN_ON-only (os_types.h:59-77) β no
backstop.
Deterministic NULL-deref, but gated on OOM during GPU probe β Low severity.
Fix is defensive guards; not runtime-validated (no hardware) β
fix_status: not_testable.
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_testablediff authored and git-apply-verified against the read-only sys/ tree; runtime fix validation impossible without the triggering hardware on the QEMU guest
findings/poc/DF-4215/fix.diff (git apply --check passed)
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-4215/VERDICT.md, findings/poc/DF-4215/env.txt (guest pciconf: no triggering hardware), findings/poc/DF-4215/fix.diff
PoC changes
no runnable PoC: hardware-gated path absent from the audit guest (env.txt)
Verified recommended fix
NULL-guard current_state and all dc->ctx derefs in destruct()
Verdict
construct() jumps to destruct() on any of six kzalloc failures, but destruct() unconditionally does dc_release_state(dc->current_state) (dc.c:521 β kref_put through NULL when current_state alloc at 615 failed) and derefs dc->ctx at 528/531/534/537 (NULL when the ctx alloc at 602 failed). A single OOM during amdgpu DC probe turns into a kernel NULL-deref panic instead of graceful 'Display Core failed to initialize'.
No comments yet.