SET_RESOURCE SQ_TEX_VTX_VALID_BUFFER bound-check bypass: (size + offset) added in u32 (+ forced-size underflow) β GPU vertex fetch up to ~4 GB past the BO
Summary
In PACKET3_SET_RESOURCE with SQ_TEX_VTX_VALID_BUFFER, offset and size are raw u32 IB dwords and the BO-bound comparison (size + offset) > radeon_bo_size(robj) is computed in u32: offset=0xfffffff0, size=0x20 wraps to 0x10 and passes for any BO; offset64 = reloc->gpu_offset + offset (u64) then programs a vertex buffer ~4 GB past the BO -> GPU OOB vertex fetch (cross-process GPU memory disclosure via rendered attributes). When the wrapped check does fire with offset > bo_size, the 'forced size' underflows to a huge value, enlarging the OOB window. Evergreen SET_RESOURCE instance of the DF-3766 family.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3839 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | file | 959 B | β raw | |
| VERDICT.md | file | 2.0 KB | β raw | |
| build.log | file | 101 B | view raw | |
| build.sh | file | 82 B | view raw | |
| env.txt | file | 286 B | view raw | |
| fix.diff | file | 628 B | view raw | |
| manifest.json | file | 579 B | view raw | |
| run.log | file | 959 B | view raw | |
| run.sh | file | 105 B | view raw | |
| trigger.c | file | 3.0 KB | view raw | |
| verdict.json | file | 2.8 KB | view raw |
DF-3839 β SET_RESOURCE (SQ_TEX_VTX_VALID_BUFFER) bound check bypass:
(size + offset) added in u32 at evergreen_cs.c:2416 (+ forced-size
underflow at :2419) -> GPU vertex fetch reads up to ~4 GB past the BO.
WHAT TO LOOK AT trigger.c SET_RESOURCE with word0=offset=0xfffffff0, word1=size=0x20: (size+offset) wraps to 0x10 -> "vbo resource seems too big" never fires -> GPU buffer base = gpu_offset + 0xfffffff0. fix.diff u64 promotion + underflow guard. verdict.json status inconclusive (no GPU on guest), source-level certain.
BUILD cc -O2 -o trigger trigger.c RUN ./trigger /dev/dri/card0 EXPECT On real pre-SI radeon HW the CS is accepted (no "vbo resource seems too big for the bo" in dmesg) although the buffer points far past the relocated BO. On the audit QEMU guest: ENOENT.
FAMILY DF-3766 (r100 INDX_BUFFER (value+1) wrap) β evergreen SET_RESOURCE instance.
DF-3839 VERDICT β status: inconclusive (hardware-gated), source-level: CERTAIN
Chain:
1. DRM-authenticated user, non-VM CS -> evergreen_packet3_check()
PACKET3_SET_RESOURCE (evergreen_cs.c:2332).
2. Resource word7 type == SQ_TEX_VTX_VALID_BUFFER (2405).
3. :2414-2415 offset, size = raw u32 IB dwords.
4. :2416 if (p->rdev && (size + offset) > radeon_bo_size(robj))
β the addition happens in u32: offset=0xfffffff0, size=0x20 wraps to
0x10 which is <= any BO size -> check passes silently.
5. :2422-2425 offset64 = reloc->gpu_offset + offset (u64!) is programmed
into the resource: the GPU vertex/index fetch now reads from
bo_base + 0xfffffff0 β a 40-bit GPU address ~4 GB past the BO, i.e.
unrelated GTT/VRAM allocations (other processes' buffers). Fetched
data is returned as vertex attributes and can be rendered into an
attacker-readable target -> cross-process GPU memory disclosure.
6. Secondary: when the wrapped check does fire with offset > bo_size,
:2419 ib[...] = radeon_bo_size(robj) - offset underflows to a huge
forced size, making the OOB window larger still.
Why not reproduced: guest has no DRI device (run.log: ENOENT; build.log:
clean compile). The wrap (uint32)0xfffffff0 + 0x20 == 0x10 is arithmetic
fact, not runtime-dependent.
Severity: Medium β matches DF-3766 (r100 sibling, Medium/certain): an OOB read primitive on the GPU from an unprivileged DRM client.
Fix: fix.diff β widen the comparison to u64 and reject offset >= bo_size before the forced-size write (which would underflow). Fix validation: not possible on this guest β fix_status not_applicable.
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 validation impossible on this guest: the vulnerable device (AMD radeon CS / Intel 2200BG) does not exist, so neither baseline nor patched kernel can exercise the path. fix.diff authored against the read-only sys/ tree, never applied.
fix.diff in this pack; structurally mirrors the bounds checks the same driver family already enforces elsewhere (radeon_cs_packet_parse fit check; iwn ring-index masking).
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md (what to look at), VERDICT.md (line-by-line trace), trigger.c/mkbadfw.c, build.log (clean guest compile, gcc 8.3), run.log (honest ENOENT / generator RUN_OK), env.txt (guest has no DRI, no iwi NIC), fix.diff
PoC changes
PoC authored fresh for this finding (cross-file family seed): raw DRM ioctl ABI (cmd low byte 0x66 dispatches RADEON_CS on DF drm_ioctl.c:809), chunk structs and packet encodings verified against sys/dev/drm/include/uapi/drm/radeon_drm.h; iwi mkbadfw.c emits the v3 combined-image header per if_iwireg.h:161-166.
Verified recommended fix
Widen the (size+offset) comparison to u64 and reject offset >= bo_size before the forced-size write.
Verdict
Source-level certain, runtime inconclusive (guest virtio-only, ENOENT in run.log; clean compile in build.log). offset=0xfffffff0/size=0x20 wraps the u32 bound check to 0x10 so it passes for any BO, then offset64 = reloc->gpu_offset + offset programs a vertex buffer ~4GB past the BO (u64, no wrap on the GPU side) -> cross-process GPU memory disclosure via vertex fetch; when the wrapped check fires with offset > bo_size, the forced size at :2419 underflows to a huge value. Evergreen SET_RESOURCE instance of the DF-3766 (r100 INDX_BUFFER +1 wrap) family.
No comments yet.