Skip to content

Commit

Permalink
usb: usbmon: Read text within supplied buffer size
Browse files Browse the repository at this point in the history
commit a5f5968 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>
  • Loading branch information
Pete Zaitcev authored and Greg Kroah-Hartman committed Mar 18, 2018
1 parent c3d114a commit d6d68ab
Showing 1 changed file with 78 additions and 48 deletions.
126 changes: 78 additions & 48 deletions drivers/usb/mon/mon_text.c
Original file line number Diff line number Diff line change
@@ -82,6 +82,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;

@@ -373,75 +375,103 @@ static int mon_text_open(struct inode *inode, struct file *file)
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,

0 comments on commit d6d68ab

Please sign in to comment.