radeon_cs_parser_relocs: unchecked radeon_vm_get_bos() NULL return silently drops the VM page directory and page tables from the CS reservation/validation set β submission then updates the PD/PTs without holding their reservations (stale-address GPU writes, unsynchronized eviction); latent on DF as compiled, live upstream and after any DF-4580-style allocator fix
Summary
The vm_bos assignment ignores the NULL return: the VM PD/PT BOs are never ttm_eu-reserved, validated, or fenced - yet the CS proceeds to submit a DMA IB writing PDEs at the now-unheld BOs' offsets; a concurrent eviction can relocate/free them, yielding GPU writes to stale addresses. Distinct from DF-4412 (max_pde_used TOCTOU inside radeon_vm.c) - this is the missing NULL check at the call site. Latent on DF today (GFP_KERNEL never returns NULL here - it panics per DF-4580) and live upstream. Check-and-unwind fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4581 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.2 KB | β raw | |
| VERDICT.md | β | 3.2 KB | β raw | |
| DF-4581-trace.c | β | 1.9 KB | view raw | |
| fix.diff | β | 473 B | view raw | |
| manifest.json | β | 802 B | view raw | |
| verdict.json | β | 2.9 KB | view raw |
DF-4581 β radeon_cs_parser_relocs: unchecked radeon_vm_get_bos() NULL return omits VM PD/PTs from the CS reservation set
Where
sys/dev/drm/radeon/radeon_cs.c:190-192:
if (p->cs_flags & RADEON_CS_USE_VM)
p->vm_bos = radeon_vm_get_bos(p->rdev, p->ib.vm, &p->validated);
No NULL check. radeon_vm_get_bos() (sys/dev/drm/radeon/radeon_vm.c:128-138)
returns NULL when its kvmalloc_array() fails β after having added nothing
to &p->validated.
Consequence on failure
- VM page directory + page tables are missing from
p->validated: neverttm_eu_reserve_buffers-reserved, never validated/pinned, and never fenced byttm_eu_fence_buffer_objects(radeon_cs.c:427). - Submission proceeds anyway:
radeon_cs_ib_vm_chunkβradeon_bo_vm_update_pte(radeon_cs.c:565) βradeon_vm_update_page_directorybuilds a DMA IB writing PDEs atpd_addr = radeon_bo_gpu_offset(pd)β a BO whose placement is not held for this submission. Concurrent eviction can move the PD/PT BOs (their reservations are free) β the GPU writes PDEs at a stale address (freed/reused VRAM/GTT memory of another client), and the PTs can be evicted while the IB still executes. Race-grade GPU memory corruption, trigger-gated behind an allocation failure.
Reachability on this tree (important, honest caveat)
On DragonFly as compiled, kvmalloc_array(..., GFP_KERNEL) cannot return
NULL: GFP_KERNEL = M_WAITOK without M_NULLOK
(sys/dev/drm/include/linux/gfp.h:40), so failure panics or hangs instead
(see DF-4580). The NULL path is therefore latent on DF today and a
live upstream bug on Linux; it must be fixed together with any DF-4580
allocator-semantics fix, or fixing one bug silently activates the other.
Run
No runtime PoC is possible on the audit guest (no AMD GPU, no /dev/dri β
radeon_cs_ioctl unreachable) and the NULL path cannot even be forced
without allocator changes. DF-4581-trace.c documents the trace and the
fix; verification is by code inspection only.
Expected result of the fix
radeon_cs_parser_relocs returns -ENOMEM when radeon_vm_get_bos fails,
the ioctl unwinds via radeon_cs_parser_fini β no submission with an
unreserved page directory.
DF-4581 VERDICT
Status: not_reproduced β code-proven defect, runtime-gated twice over (no AMD GPU on the guest; NULL path latent on DF as compiled).
Trace (by inspection, line-accurate)
radeon_cs_ioctl(sys/dev/drm/radeon/radeon_cs.c:655) βradeon_cs_parser_relocs(radeon_cs.c:90-201).radeon_cs.c:190-192:p->vm_bos = radeon_vm_get_bos(...)β return ignored.radeon_vm_get_bos(sys/dev/drm/radeon/radeon_vm.c:135-138):list = kvmalloc_array(...); if (!list) return NULL;β nothing has been added toheadat that point (the firstlist_addis at radeon_vm.c:147).- Failure result:
p->validatedcontains only the user reloc BOs. The VM page directory (vm->page_directory) and page-table BOs are never reserved byradeon_bo_list_validate(radeon_cs.c:196βttm_eu_reserve_buffers) and never fenced byttm_eu_fence_buffer_objects(radeon_cs.c:427). - Submission continues:
radeon_cs_ib_vm_chunk(radeon_cs.c:537) undervm->mutexβradeon_bo_vm_update_pte(radeon_cs.c:492) βradeon_vm_update_page_directory(radeon_vm.c) submits a DMA IB that writes PDEs atradeon_bo_gpu_offset(pd)β with the PD/PT BOs unreserved and unpinned for this CS, so a concurrent eviction (their reservations are not held) can relocate/free them β stale-address GPU writes + unsynchronized eviction mid-IB = GPU memory corruption.
Distinct from DF-4412: DF-4412 is a TOCTOU inside radeon_vm_get_bos
(max_pde_used re-read racing GEM_VA, OOB write in radeon_vm.c's fill
loop). DF-4581 is the unchecked NULL return at the radeon_cs.c:191 call
site β different root cause, different file, different fix.
Why not reproduced on the guest
- Guest has no AMD GPU (
pciconfig -lhas no 1002: device;/dev/driabsent) βradeon_cs_ioctlunreachable at runtime. - Even with the hardware, on DragonFly as compiled
kvmalloc_arraywithGFP_KERNELnever returns NULL (M_WAITOKwithoutM_NULLOK,sys/dev/drm/include/linux/gfp.h:40; see DF-4580's reproduced panic mechanism) β the NULL branch is latent until the allocator semantics are fixed (which is exactly when this missing check would become a live corruption race).
Impact ceiling
On a kernel where kvmalloc can fail (upstream Linux; DF after an M_NULLOK-style fix): race-grade GPU memory corruption behind an allocation failure β Medium-class, hardware- and race-gated. On DF today: latent.
Fix
One line + error unwind (fix.diff): check p->vm_bos == NULL β
DRM_ERROR + return -ENOMEM; the ioctl's error path
(radeon_cs_parser_fini(parser, r, false)) already unwinds correctly with
an empty/partial validated list (relocs GEM refs are put at
radeon_cs.c:435-443; kvfree(NULL) is a no-op).
Artifacts
DF-4581-trace.cβ annotated trace + fixfix.diffβ git-apply-check-clean unified diffREADME.md,manifest.json,verdict.json
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 authored and git-apply-check clean; not runnable on this guest (no radeon hardware, NULL path latent on DF as compiled).
findings/poc/DF-4581/fix.diff; git apply --check passes.
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (line-accurate trace radeon_cs.c:190->radeon_vm.c:135-138->unreserved PD/PT update path radeon_cs.c:565); fix.diff (NULL check + -ENOMEM) git-apply-check clean.
PoC changes
No seed existed; authored DF-4581-trace.c as an annotated code-trace scaffold (no runtime possible: hardware absent + NULL path latent on DF).
Verified recommended fix
Check radeon_vm_get_bos() return at radeon_cs.c:191: if NULL, DRM_ERROR and return -ENOMEM so the ioctl unwinds instead of submitting with an unreserved page directory.
Verdict
Defect certain by inspection: radeon_cs.c:190-192 ignores the return of radeon_vm_get_bos(); on NULL (kvmalloc_array failure, radeon_vm.c:135-138) the VM page directory and page tables are silently omitted from p->validated, so they are neither reserved, validated/pinned, nor fenced for the submission, while radeon_cs_ib_vm_chunk proceeds to update the page directory via a DMA IB using radeon_bo_gpu_offset() of the now-unheld BOs -> stale-address GPU writes and unsynchronized eviction (race-grade GPU memory corruption). Not reproduced at runtime: the audit guest has no AMD GPU (no /dev/dri, no 1002: PCI device) so radeon_cs_ioctl is unreachable, and on DragonFly as compiled the NULL branch is itself unreachable because GFP_KERNEL kvmalloc never returns NULL (M_WAITOK without M_NULLOK; failure panics/hangs - see DF-4580 whose sink panic was reproduced on this guest). The bug is live upstream-shaped code and activates on DF as soon as allocator NULL semantics are introduced; fix must land together with DF-4580.
No comments yet.