Skip to content

Commit a5f5968

Browse files
zaitcevgregkh
authored andcommitted
usb: usbmon: Read text within supplied buffer size
This change fixes buffer overflows and silent data corruption with the usbmon device driver text file read operations. Signed-off-by: Fredrik Noring <[email protected]> Signed-off-by: Pete Zaitcev <[email protected]> Cc: stable <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 015dbeb commit a5f5968

File tree

1 file changed

+78
-48
lines changed

1 file changed

+78
-48
lines changed

drivers/usb/mon/mon_text.c

Lines changed: 78 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ struct mon_reader_text {
8585

8686
wait_queue_head_t wait;
8787
int printf_size;
88+
size_t printf_offset;
89+
size_t printf_togo;
8890
char *printf_buf;
8991
struct mutex printf_lock;
9092

@@ -376,75 +378,103 @@ static int mon_text_open(struct inode *inode, struct file *file)
376378
return rc;
377379
}
378380

379-
/*
380-
* For simplicity, we read one record in one system call and throw out
381-
* what does not fit. This means that the following does not work:
382-
* dd if=/dbg/usbmon/0t bs=10
383-
* Also, we do not allow seeks and do not bother advancing the offset.
384-
*/
381+
static ssize_t mon_text_copy_to_user(struct mon_reader_text *rp,
382+
char __user * const buf, const size_t nbytes)
383+
{
384+
const size_t togo = min(nbytes, rp->printf_togo);
385+
386+
if (copy_to_user(buf, &rp->printf_buf[rp->printf_offset], togo))
387+
return -EFAULT;
388+
rp->printf_togo -= togo;
389+
rp->printf_offset += togo;
390+
return togo;
391+
}
392+
393+
/* ppos is not advanced since the llseek operation is not permitted. */
385394
static ssize_t mon_text_read_t(struct file *file, char __user *buf,
386-
size_t nbytes, loff_t *ppos)
395+
size_t nbytes, loff_t *ppos)
387396
{
388397
struct mon_reader_text *rp = file->private_data;
389398
struct mon_event_text *ep;
390399
struct mon_text_ptr ptr;
400+
ssize_t ret;
391401

392-
ep = mon_text_read_wait(rp, file);
393-
if (IS_ERR(ep))
394-
return PTR_ERR(ep);
395402
mutex_lock(&rp->printf_lock);
396-
ptr.cnt = 0;
397-
ptr.pbuf = rp->printf_buf;
398-
ptr.limit = rp->printf_size;
399-
400-
mon_text_read_head_t(rp, &ptr, ep);
401-
mon_text_read_statset(rp, &ptr, ep);
402-
ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
403-
" %d", ep->length);
404-
mon_text_read_data(rp, &ptr, ep);
405-
406-
if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
407-
ptr.cnt = -EFAULT;
403+
404+
if (rp->printf_togo == 0) {
405+
406+
ep = mon_text_read_wait(rp, file);
407+
if (IS_ERR(ep)) {
408+
mutex_unlock(&rp->printf_lock);
409+
return PTR_ERR(ep);
410+
}
411+
ptr.cnt = 0;
412+
ptr.pbuf = rp->printf_buf;
413+
ptr.limit = rp->printf_size;
414+
415+
mon_text_read_head_t(rp, &ptr, ep);
416+
mon_text_read_statset(rp, &ptr, ep);
417+
ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
418+
" %d", ep->length);
419+
mon_text_read_data(rp, &ptr, ep);
420+
421+
rp->printf_togo = ptr.cnt;
422+
rp->printf_offset = 0;
423+
424+
kmem_cache_free(rp->e_slab, ep);
425+
}
426+
427+
ret = mon_text_copy_to_user(rp, buf, nbytes);
408428
mutex_unlock(&rp->printf_lock);
409-
kmem_cache_free(rp->e_slab, ep);
410-
return ptr.cnt;
429+
return ret;
411430
}
412431

432+
/* ppos is not advanced since the llseek operation is not permitted. */
413433
static ssize_t mon_text_read_u(struct file *file, char __user *buf,
414-
size_t nbytes, loff_t *ppos)
434+
size_t nbytes, loff_t *ppos)
415435
{
416436
struct mon_reader_text *rp = file->private_data;
417437
struct mon_event_text *ep;
418438
struct mon_text_ptr ptr;
439+
ssize_t ret;
419440

420-
ep = mon_text_read_wait(rp, file);
421-
if (IS_ERR(ep))
422-
return PTR_ERR(ep);
423441
mutex_lock(&rp->printf_lock);
424-
ptr.cnt = 0;
425-
ptr.pbuf = rp->printf_buf;
426-
ptr.limit = rp->printf_size;
427442

428-
mon_text_read_head_u(rp, &ptr, ep);
429-
if (ep->type == 'E') {
430-
mon_text_read_statset(rp, &ptr, ep);
431-
} else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
432-
mon_text_read_isostat(rp, &ptr, ep);
433-
mon_text_read_isodesc(rp, &ptr, ep);
434-
} else if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
435-
mon_text_read_intstat(rp, &ptr, ep);
436-
} else {
437-
mon_text_read_statset(rp, &ptr, ep);
443+
if (rp->printf_togo == 0) {
444+
445+
ep = mon_text_read_wait(rp, file);
446+
if (IS_ERR(ep)) {
447+
mutex_unlock(&rp->printf_lock);
448+
return PTR_ERR(ep);
449+
}
450+
ptr.cnt = 0;
451+
ptr.pbuf = rp->printf_buf;
452+
ptr.limit = rp->printf_size;
453+
454+
mon_text_read_head_u(rp, &ptr, ep);
455+
if (ep->type == 'E') {
456+
mon_text_read_statset(rp, &ptr, ep);
457+
} else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
458+
mon_text_read_isostat(rp, &ptr, ep);
459+
mon_text_read_isodesc(rp, &ptr, ep);
460+
} else if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
461+
mon_text_read_intstat(rp, &ptr, ep);
462+
} else {
463+
mon_text_read_statset(rp, &ptr, ep);
464+
}
465+
ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
466+
" %d", ep->length);
467+
mon_text_read_data(rp, &ptr, ep);
468+
469+
rp->printf_togo = ptr.cnt;
470+
rp->printf_offset = 0;
471+
472+
kmem_cache_free(rp->e_slab, ep);
438473
}
439-
ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
440-
" %d", ep->length);
441-
mon_text_read_data(rp, &ptr, ep);
442474

443-
if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
444-
ptr.cnt = -EFAULT;
475+
ret = mon_text_copy_to_user(rp, buf, nbytes);
445476
mutex_unlock(&rp->printf_lock);
446-
kmem_cache_free(rp->e_slab, ep);
447-
return ptr.cnt;
477+
return ret;
448478
}
449479

450480
static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp,

0 commit comments

Comments
 (0)