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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4186 Β· 5 files
FileTypeDescriptionSize
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)

  1. 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.
  2. kldload radeonkms / boot with the card present.
  3. Expected: NULL-page write panic in cik_compute_set_wptr during radeon_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.

VERDICT.md
↓ download raw

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_init fails -EINVAL (num_doorbells == 0) or -ENOMEM (ioremap fails β‡’ doorbell.ptr == NULL while num_doorbells stays > 0; the used bitmap is pre-zeroed by the kzalloc'd rdev).
  • radeon_device.c:374-384 β€” radeon_doorbell_get allocates from the bitmap only; it cannot detect the NULL mapping.
  • cik.c:8662/8669 β€” radeon_doorbell_get results are stored for the two compute rings during cik_init; with ptr==NULL and indices valid, load proceeds.
  • cik.c:4207 β€” WDOORBELL32(ring->doorbell_index, ring->wptr) in cik_compute_set_wptr dereferences rdev->doorbell.ptr[index] β‡’ NULL-page write β‡’ panic during radeon_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/pciconf on the audit QEMU guest β€” no AMD/ATI PCI device exists; radeon_device_init is 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_testable
baseline no→ patch + rebuild →patched clean

fix.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
↓ fix.diffper-fix-DF-4186

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.