DF-1066 / fix.diff
diff --git a/sys/bus/u4b/uvc/uvc_buf.c b/sys/bus/u4b/uvc/uvc_buf.c --- a/sys/bus/u4b/uvc/uvc_buf.c +++ b/sys/bus/u4b/uvc/uvc_buf.c @@ -104,22 +104,35 @@ static __inline int uvc_buf_queue_mmap_locked(struct uvc_buf_queue *bq, vm_paddr_t *paddr, vm_offset_t offset) { - uint64_t max_offset = (uint64_t)(bq->buf_size * bq->buf_count) - PAGE_SIZE; + uint64_t total, max_offset; - if (bq->mem == NULL) + /* + * Reject the degenerate queue state outright. buf_size==0 (reachable + * when REQBUFS was called with len==0, e.g. a camera probe returning + * dwMaxFrameSize==0) used to make the old + * "(uint64_t)(buf_size*buf_count) - PAGE_SIZE" subtraction underflow to + * 0xFFFFFFFFFFFFF000, defeating the offset bounds check below and + * turning vtophys(bq->mem + offset) into an arbitrary kernel-VA -> + * physical-page translator. The old "offset < 0" / "max_offset < 0" + * checks were dead code (both operands are unsigned). + */ + if (bq->mem == NULL || bq->buf_size == 0 || bq->buf_count == 0) return EINVAL; - if (offset < 0) + /* Guard the multiplication against uint64 overflow. */ + if (bq->buf_size > UINT64_MAX / bq->buf_count) return EINVAL; - if (max_offset < 0) + total = bq->buf_size * bq->buf_count; + if (total < (uint64_t)PAGE_SIZE) return EINVAL; + max_offset = total - PAGE_SIZE; - if (offset > max_offset) { - kprintf("WARNING: %s offset out of bound: %ld\n; max: %ld", + if ((uint64_t)offset > max_offset) { + kprintf("WARNING: %s offset out of bound: %ld\n; max: %lu", __func__, offset, - max_offset); + (unsigned long)max_offset); return EINVAL; } @@ -543,6 +556,10 @@ unsigned long rl; DPRINTF("%s\n", __func__); + /* Reject a zero frame size: it would yield buf_size==0, which is not a + * meaningful buffer and used to enable the mmap offset underflow. */ + if (len == 0) + return EINVAL; rl = round_page(len); DPRINTF("page align size:%lu-real size:%u\n", rl, len); |