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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3839 Β· 11 files
FileTypeDescriptionSize
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
README.md file
↓ download 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.

VERDICT.md file
↓ download raw

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_testable
baseline no→ patch + rebuild →patched clean

Fix 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).
↓ fix.diffper-fix-DF-3839

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.