cayman_init dropped the CP1/CP2 compute-ring r600_ring_init (upstream and in-tree si.c/cik.c have them) β cayman_cp_resume evaluates order_base_2(0) = 1UL<<64 (C-standard UB) and programs CP_RB1/CP_RB2_CNTL/BASE with zero-size garbage on every Cayman/Aruba boot
Summary
ni.c inits only GFX/DMA0/DMA1 rings; CP1/CP2 never get r600_ring_init, yet cayman_cp_resume iterates them: order_base_2(0) expands to 1UL<<64 (undefined shift) then writes derived RB_BUFSZ and CP_RB1/2_BASE=0 to the GPU. In-tree proof of divergence (si.c/cik.c init their compute rings; upstream ni.c does too). No attacker (boot path only, memory-safe consumers reject), but C-UB, wrong MMIO config, and compute permanently unusable on this family. fix.diff restores the dropped blocks verbatim.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3986 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.5 KB | β raw | |
| VERDICT.md | β | 2.8 KB | β raw | |
| fix.diff | β | 752 B | view raw | |
| manifest.json | β | 1.3 KB | view raw | |
| verdict.json | β | 2.8 KB | view raw |
DF-3986 β drm/radeon ni.c: port dropped the CP1/CP2 compute-ring r600_ring_init from cayman_init β order_base_2(0) UB (1UL << 64) and garbage compute-ring MMIO programming at every Cayman/Aruba boot
Where
sys/dev/drm/radeon/ni.c:2434-2443 (cayman_init):
ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
ring->ring_obj = NULL;
r600_ring_init(rdev, ring, 1024 * 1024);
ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; /* CP1/CP2 inits MISSING */
ring->ring_obj = NULL;
r600_ring_init(rdev, ring, 64 * 1024);
...
Upstream Linux ni.c (cayman_init) initializes five rings β GFX,
CP1, CP2 (1 MB each), DMA0, DMA1. In-tree corroboration that compute rings
must be initialized: si.c:6876-6894 (SI) and cik.c:8655-8679 (CIK) both
call r600_ring_init for CAYMAN_RING_TYPE_CP1/2_INDEX. The DFly port
kept the consumers but dropped the producers:
cayman_cp_resume(ni.c:1698-1734) loops over{GFX, CP1, CP2}and computesrb_cntl = order_base_2(ring->ring_size / 8)(ni.c:1704) β withring_size == 0this evaluatesilog2(roundup_pow_of_two(0))β1UL << flsl(ULONG_MAX)β1UL << 64(C-standard undefined shift; sys/dev/drm/include/linux/log2.h:33-36, 100). It then programsCP_RB1_CNTL/CP_RB2_CNTLwith the resulting RB size field andCP_RB1/2_BASE = ring->gpu_addr >> 8= 0 (ni.c:1713-1721) β garbage compute-ring configuration written to real silicon at every Cayman/Aruba boot.radeon_fence_driver_start_ring(CP1/CP2)is still called (ni.c:2225-2235) andcayman_cp_resumeexplicitly leaves the ringsready = false(ni.c:1739-1740).
Why impact stays Info
- Userspace cannot select CP1/CP2 on this family:
radeon_cs_get_ringmapsRADEON_CS_RING_COMPUTEtoCAYMAN_RING_TYPE_CP{1,2}_INDEXonly forfamily >= CHIP_TAHITI(radeon_cs.c:218-224); CAYMAN/ARUBA map COMPUTE to the GFX ring. - Even if something queued to an uninitialized ring,
radeon_ring_allocrejectsndw > ring->ring_size/4(radeon_ring.c:109-111), so no ring-buffer OOB write is reachable. - Residual impact: standards-UB executed in the boot path (currently
benign
0on x86shlsemantics) + wrong MMIO config for the compute rings (functional: compute unusable on Cayman/Aruba in this port).
Hardware-gated (needs a Cayman/Aruba GPU; the audit guest has none β see
../DF-3981/env.txt). Info severity, source-certain.
Fix
fix.diff β restore the two dropped blocks (verbatim upstream shape):
r600_ring_init(rdev, &rdev->ring[CAYMAN_RING_TYPE_CP{1,2}_INDEX], 1024*1024).
DF-3986 VERDICT β dropped CP1/CP2 ring init in cayman_init (ni.c)
Status: inconclusive (hardware-gated: no AMD Northern-Islands GPU on the audit guest β vgapci is QEMU std VGA, env evidence in ../DF-3981/env.txt). Source-level certainty: certain (divergence provable in-tree).
Divergence proof
Consumers of CP1/CP2 ring state in this file:
- cayman_cp_resume ridx[] = {GFX, CP1, CP2} (ni.c:1636-1640) iterates all
three: rb_cntl = order_base_2(ring->ring_size / 8) (ni.c:1704),
WREG32(cp_rb_base[i], ring->gpu_addr >> 8) (ni.c:1720);
- radeon_fence_driver_start_ring(CP1) / (CP2) (ni.c:2225, 2231);
- ready = false for both at ni.c:1739-1740.
Producers: cayman_init calls r600_ring_init only for GFX (1 MB) and
DMA0/DMA1 (64 KB) (ni.c:2434-2443). r600_ring_init (r600.c:2789-2807) is
the only setter of ring->ring_size before resume; CP1/CP2 therefore
reach cayman_cp_resume with ring_size == 0 and gpu_addr == 0.
Upstream Linux ni.c initializes all five rings (GFX/CP1/CP2 at 1 MB,
DMA0/DMA1 at 64 KB) in cayman_init; the in-tree siblings si.c
(si.c:6876-6894) and cik.c (cik.c:8655-8679) do the same for their
compute rings. The DFly port dropped exactly the two CP blocks.
UB and misprogramming
order_base_2(n) = ilog2(roundup_pow_of_two(n))
(sys/dev/drm/include/linux/log2.h:100 and :33-36):
roundup_pow_of_two(0) = 1UL << flsl(0 - 1) = 1UL << flsl(ULONG_MAX) =
1UL << 64 β shift count equals the width of unsigned long: undefined
behavior per C11 6.5.7. On x86-64 the compiler emits shl which masks the
count mod 64, yielding 1 β order_base_2(0) == 0 β RB_BUFSZ field 0 with
ring base 0 programmed into CP_RB1/CP_RB2 at every Cayman/Aruba boot
(functionally meaningless config; compute rings never enabled since
ready=false).
Why no memory-safety impact
radeon_cs_get_ring(radeon_cs.c:203-234): COMPUTE β CP1/CP2 only forfamily >= CHIP_TAHITI; CAYMAN/ARUBA (< TAHITI) map COMPUTE to GFX.radeon_ring_alloc(radeon_ring.c:104-137) fails-ENOMEMfor anyndw > ring->ring_size/4, so a zero-sized ring cannot be written.radeon_ring_supports_scratch_reg/debugfs only read the ring array.
Residual: C-standard UB in the boot path + wrong MMIO values + compute rings permanently unusable on this family in this port (upstream supports compute on Cayman). Info severity.
Fix validation
Not runnable (no AMD GPU in the guest; vgapci0 is 1234:1111 QEMU VGA).
fix.diff restores the two upstream init blocks verbatim.
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 applies cleanly against sys/dev/drm/radeon/ni.c; cannot be kernel-build/boot validated without NI hardware.
fix.diff in pack; upstream-shape init blocks
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (divergence proof, UB expansion, no-impact argument); fix.diff (restores the two upstream init blocks verbatim, patch --dry-run clean)
PoC changes
None possible: no AMD GPU on the guest; the finding is a provable source divergence.
Verified recommended fix
Restore the two dropped r600_ring_init blocks for CAYMAN_RING_TYPE_CP1/CP2_INDEX in cayman_init (fix.diff).
Verdict
cayman_init (ni.c:2434-2443) initializes only GFX/DMA0/DMA1 rings; the CP1/CP2 compute-ring r600_ring_init blocks present in upstream Linux ni.c (and mirrored in-tree by si.c:6876-6894 and cik.c:8655-8679) were dropped in the port. Consumers remained: cayman_cp_resume (ni.c:1636-1734) computes order_base_2(ring->ring_size/8) for CP1/CP2 with ring_size==0 (ni.c:1704), which expands to ilog2(roundup_pow_of_two(0)) = 1UL<<flsl(ULONG_MAX) = 1UL<<64 - undefined shift per C11 6.5.7 (log2.h:33-36,100) - and programs CP_RB1/CP_RB2 with RB_BUFSZ derived from it and base 0 (ni.c:1713-1721) on every Cayman/Aruba boot; fence start for CP1/CP2 still runs (ni.c:2225,2231). No kernel memory-safety impact: radeon_cs_get_ring maps COMPUTE to CP1/CP2 only for family >= CHIP_TAHITI (radeon_cs.c:218-224), and radeon_ring_alloc rejects ndw > ring_size/4 (radeon_ring.c:109-111). Info-severity porting divergence + boot-path UB; hardware-gated (guest has no AMD GPU - vgapci is QEMU 1234:1111, ../DF-3981/env.txt) so no runtime verification possible.
No comments yet.