Commit 68cc5946 authored by Pete Zaitcev's avatar Pete Zaitcev Committed by Stefan Bader

usb: usbmon: Read text within supplied buffer size

BugLink: http://bugs.launchpad.net/bugs/1764627

commit a5f59683 upstream.

This change fixes buffer overflows and silent data corruption with the
usbmon device driver text file read operations.
Signed-off-by: default avatarFredrik Noring <noring@nocrew.org>
Signed-off-by: default avatarPete Zaitcev <zaitcev@redhat.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarJuerg Haefliger <juergh@canonical.com>
Signed-off-by: default avatarStefan Bader <stefan.bader@canonical.com>
parent 1577e6c4
...@@ -82,6 +82,8 @@ struct mon_reader_text { ...@@ -82,6 +82,8 @@ struct mon_reader_text {
wait_queue_head_t wait; wait_queue_head_t wait;
int printf_size; int printf_size;
size_t printf_offset;
size_t printf_togo;
char *printf_buf; char *printf_buf;
struct mutex printf_lock; struct mutex printf_lock;
...@@ -373,75 +375,103 @@ static int mon_text_open(struct inode *inode, struct file *file) ...@@ -373,75 +375,103 @@ static int mon_text_open(struct inode *inode, struct file *file)
return rc; return rc;
} }
/* static ssize_t mon_text_copy_to_user(struct mon_reader_text *rp,
* For simplicity, we read one record in one system call and throw out char __user * const buf, const size_t nbytes)
* what does not fit. This means that the following does not work: {
* dd if=/dbg/usbmon/0t bs=10 const size_t togo = min(nbytes, rp->printf_togo);
* Also, we do not allow seeks and do not bother advancing the offset.
*/ 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, 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_reader_text *rp = file->private_data;
struct mon_event_text *ep; struct mon_event_text *ep;
struct mon_text_ptr ptr; 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); mutex_lock(&rp->printf_lock);
ptr.cnt = 0;
ptr.pbuf = rp->printf_buf; if (rp->printf_togo == 0) {
ptr.limit = rp->printf_size;
ep = mon_text_read_wait(rp, file);
mon_text_read_head_t(rp, &ptr, ep); if (IS_ERR(ep)) {
mon_text_read_statset(rp, &ptr, ep); mutex_unlock(&rp->printf_lock);
ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, return PTR_ERR(ep);
" %d", ep->length); }
mon_text_read_data(rp, &ptr, ep); ptr.cnt = 0;
ptr.pbuf = rp->printf_buf;
if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) ptr.limit = rp->printf_size;
ptr.cnt = -EFAULT;
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); mutex_unlock(&rp->printf_lock);
kmem_cache_free(rp->e_slab, ep); return ret;
return ptr.cnt;
} }
/* ppos is not advanced since the llseek operation is not permitted. */
static ssize_t mon_text_read_u(struct file *file, char __user *buf, 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_reader_text *rp = file->private_data;
struct mon_event_text *ep; struct mon_event_text *ep;
struct mon_text_ptr ptr; 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); 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 (rp->printf_togo == 0) {
if (ep->type == 'E') {
mon_text_read_statset(rp, &ptr, ep); ep = mon_text_read_wait(rp, file);
} else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) { if (IS_ERR(ep)) {
mon_text_read_isostat(rp, &ptr, ep); mutex_unlock(&rp->printf_lock);
mon_text_read_isodesc(rp, &ptr, ep); return PTR_ERR(ep);
} else if (ep->xfertype == USB_ENDPOINT_XFER_INT) { }
mon_text_read_intstat(rp, &ptr, ep); ptr.cnt = 0;
} else { ptr.pbuf = rp->printf_buf;
mon_text_read_statset(rp, &ptr, ep); 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)) ret = mon_text_copy_to_user(rp, buf, nbytes);
ptr.cnt = -EFAULT;
mutex_unlock(&rp->printf_lock); mutex_unlock(&rp->printf_lock);
kmem_cache_free(rp->e_slab, ep); return ret;
return ptr.cnt;
} }
static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp, static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment