diff options
Diffstat (limited to 'drivers/usb/core/devio.c')
-rw-r--r-- | drivers/usb/core/devio.c | 102 |
1 files changed, 56 insertions, 46 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 3f8e06279c92..a94c63bef632 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -47,6 +47,7 @@ #include <linux/usbdevice_fs.h> #include <linux/cdev.h> #include <linux/notifier.h> +#include <linux/security.h> #include <asm/uaccess.h> #include <asm/byteorder.h> #include <linux/moduleparam.h> @@ -58,6 +59,9 @@ #define USB_DEVICE_MAX USB_MAXBUS * 128 static struct class *usb_device_class; +/* Mutual exclusion for removal, open, and release */ +DEFINE_MUTEX(usbfs_mutex); + struct async { struct list_head asynclist; struct dev_state *ps; @@ -68,6 +72,7 @@ struct async { void __user *userbuffer; void __user *userurb; struct urb *urb; + u32 secid; }; static int usbfs_snoop = 0; @@ -85,9 +90,10 @@ MODULE_PARM_DESC (usbfs_snoop, "true to log all usbfs traffic"); #define MAX_USBFS_BUFFER_SIZE 16384 -static inline int connected (struct usb_device *dev) +static inline int connected (struct dev_state *ps) { - return dev->state != USB_STATE_NOTATTACHED; + return (!list_empty(&ps->list) && + ps->dev->state != USB_STATE_NOTATTACHED); } static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig) @@ -116,7 +122,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig) static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { - struct dev_state *ps = (struct dev_state *)file->private_data; + struct dev_state *ps = file->private_data; struct usb_device *dev = ps->dev; ssize_t ret = 0; unsigned len; @@ -125,7 +131,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l pos = *ppos; usb_lock_device(dev); - if (!connected(dev)) { + if (!connected(ps)) { ret = -ENODEV; goto err; } else if (pos < 0) { @@ -299,7 +305,7 @@ static void snoop_urb(struct urb *urb, void __user *userurb) static void async_completed(struct urb *urb, struct pt_regs *regs) { - struct async *as = (struct async *)urb->context; + struct async *as = urb->context; struct dev_state *ps = as->ps; struct siginfo sinfo; @@ -312,7 +318,7 @@ static void async_completed(struct urb *urb, struct pt_regs *regs) sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = as->userurb; kill_proc_info_as_uid(as->signr, &sinfo, as->pid, as->uid, - as->euid); + as->euid, as->secid); } snoop(&urb->dev->dev, "urb complete\n"); snoop_urb(urb, as->userurb); @@ -515,19 +521,19 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig static struct usb_device *usbdev_lookup_minor(int minor) { - struct device *device; - struct usb_device *udev = NULL; + struct class_device *class_dev; + struct usb_device *dev = NULL; down(&usb_device_class->sem); - list_for_each_entry(device, &usb_device_class->devices, node) { - if (device->devt == MKDEV(USB_DEVICE_MAJOR, minor)) { - udev = device->platform_data; + list_for_each_entry(class_dev, &usb_device_class->children, node) { + if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) { + dev = class_dev->class_data; break; } } up(&usb_device_class->sem); - return udev; + return dev; }; /* @@ -539,25 +545,25 @@ static int usbdev_open(struct inode *inode, struct file *file) struct dev_state *ps; int ret; - /* - * no locking necessary here, as chrdev_open has the kernel lock - * (still acquire the kernel lock for safety) - */ + /* Protect against simultaneous removal or release */ + mutex_lock(&usbfs_mutex); + ret = -ENOMEM; if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL))) - goto out_nolock; + goto out; - lock_kernel(); ret = -ENOENT; /* check if we are called from a real node or usbfs */ if (imajor(inode) == USB_DEVICE_MAJOR) dev = usbdev_lookup_minor(iminor(inode)); if (!dev) - dev = inode->u.generic_ip; - if (!dev) { - kfree(ps); + dev = inode->i_private; + if (!dev) goto out; - } + ret = usb_autoresume_device(dev, 1); + if (ret) + goto out; + usb_get_dev(dev); ret = 0; ps->dev = dev; @@ -572,34 +578,41 @@ static int usbdev_open(struct inode *inode, struct file *file) ps->disc_euid = current->euid; ps->disccontext = NULL; ps->ifclaimed = 0; + security_task_getsecid(current, &ps->secid); wmb(); list_add_tail(&ps->list, &dev->filelist); file->private_data = ps; out: - unlock_kernel(); - out_nolock: - return ret; + if (ret) + kfree(ps); + mutex_unlock(&usbfs_mutex); + return ret; } static int usbdev_release(struct inode *inode, struct file *file) { - struct dev_state *ps = (struct dev_state *)file->private_data; + struct dev_state *ps = file->private_data; struct usb_device *dev = ps->dev; unsigned int ifnum; usb_lock_device(dev); + + /* Protect against simultaneous open */ + mutex_lock(&usbfs_mutex); list_del_init(&ps->list); + mutex_unlock(&usbfs_mutex); + for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); ifnum++) { if (test_bit(ifnum, &ps->ifclaimed)) releaseintf(ps, ifnum); } destroy_all_async(ps); + usb_autosuspend_device(dev, 1); usb_unlock_device(dev); usb_put_dev(dev); - ps->dev = NULL; kfree(ps); - return 0; + return 0; } static int proc_control(struct dev_state *ps, void __user *arg) @@ -1053,6 +1066,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->pid = current->pid; as->uid = current->uid; as->euid = current->euid; + security_task_getsecid(current, &as->secid); if (!(uurb->endpoint & USB_DIR_IN)) { if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) { free_async(as); @@ -1078,9 +1092,7 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) if (copy_from_user(&uurb, arg, sizeof(uurb))) return -EFAULT; - return proc_do_submiturb(ps, &uurb, - (struct usbdevfs_iso_packet_desc __user *)uurb.iso_frame_desc, - arg); + return proc_do_submiturb(ps, &uurb, (((struct usbdevfs_urb __user *)arg)->iso_frame_desc), arg); } static int proc_unlinkurb(struct dev_state *ps, void __user *arg) @@ -1205,9 +1217,7 @@ static int proc_submiturb_compat(struct dev_state *ps, void __user *arg) if (get_urb32(&uurb,(struct usbdevfs_urb32 *)arg)) return -EFAULT; - return proc_do_submiturb(ps, &uurb, - (struct usbdevfs_iso_packet_desc __user *)uurb.iso_frame_desc, - arg); + return proc_do_submiturb(ps, &uurb, ((struct usbdevfs_urb32 __user *)arg)->iso_frame_desc, arg); } static int processcompl_compat(struct async *as, void __user * __user *arg) @@ -1322,7 +1332,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) } } - if (!connected(ps->dev)) { + if (!connected(ps)) { kfree(buf); return -ENODEV; } @@ -1349,7 +1359,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) /* let kernel drivers try to (re)bind to the interface */ case USBDEVFS_CONNECT: usb_unlock_device(ps->dev); - bus_rescan_devices(intf->dev.bus); + retval = bus_rescan_devices(intf->dev.bus); usb_lock_device(ps->dev); break; @@ -1413,7 +1423,7 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg) */ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct dev_state *ps = (struct dev_state *)file->private_data; + struct dev_state *ps = file->private_data; struct usb_device *dev = ps->dev; void __user *p = (void __user *)arg; int ret = -ENOTTY; @@ -1421,7 +1431,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd if (!(file->f_mode & FMODE_WRITE)) return -EPERM; usb_lock_device(dev); - if (!connected(dev)) { + if (!connected(ps)) { usb_unlock_device(dev); return -ENODEV; } @@ -1556,18 +1566,18 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd /* No kernel lock - fine */ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait) { - struct dev_state *ps = (struct dev_state *)file->private_data; - unsigned int mask = 0; + struct dev_state *ps = file->private_data; + unsigned int mask = 0; poll_wait(file, &ps->wait, wait); if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed)) mask |= POLLOUT | POLLWRNORM; - if (!connected(ps->dev)) + if (!connected(ps)) mask |= POLLERR | POLLHUP; return mask; } -struct file_operations usbfs_device_file_operations = { +const struct file_operations usbfs_device_file_operations = { .llseek = usbdev_lseek, .read = usbdev_read, .poll = usbdev_poll, @@ -1580,16 +1590,16 @@ static void usbdev_add(struct usb_device *dev) { int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1); - dev->usbfs_dev = device_create(usb_device_class, &dev->dev, - MKDEV(USB_DEVICE_MAJOR, minor), + dev->class_dev = class_device_create(usb_device_class, NULL, + MKDEV(USB_DEVICE_MAJOR, minor), &dev->dev, "usbdev%d.%d", dev->bus->busnum, dev->devnum); - dev->usbfs_dev->platform_data = dev; + dev->class_dev->class_data = dev; } static void usbdev_remove(struct usb_device *dev) { - device_unregister(dev->usbfs_dev); + class_device_unregister(dev->class_dev); } static int usbdev_notify(struct notifier_block *self, unsigned long action, |