| From a5f596830e27e15f7a0ecd6be55e433d776986d8 Mon Sep 17 00:00:00 2001 |
| From: Pete Zaitcev <zaitcev@kotori.zaitcev.us> |
| Date: Fri, 9 Mar 2018 00:21:14 -0600 |
| Subject: usb: usbmon: Read text within supplied buffer size |
| |
| From: Pete Zaitcev <zaitcev@kotori.zaitcev.us> |
| |
| commit a5f596830e27e15f7a0ecd6be55e433d776986d8 upstream. |
| |
| This change fixes buffer overflows and silent data corruption with the |
| usbmon device driver text file read operations. |
| |
| Signed-off-by: Fredrik Noring <noring@nocrew.org> |
| Signed-off-by: Pete Zaitcev <zaitcev@redhat.com> |
| Cc: stable <stable@vger.kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/mon/mon_text.c | 124 +++++++++++++++++++++++++++------------------ |
| 1 file changed, 77 insertions(+), 47 deletions(-) |
| |
| --- a/drivers/usb/mon/mon_text.c |
| +++ b/drivers/usb/mon/mon_text.c |
| @@ -85,6 +85,8 @@ struct mon_reader_text { |
| |
| wait_queue_head_t wait; |
| int printf_size; |
| + size_t printf_offset; |
| + size_t printf_togo; |
| char *printf_buf; |
| struct mutex printf_lock; |
| |
| @@ -376,75 +378,103 @@ err_alloc: |
| return rc; |
| } |
| |
| -/* |
| - * For simplicity, we read one record in one system call and throw out |
| - * what does not fit. This means that the following does not work: |
| - * dd if=/dbg/usbmon/0t bs=10 |
| - * Also, we do not allow seeks and do not bother advancing the offset. |
| - */ |
| +static ssize_t mon_text_copy_to_user(struct mon_reader_text *rp, |
| + char __user * const buf, const size_t nbytes) |
| +{ |
| + const size_t togo = min(nbytes, rp->printf_togo); |
| + |
| + if (copy_to_user(buf, &rp->printf_buf[rp->printf_offset], togo)) |
| + return -EFAULT; |
| + rp->printf_togo -= togo; |
| + rp->printf_offset += togo; |
| + return togo; |
| +} |
| + |
| +/* ppos is not advanced since the llseek operation is not permitted. */ |
| static ssize_t mon_text_read_t(struct file *file, char __user *buf, |
| - size_t nbytes, loff_t *ppos) |
| + size_t nbytes, loff_t *ppos) |
| { |
| struct mon_reader_text *rp = file->private_data; |
| struct mon_event_text *ep; |
| struct mon_text_ptr ptr; |
| + ssize_t ret; |
| |
| - ep = mon_text_read_wait(rp, file); |
| - if (IS_ERR(ep)) |
| - return PTR_ERR(ep); |
| mutex_lock(&rp->printf_lock); |
| - ptr.cnt = 0; |
| - ptr.pbuf = rp->printf_buf; |
| - ptr.limit = rp->printf_size; |
| - |
| - mon_text_read_head_t(rp, &ptr, ep); |
| - mon_text_read_statset(rp, &ptr, ep); |
| - ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, |
| - " %d", ep->length); |
| - mon_text_read_data(rp, &ptr, ep); |
| |
| - if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) |
| - ptr.cnt = -EFAULT; |
| + if (rp->printf_togo == 0) { |
| + |
| + ep = mon_text_read_wait(rp, file); |
| + if (IS_ERR(ep)) { |
| + mutex_unlock(&rp->printf_lock); |
| + return PTR_ERR(ep); |
| + } |
| + ptr.cnt = 0; |
| + ptr.pbuf = rp->printf_buf; |
| + ptr.limit = rp->printf_size; |
| + |
| + mon_text_read_head_t(rp, &ptr, ep); |
| + mon_text_read_statset(rp, &ptr, ep); |
| + ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, |
| + " %d", ep->length); |
| + mon_text_read_data(rp, &ptr, ep); |
| + |
| + rp->printf_togo = ptr.cnt; |
| + rp->printf_offset = 0; |
| + |
| + kmem_cache_free(rp->e_slab, ep); |
| + } |
| + |
| + ret = mon_text_copy_to_user(rp, buf, nbytes); |
| mutex_unlock(&rp->printf_lock); |
| - kmem_cache_free(rp->e_slab, ep); |
| - return ptr.cnt; |
| + return ret; |
| } |
| |
| +/* ppos is not advanced since the llseek operation is not permitted. */ |
| static ssize_t mon_text_read_u(struct file *file, char __user *buf, |
| - size_t nbytes, loff_t *ppos) |
| + size_t nbytes, loff_t *ppos) |
| { |
| struct mon_reader_text *rp = file->private_data; |
| struct mon_event_text *ep; |
| struct mon_text_ptr ptr; |
| + ssize_t ret; |
| |
| - ep = mon_text_read_wait(rp, file); |
| - if (IS_ERR(ep)) |
| - return PTR_ERR(ep); |
| mutex_lock(&rp->printf_lock); |
| - ptr.cnt = 0; |
| - ptr.pbuf = rp->printf_buf; |
| - ptr.limit = rp->printf_size; |
| |
| - mon_text_read_head_u(rp, &ptr, ep); |
| - if (ep->type == 'E') { |
| - mon_text_read_statset(rp, &ptr, ep); |
| - } else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) { |
| - mon_text_read_isostat(rp, &ptr, ep); |
| - mon_text_read_isodesc(rp, &ptr, ep); |
| - } else if (ep->xfertype == USB_ENDPOINT_XFER_INT) { |
| - mon_text_read_intstat(rp, &ptr, ep); |
| - } else { |
| - mon_text_read_statset(rp, &ptr, ep); |
| + if (rp->printf_togo == 0) { |
| + |
| + ep = mon_text_read_wait(rp, file); |
| + if (IS_ERR(ep)) { |
| + mutex_unlock(&rp->printf_lock); |
| + return PTR_ERR(ep); |
| + } |
| + ptr.cnt = 0; |
| + ptr.pbuf = rp->printf_buf; |
| + ptr.limit = rp->printf_size; |
| + |
| + mon_text_read_head_u(rp, &ptr, ep); |
| + if (ep->type == 'E') { |
| + mon_text_read_statset(rp, &ptr, ep); |
| + } else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) { |
| + mon_text_read_isostat(rp, &ptr, ep); |
| + mon_text_read_isodesc(rp, &ptr, ep); |
| + } else if (ep->xfertype == USB_ENDPOINT_XFER_INT) { |
| + mon_text_read_intstat(rp, &ptr, ep); |
| + } else { |
| + mon_text_read_statset(rp, &ptr, ep); |
| + } |
| + ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, |
| + " %d", ep->length); |
| + mon_text_read_data(rp, &ptr, ep); |
| + |
| + rp->printf_togo = ptr.cnt; |
| + rp->printf_offset = 0; |
| + |
| + kmem_cache_free(rp->e_slab, ep); |
| } |
| - ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, |
| - " %d", ep->length); |
| - mon_text_read_data(rp, &ptr, ep); |
| |
| - if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) |
| - ptr.cnt = -EFAULT; |
| + ret = mon_text_copy_to_user(rp, buf, nbytes); |
| mutex_unlock(&rp->printf_lock); |
| - kmem_cache_free(rp->e_slab, ep); |
| - return ptr.cnt; |
| + return ret; |
| } |
| |
| static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp, |