Commit 84dcd594 authored by Stephen Ware's avatar Stephen Ware Committed by Greg Kroah-Hartman

USB: fix up problems in the vtusb driver

Add range check on buffer sizes passed in from user space
(max is 8*PAGE_SIZE) which will work for the most common
spectrometers even at pages as small as 1K.

Add kref to vst device structure to preserve reference to the
usb object until we truly are done with it.

From: Stephen Ware <stephen.ware@eqware.net>
From: Dennis O'Brien <dennis.obrien@eqware.net>
Signed-off-by: default avatarDennis O'Brien <dennis.obrien@eqware.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 71b7497c
...@@ -59,6 +59,8 @@ ...@@ -59,6 +59,8 @@
#define USB_PRODUCT_LABPRO 0x0001 #define USB_PRODUCT_LABPRO 0x0001
#define USB_PRODUCT_LABQUEST 0x0005 #define USB_PRODUCT_LABQUEST 0x0005
#define VST_MAXBUFFER (64*1024)
static struct usb_device_id id_table[] = { static struct usb_device_id id_table[] = {
{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB2000)}, { USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB2000)},
{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_HR4000)}, { USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_HR4000)},
...@@ -73,6 +75,7 @@ static struct usb_device_id id_table[] = { ...@@ -73,6 +75,7 @@ static struct usb_device_id id_table[] = {
MODULE_DEVICE_TABLE(usb, id_table); MODULE_DEVICE_TABLE(usb, id_table);
struct vstusb_device { struct vstusb_device {
struct kref kref;
struct mutex lock; struct mutex lock;
struct usb_device *usb_dev; struct usb_device *usb_dev;
char present; char present;
...@@ -83,9 +86,18 @@ struct vstusb_device { ...@@ -83,9 +86,18 @@ struct vstusb_device {
int wr_pipe; int wr_pipe;
int wr_timeout_ms; int wr_timeout_ms;
}; };
#define to_vst_dev(d) container_of(d, struct vstusb_device, kref)
static struct usb_driver vstusb_driver; static struct usb_driver vstusb_driver;
static void vstusb_delete(struct kref *kref)
{
struct vstusb_device *vstdev = to_vst_dev(kref);
usb_put_dev(vstdev->usb_dev);
kfree(vstdev);
}
static int vstusb_open(struct inode *inode, struct file *file) static int vstusb_open(struct inode *inode, struct file *file)
{ {
struct vstusb_device *vstdev; struct vstusb_device *vstdev;
...@@ -114,6 +126,9 @@ static int vstusb_open(struct inode *inode, struct file *file) ...@@ -114,6 +126,9 @@ static int vstusb_open(struct inode *inode, struct file *file)
return -EBUSY; return -EBUSY;
} }
/* increment our usage count */
kref_get(&vstdev->kref);
vstdev->isopen = 1; vstdev->isopen = 1;
/* save device in the file's private structure */ /* save device in the file's private structure */
...@@ -126,7 +141,7 @@ static int vstusb_open(struct inode *inode, struct file *file) ...@@ -126,7 +141,7 @@ static int vstusb_open(struct inode *inode, struct file *file)
return 0; return 0;
} }
static int vstusb_close(struct inode *inode, struct file *file) static int vstusb_release(struct inode *inode, struct file *file)
{ {
struct vstusb_device *vstdev; struct vstusb_device *vstdev;
...@@ -138,15 +153,13 @@ static int vstusb_close(struct inode *inode, struct file *file) ...@@ -138,15 +153,13 @@ static int vstusb_close(struct inode *inode, struct file *file)
mutex_lock(&vstdev->lock); mutex_lock(&vstdev->lock);
vstdev->isopen = 0; vstdev->isopen = 0;
file->private_data = NULL;
/* if device is no longer present */ dev_dbg(&vstdev->usb_dev->dev, "%s: released\n", __func__);
if (!vstdev->present) {
mutex_unlock(&vstdev->lock);
kfree(vstdev);
} else
mutex_unlock(&vstdev->lock); mutex_unlock(&vstdev->lock);
kref_put(&vstdev->kref, vstusb_delete);
return 0; return 0;
} }
...@@ -268,7 +281,7 @@ static ssize_t vstusb_read(struct file *file, char __user *buffer, ...@@ -268,7 +281,7 @@ static ssize_t vstusb_read(struct file *file, char __user *buffer,
return -ENODEV; return -ENODEV;
/* verify that we actually want to read some data */ /* verify that we actually want to read some data */
if (count == 0) if ((count == 0) || (count > VST_MAXBUFFER))
return -EINVAL; return -EINVAL;
/* lock this object */ /* lock this object */
...@@ -354,7 +367,7 @@ static ssize_t vstusb_write(struct file *file, const char __user *buffer, ...@@ -354,7 +367,7 @@ static ssize_t vstusb_write(struct file *file, const char __user *buffer,
return -ENODEV; return -ENODEV;
/* verify that we actually have some data to write */ /* verify that we actually have some data to write */
if (count == 0) if ((count == 0) || (count > VST_MAXBUFFER))
return retval; return retval;
/* lock this object */ /* lock this object */
...@@ -498,7 +511,7 @@ static long vstusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -498,7 +511,7 @@ static long vstusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case IOCTL_VSTUSB_SEND_PIPE: case IOCTL_VSTUSB_SEND_PIPE:
if (usb_data.count == 0) { if ((usb_data.count == 0) || (usb_data.count > VST_MAXBUFFER)) {
mutex_unlock(&vstdev->lock); mutex_unlock(&vstdev->lock);
retval = -EINVAL; retval = -EINVAL;
goto exit; goto exit;
...@@ -551,7 +564,7 @@ static long vstusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -551,7 +564,7 @@ static long vstusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break; break;
case IOCTL_VSTUSB_RECV_PIPE: case IOCTL_VSTUSB_RECV_PIPE:
if (usb_data.count == 0) { if ((usb_data.count == 0) || (usb_data.count > VST_MAXBUFFER)) {
mutex_unlock(&vstdev->lock); mutex_unlock(&vstdev->lock);
retval = -EINVAL; retval = -EINVAL;
goto exit; goto exit;
...@@ -633,7 +646,7 @@ static const struct file_operations vstusb_fops = { ...@@ -633,7 +646,7 @@ static const struct file_operations vstusb_fops = {
.unlocked_ioctl = vstusb_ioctl, .unlocked_ioctl = vstusb_ioctl,
.compat_ioctl = vstusb_ioctl, .compat_ioctl = vstusb_ioctl,
.open = vstusb_open, .open = vstusb_open,
.release = vstusb_close, .release = vstusb_release,
}; };
static struct usb_class_driver usb_vstusb_class = { static struct usb_class_driver usb_vstusb_class = {
...@@ -656,6 +669,10 @@ static int vstusb_probe(struct usb_interface *intf, ...@@ -656,6 +669,10 @@ static int vstusb_probe(struct usb_interface *intf,
if (vstdev == NULL) if (vstdev == NULL)
return -ENOMEM; return -ENOMEM;
/* must do usb_get_dev() prior to kref_init() since the kref_put()
* release function will do a usb_put_dev() */
usb_get_dev(dev);
kref_init(&vstdev->kref);
mutex_init(&vstdev->lock); mutex_init(&vstdev->lock);
i = dev->descriptor.bcdDevice; i = dev->descriptor.bcdDevice;
...@@ -676,7 +693,7 @@ static int vstusb_probe(struct usb_interface *intf, ...@@ -676,7 +693,7 @@ static int vstusb_probe(struct usb_interface *intf,
"%s: Not able to get a minor for this device.\n", "%s: Not able to get a minor for this device.\n",
__func__); __func__);
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
kfree(vstdev); kref_put(&vstdev->kref, vstusb_delete);
return retval; return retval;
} }
...@@ -704,14 +721,11 @@ static void vstusb_disconnect(struct usb_interface *intf) ...@@ -704,14 +721,11 @@ static void vstusb_disconnect(struct usb_interface *intf)
usb_kill_anchored_urbs(&vstdev->submitted); usb_kill_anchored_urbs(&vstdev->submitted);
/* if the device is not opened, then we clean up right now */
if (!vstdev->isopen) {
mutex_unlock(&vstdev->lock);
kfree(vstdev);
} else
mutex_unlock(&vstdev->lock); mutex_unlock(&vstdev->lock);
kref_put(&vstdev->kref, vstusb_delete);
} }
} }
static int vstusb_suspend(struct usb_interface *intf, pm_message_t message) static int vstusb_suspend(struct usb_interface *intf, pm_message_t message)
......
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