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

dce80/dce81/dce83_create_resource_pool leak struct dce110_resource_pool on construct() failure (missing kfree at 3 sites)

Summary

All three create_resource_pool entry points kzalloc the pool, and on construct() failure return NULL without freeing it. The failure path runs destruct(pool) which frees only the sub-objects; the caller never received the pointer and can only destruct(dc). ~2KB leaks per failed amdgpu bind on DCE8 hardware (OOM/sub-object failure during bind/hotplug; rebind loops amplify). Guest-built model proof ('LEAK CONFIRMED'); hardware-gated. kfree-in-all-three-failure-arms fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4318 Β· 10 files
FileTypeDescriptionSize
df4318.c β€” 4.0 KB view raw
build.sh β€” 86 B view raw
run.sh β€” 89 B view raw
build.log β€” 598 B view raw
run.log β€” 339 B view raw
build_and_run.log β€” 598 B view raw
env.txt β€” 891 B view raw
fix.diff β€” 730 B view raw
VERDICT.md β€” 3.4 KB ↓ raw
verdict.json β€” 3.1 KB view raw
VERDICT.md
↓ download raw

VERDICT β€” DF-4318

Status: untested (kernel path) / model-logic-confirmed (guest) Impact: none on this guest (hardware-gated); code defect certain

Reproduced?

Not against the kernel: the trigger needs an AMD DCE8-class (Sea Islands / Bonaire / Kaveri / Kabini) dGPU driven by amdgpu, plus a failure inside dce80_construct(). The audit guest (DragonFly dfbsd 6.5-DEVELOPMENT, X86_64_GENERIC) has no AMD GPU β€” pciconf -l shows only QEMU hostbridge, IDE, virtio-net/blk and a generic VGA (0x11111234). amdgpu.ko exists in /boot/kernel but can never bind.

What was verified, on the guest:

  1. Control-flow model proof (df4318.c, exact copy of the three-function flow) built with the guest's cc 8.3 [DragonFly] and run: the failure path ends with exactly one orphaned allocation (the pool) while destruct() reclaimed the children; the success path ends at zero live allocations. Output in run.log / build_and_run.log.

  2. Line-by-line trace (static, cited): - dce80_resource.c:1052-1067 β€” pool = kzalloc(...); if (dce80_construct(...)) return &pool->base; … return NULL; β€” no kfree(pool) on the failure arm. Same shape at :1262-1277 (dce81) and :1468-1483 (dce83). - dce80_resource.c:1047-1049 β€” construct failure runs destruct(pool) (:726-796) which frees only the sub-objects (opps/transforms/ipps/mis/ tgs/engines/i2cs/stream_encs/clk_srcs/abm/dmcu/dccg/irqs/audios), never pool. - Caller core/dc.c:671-677 (dc_construct): on NULL it does goto fail; destruct(dc) β€” it never received the pointer, so nothing can free the orphan.

Trigger conditions (when hardware exists)

Any of, during amdgpu bind/hotplug: - sub-object kzalloc failure (OOM): tg (:974), mi (:982), ipp (:989), transform (:996), opp (:1003), aux engine (:1012), hw i2c (:1019), sw i2c (:1026), dccg (:931), dmcu (:941), abm (:951), clock sources (:895-914), dp clock source (:917) - dal_irq_service_dce80_create failure (:968-970) - resource_construct failure (audio create fail β€” dc_resource.c:235-238 β€” or virtual stream encoder fail β€” dc_resource.c:267-270)

Each failed bind leaks sizeof(struct dce110_resource_pool) (~1-2 KB: embedded resource_pool with 6/6/6/6/6/12/6/6/6/7/6 pointer arrays + dce110 members). Rebind/hotplug loops under memory pressure amplify it. No privilege boundary is crossed β€” this is a hardening-class leak.

Exploit chain

None (CWE-401 leak, not corruption). Ceiling: unbounded kernel heap consumption over repeated failing attaches on the affected GPU.

Fix validation

fix.diff adds kfree(pool) to the three failure arms (mirrors dce80_destroy_resource_pool's destruct(pool); kfree(pool); at :841-848). Not kernel-tested: guest lacks the hardware; the model proves the logic trivially β€” with the fix, failure path ends at 0 live allocations.

Conclusion

Code defect is certain (dead-simple missing free, three sites). Guest kernel-path reproduction untested (hardware absent). Severity Low.

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 adds the three missing kfree(pool) calls (mirroring dce80_destroy_resource_pool :841-848 destruct+free pattern). Cannot be kernel-validated on this guest (no AMD GPU); the model trivially shows the patched failure path ends at 0 live allocations.

fix.diff; model accounting in df4318.c (pool_destroy() demonstrates the destruct+free pairing the fix restores on the failure arm)
↓ fix.diffper-fix-DF-4318

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log / build_and_run.log (guest model run: 'LEAK CONFIRMED (model)', failure path live_allocs=1 = orphaned pool); env.txt (no AMD GPU on guest); kernel refs dce80_resource.c:1056,1062,1066 + 1272-1276 + 1478-1482, destruct at :726-796, caller core/dc.c:671-677

PoC changes

No seed PoC existed (pass-2 checklist hint only). Authored df4318.c: plain-C control-flow model of dce80_create_resource_pool/dce80_construct/destruct with explicit allocation accounting (host lacked a compiler; guest cc used; sanitizer-free so it runs anywhere).

Verified recommended fix

Add kfree(pool) before 'return NULL' in dce80/dce81/dce83_create_resource_pool failure paths (see fix.diff).

Verdict

Code defect certain: dce80/dce81/dce83_create_resource_pool (dce80_resource.c:1052-1067, 1262-1277, 1468-1483) never kfree(pool) when dceXX_construct() fails; construct's res_create_fail path runs destruct(pool) (frees sub-objects only) and the caller dc_construct (core/dc.c:671-677) cannot free a pointer it never received β€” sizeof(struct dce110_resource_pool) (~2KB) leaks per failed amdgpu bind on Sea Islands hardware (OOM in any sub-create, irq-service create failure, or resource_construct failure). Kernel-path reproduction untested: the audit guest has no AMD GPU (pciconf: virtio + generic VGA only; amdgpu.ko present but unbindable). A line-faithful control-flow model was built and run on the guest (cc 8.3) and confirms the orphaned allocation (run.log). Hardening-class leak, no privilege boundary crossed.