radeon_device_init ignores radeon_doorbell_init() failure: NULL doorbell aperture WDOORBELL32 write (kernel panic) on CIK+ when BAR2 ioremap fails
Summary
radeon_device_init drops radeon_doorbell_init's return value. On its -ENOMEM path the BAR2 ioremap fails leaving doorbell.ptr == NULL while num_doorbells > 0, so radeon_doorbell_get hands out valid indices during cik_init and the first compute-ring doorbell write cik_compute_set_wptr -> WDOORBELL32 writes to the NULL page during radeon_ib_ring_tests - load-time panic on BONAIRE+ under ioremap pressure or a broken BAR2. Upstream carries the identical dropped check - inherited latent defect. Propagate-the-error fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4186 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.8 KB | β raw | |
| VERDICT.md | β | 2.7 KB | β raw | |
| fix.diff | β | 397 B | view raw | |
| verdict.json | β | 2.5 KB | view raw | |
| manifest.json | β | 755 B | view raw |
DF-4186 β radeon_device_init ignores radeon_doorbell_init() failure: NULL doorbell aperture write (WDOORBELL32) on CIK+
- File:
sys/dev/drm/radeon/radeon_device.c(pass 2) - Class: NULL-pointer write / panic at driver load (CWE-690), memory-pressure and broken-BAR2 conditional
- Hardware: AMD Sea Islands (BONAIRE/KAVERI/KABINI/HAWAII/MULLINS) and newer radeon-supported asics β not present on the audit guest (QEMU has no radeon GPU), so this pack is a static analysis pack.
Root cause
sys/dev/drm/radeon/radeon_device.c:1421-1423:
/* doorbell bar mapping */
if (rdev->family >= CHIP_BONAIRE)
radeon_doorbell_init(rdev); /* return value dropped */
radeon_doorbell_init() (radeon_device.c:330-350) fails with
* -EINVAL when BAR2 yields num_doorbells == 0 (unassigned BAR), or
* -ENOMEM when ioremap() of the doorbell aperture fails
(doorbell.ptr == NULL, but num_doorbells > 0 and the used bitmap is
already zero from the kzalloc'd device).
The dropped -ENOMEM case is the dangerous one: radeon_doorbell_get()
(radeon_device.c:374-384) succeeds purely on the bitmap/num_doorbells
state, so cik_init() (cik.c:8662/8669) stores real doorbell indices while
rdev->doorbell.ptr is still NULL. The first compute-ring doorbell write β
cik_compute_set_wptr() (cik.c:4200-4208):
WDOORBELL32(ring->doorbell_index, ring->wptr); /* ((u32*)NULL)[idx] = wptr */
writes to the NULL page β kernel panic during radeon_ib_ring_tests() at
load. (The -EINVAL case aborts load cleanly because doorbell_get fails,
so the bug window is specifically ioremap failure / half-mapped BAR2.)
Upstream Linux carries the same dropped return value (verified against
torvalds/master radeon_device.c "doorbell bar mapping" block), so this is an
inherited latent defect, reported here for completeness.
Trigger (requires CIK+ radeon hardware)
- CIK+ radeon device whose BAR2 fails to ioremap at load β e.g. kernel virtual-address space exhausted by concurrent ioremaps (root-triggerable memory pressure), or a device/BIOS that leaves BAR2 assigned but unmapable.
kldload radeonkms/ boot with the card present.- Expected: NULL-page write panic in
cik_compute_set_wptrduringradeon_ib_ring_testsβ not reproducible on the audit guest (no radeon).
Why not verified live
The audit guest is QEMU without any radeon/AMD GPU; radeon_device_init for
family >= CHIP_BONAIRE cannot execute there. Status accordingly
not_reproduced (device absent), confidence likely (the NULL-write path is
provable by inspection; the trigger conditions are hardware/mempressure-bound).
Fix
Check the return value (fix.diff β one-liner, matches the pattern used for
every other init call in radeon_device_init). No runtime validation was
possible on this guest.
DF-4186 VERDICT
Status: not_reproduced (impact as tested: none β the audit guest has no radeon hardware; the defect is statically certain, its trigger is hardware/memory-pressure-bound). Confidence: likely.
Analysis (path:line)
radeon_device.c:1422-1423βif (rdev->family >= CHIP_BONAIRE) radeon_doorbell_init(rdev);return value ignored. Upstream Linux has the identical dropped check (torvalds/master), i.e. inherited defect.radeon_device.c:330-350βradeon_doorbell_initfails-EINVAL(num_doorbells == 0) or-ENOMEM(ioremapfails βdoorbell.ptr == NULLwhilenum_doorbellsstays > 0; theusedbitmap is pre-zeroed by the kzalloc'd rdev).radeon_device.c:374-384βradeon_doorbell_getallocates from the bitmap only; it cannot detect the NULL mapping.cik.c:8662/8669βradeon_doorbell_getresults are stored for the two compute rings duringcik_init; with ptr==NULL and indices valid, load proceeds.cik.c:4207βWDOORBELL32(ring->doorbell_index, ring->wptr)incik_compute_set_wptrdereferencesrdev->doorbell.ptr[index]β NULL-page write β panic duringradeon_ib_ring_tests(radeon_device.c:1486).
Exploitability: write of a GPU-ring write-pointer value to the NULL page on CIK+ hardware at driver load. Practically a load-time crash under ioremap pressure / broken BAR2; not attacker-controllable content or address beyond the fixed NULL page. Low severity, availability only, root/HW-conditional.
Verification attempt
- Guest recon:
dmesg/pciconfon the audit QEMU guest β no AMD/ATI PCI device exists;radeon_device_initis unreachable. No kernel rebuild can synthesize a CIK PCI device for this driver path without emulating hardware (unlike the atapi case, where the device is present and only its responses needed emulation). - Class closed for the rest of the file: see the audit result block negative notes (rwsem asymmetry benign under DF lockmgr; golden-register callers all compile-time arrays; vram/gtt underflow is upstream-identical HW-fed math; AGP-retry fini pattern upstream-identical with guarded sub-finis).
Fix
fix.diff: propagate radeon_doorbell_init() failure (pattern already used
for every sibling init call in the same function). Cannot be runtime-validated
on this guest β 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_testablefix.diff is a one-line return-value check matching the sibling init calls in radeon_device_init; cannot be runtime-validated on this guest because no radeon hardware exists (device-absent), so fix validation is not_testable rather than fixed
n/a - no radeon device on the audit guest
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md path walk radeon_device.c:1422 -> :330-350 -> :374-384 -> cik.c:8662/8669 -> cik.c:4207; guest recon: no AMD PCI device in dmesg/pciconf (DF-4186/VERDICT.md)
PoC changes
no runnable PoC exists for this class on this guest (requires CIK+ radeon PCI hardware); static analysis pack only
Verified recommended fix
check radeon_doorbell_init() return value and fail radeon_device_init (fix.diff)
Verdict
radeon_device_init() drops radeon_doorbell_init()'s return value; when the CIK+ doorbell BAR2 ioremap fails (-ENOMEM), doorbell.ptr is NULL while num_doorbells>0, radeon_doorbell_get() then hands out valid indices and the first compute-ring WDOORBELL32 (cik.c:4207 via cik_compute_set_wptr during radeon_ib_ring_tests) writes to the NULL page: a load-time kernel panic on AMD CIK+ hardware under ioremap pressure or with a broken BAR2. Statically certain (path proven path:line), but the audit guest has no radeon GPU, so the path cannot execute there - status not_reproduced (device absent), impact ceiling is an availability crash (root/HW-conditional). Upstream Linux carries the same dropped check, so this is an inherited latent defect.
No comments yet.