diff options
author | Guus Sliepen <guus@sliepen.org> | 2009-07-22 11:39:42 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-23 09:46:30 -0400 |
commit | 665d7662d15441b4b3e54131a9418a1a198d0d31 (patch) | |
tree | 029c210e11d5f9befee6a51457a9dcd79b7934b6 /drivers/usb/class/usbtmc.c | |
parent | a9d43091c5be1e7a60d5abe84be4f3050236b26a (diff) |
USB: usbtmc: sanity checks for DEV_DEP_MSG_IN urbs
According to the specifications, an instrument should not return more data in a
DEV_DEP_MSG_IN urb than requested. However, some instruments can send more
than requested. This could cause the kernel to write the extra data past the
end of the buffer provided by read().
Fix this by checking that the value of the TranserSize field is not larger than
the urb itself and not larger than the size of the userspace buffer. Also
correctly decrement the remaining size of the buffer when userspace read()s
more than USBTMC_SIZE_IOBUFFER.
Signed-off-by: Guus Sliepen <guus@sliepen.org>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/class/usbtmc.c')
-rw-r--r-- | drivers/usb/class/usbtmc.c | 22 |
1 files changed, 18 insertions, 4 deletions
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 4f0858fbf980..5bab8e596c88 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c | |||
@@ -369,13 +369,13 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, | |||
369 | { | 369 | { |
370 | struct usbtmc_device_data *data; | 370 | struct usbtmc_device_data *data; |
371 | struct device *dev; | 371 | struct device *dev; |
372 | unsigned long int n_characters; | 372 | u32 n_characters; |
373 | u8 *buffer; | 373 | u8 *buffer; |
374 | int actual; | 374 | int actual; |
375 | int done; | 375 | size_t done; |
376 | int remaining; | 376 | size_t remaining; |
377 | int retval; | 377 | int retval; |
378 | int this_part; | 378 | size_t this_part; |
379 | 379 | ||
380 | /* Get pointer to private data structure */ | 380 | /* Get pointer to private data structure */ |
381 | data = filp->private_data; | 381 | data = filp->private_data; |
@@ -461,6 +461,18 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, | |||
461 | (buffer[6] << 16) + | 461 | (buffer[6] << 16) + |
462 | (buffer[7] << 24); | 462 | (buffer[7] << 24); |
463 | 463 | ||
464 | /* Ensure the instrument doesn't lie about it */ | ||
465 | if(n_characters > actual - 12) { | ||
466 | dev_err(dev, "Device lies about message size: %zu > %zu\n", n_characters, actual - 12); | ||
467 | n_characters = actual - 12; | ||
468 | } | ||
469 | |||
470 | /* Ensure the instrument doesn't send more back than requested */ | ||
471 | if(n_characters > this_part) { | ||
472 | dev_err(dev, "Device returns more than requested: %zu > %zu\n", done + n_characters, done + this_part); | ||
473 | n_characters = this_part; | ||
474 | } | ||
475 | |||
464 | /* Copy buffer to user space */ | 476 | /* Copy buffer to user space */ |
465 | if (copy_to_user(buf + done, &buffer[12], n_characters)) { | 477 | if (copy_to_user(buf + done, &buffer[12], n_characters)) { |
466 | /* There must have been an addressing problem */ | 478 | /* There must have been an addressing problem */ |
@@ -471,6 +483,8 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, | |||
471 | done += n_characters; | 483 | done += n_characters; |
472 | if (n_characters < USBTMC_SIZE_IOBUFFER) | 484 | if (n_characters < USBTMC_SIZE_IOBUFFER) |
473 | remaining = 0; | 485 | remaining = 0; |
486 | else | ||
487 | remaining -= n_characters; | ||
474 | } | 488 | } |
475 | 489 | ||
476 | /* Update file position value */ | 490 | /* Update file position value */ |