NULL-pointer dereference panic in radeon_vce_cs_reloc when a VCE command stream is submitted without a relocation chunk
Summary
parser_init only assigns chunk_relocs when the user supplies a RELOCS chunk and parser_relocs silently tolerates NULL; a VCE-ring CS whose IB contains any reloc-bearing command reaches cs_reloc's deref of chunk_relocs->length_dw - a fixed-offset NULL read panic. Both sibling paths guard exactly this case with 'No relocation chunk!'; the VCE parser forgot. Deterministic single-ioctl unpriv DoS on SI/CIK VCE hardware. Code-trace certain (guest has no radeon GPU); trigger built. Mirror-the-UVD-guard fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4608 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| vce_null_relocs.c | β | 3.6 KB | view raw | |
| build.sh | β | 104 B | view raw | |
| run.sh | β | 118 B | view raw | |
| build.log | β | 107 B | view raw | |
| run.log | β | 456 B | view raw | |
| README.md | β | 2.5 KB | β raw | |
| VERDICT.md | β | 2.5 KB | β raw | |
| fix.diff | β | 974 B | view raw | |
| manifest.json | β | 839 B | view raw | |
| verdict.json | β | 2.9 KB | view raw |
DF-4608 β radeon_vce_cs_reloc() NULL-deref panic: CS submitted without a relocation chunk
File: sys/dev/drm/radeon/radeon_vce.c (pass 2)
Severity: Medium (deterministic local kernel panic from unprivileged DRM access) Confidence: certain
CWE: CWE-476
The path
A DRM_IOCTL_RADEON_CS submission is parsed by radeon_cs_parser_init()
(radeon_cs.c:302-361): it sets p->chunk_relocs only if the user
supplied a RADEON_CHUNK_ID_RELOCS chunk (radeon_cs.c:313-315) β omitting
the chunk is perfectly legal. radeon_cs_parser_relocs()
(radeon_cs.c:83-85) then returns 0 ("nothing to do") instead of
rejecting, and p->relocs/p->chunk_relocs stay NULL
(the parser was memset in radeon_cs_ioctl, radeon_cs.c:674).
Selecting a VCE ring (FLAGS chunk dword 1 = RADEON_CS_RING_VCE, no
RADEON_CS_USE_VM) dispatches radeon_vce_cs_parse
(radeon_asic.c:1769/2077). Its very first reloc-bearing command β e.g.
0x05000005 feedback buffer (radeon_vce.c:646-651) β calls
radeon_vce_cs_reloc():
relocs_chunk = p->chunk_relocs; /* NULL */
offset = radeon_get_ib_value(p, lo);
idx = radeon_get_ib_value(p, hi);
if (idx >= relocs_chunk->length_dw) { /* NULL deref */
β kernel page fault at offsetof(struct radeon_cs_chunk, length_dw) β
panic. Any local user with the DRM node open (typically unprivileged
/dev/dri/renderD128 on desktops) can panic the machine, deterministically,
with a single ioctl.
The guards everyone else has (and VCE forgot)
- generic packet path
radeon_cs_packet_next_reloc()β radeon_cs.c:846-850:if (p->chunk_relocs == NULL) { DRM_ERROR("No relocation chunk !"); return -EINVAL; } - UVD parser
radeon_uvd_cs_parse()β radeon_uvd.c:705-709: the identical guard, at the head of the parser.
radeon_vce_cs_parse()/radeon_vce_cs_reloc() have no such check anywhere
β a plain oversight when VCE support was ported.
Trigger
vce_null_relocs.c: raw ioctl (no libdrm), FLAGS chunk selecting the VCE
ring + IB chunk containing a session command followed by a feedback-buffer
command, and deliberately no RELOCS chunk.
Guest status
Builds clean with the guest cc 8.3. The audit QEMU guest has no radeon GPU
(ls /dev/dri β absent; pciconf -l | grep 1002 β zero matches), so the
ioctl cannot be exercised β trigger provided for affected hardware
(TAHITI/PITCAIRN/VERDE/OLAND/ARUBA/BONAIRE/KAVERI/KABINI/HAWAII/MULLINS
with radeonkms loaded and the VCE rings up).
Fix
Mirror the UVD guard at the head of radeon_vce_cs_parse() (fix.diff).
DF-4608 VERDICT
Status: untested (hardware-required) β code-trace certainty
Proof chain (all path:line)
radeon_cs_ioctlmemsets the parser (radeon_cs.c:674) β chunk_relocs NULL by default.radeon_cs_parser_initassignsp->chunk_relocsonly for a RADEON_CHUNK_ID_RELOCS chunk (radeon_cs.c:313-315). A CS consisting of FLAGS+IB chunks only is fully accepted (IB length != 0 checked at :319; ring validity at :371 via radeon_cs_get_ring; VCE cs_parse non-NULL checked at :376).radeon_cs_parser_relocsearly-returns 0 when chunk_relocs == NULL (radeon_cs.c:83-85) β the submission is NOT rejected.radeon_cs_ib_chunkβradeon_cs_parse(rdev, ring, parser)(radeon_cs.c:466) β(rdev)->asic->ring[r]->cs_parse(p)(radeon.h:2726) βradeon_vce_cs_parsefor TN_RING_TYPE_VCE1/2 (radeon_asic.c:1769, 2077).radeon_vce_cs_parsecase 0x05000005 (radeon_vce.c:646-651) (or encode 0x03000001 :615, context 0x05000001 :631, bitstream 0x05000004 :638) βradeon_vce_cs_relocβrelocs_chunk = p->chunk_relocs(radeon_vce.c:478) βif (idx >= relocs_chunk->length_dw)(radeon_vce.c:482) dereferences NULL + offsetof(length_dw)=4 β unrecoverable kernel page fault β panic.- Comparative guards that prove intent: radeon_cs.c:846-850 (generic reloc path) and radeon_uvd.c:705-709 (UVD parser) both reject a missing relocs chunk with "No relocation chunk !". The VCE parser has no equivalent.
The trigger (vce_null_relocs.c) compiles clean on the guest (build.log) and performs the ioctl; on this guest it exits at the /dev/dri open (run.log: "No such file or directory ... this guest has no radeon GPU"); pciconf lists zero 1002: devices. Execution requires radeon hardware with the VCE block (SI/CIK asics) β not present in the QEMU guest, matching the audit precedent for GPU findings (cf. DF-4447). Deterministic panic when run on affected hardware; no memory-corruption primitive (NULL fixed offset read on x86-64 with the NULL page unmapped β page fault β panic).
Fix
fix.diff adds the same guard radeon_uvd_cs_parse has, at the head of radeon_vce_cs_parse. fix_status=not_testable (no radeon GPU on the audit guest).
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 (UVD-equivalent guard); no radeon hardware on the audit guest, so no kernel build/run validation is possible.
findings/poc/DF-4608/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-4608/VERDICT.md (six-step path:line proof chain); vce_null_relocs.c (raw-ioctl trigger, no libdrm); build.log (clean guest build); run.log (hardware-absence proof)
PoC changes
n/a (first evidence pack; trigger authored from scratch against the DF radeon source with hand-rolled _IOWR ioctl, no libdrm)
Verified recommended fix
Add 'if (p->chunk_relocs == NULL) { DRM_ERROR("No relocation chunk !\n"); return -EINVAL; }' at the head of radeon_vce_cs_parse(), mirroring radeon_uvd.c:705-709 (see fix.diff).
Verdict
radeon_vce_cs_reloc() dereferences p->chunk_relocs (NULL when the DRM_IOCTL_RADEON_CS submission omits the RADEON_CHUNK_ID_RELOCS chunk, which radeon_cs_parser_init accepts and radeon_cs_parser_relocs silently tolerates by returning 0) at radeon_vce.c:482 -> fixed-offset NULL read -> kernel panic. Deterministic single-ioctl local DoS for any user with DRM node access on SI/CIK radeon hardware with VCE. Both sibling paths guard this exact case (generic radeon_cs.c:846-850 and radeon_uvd.c:705-709 print 'No relocation chunk !' and return -EINVAL); the VCE parser simply forgot. The audit guest has no radeon GPU (no /dev/dri, zero 1002: PCI devices), so the trigger could not be executed -- trigger builds clean on the guest and is provided ready for affected hardware; code-trace certainty per VERDICT.md.
No comments yet.