Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / usb / core / devio.c
index 545da37..d8b0476 100644 (file)
@@ -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;
@@ -312,7 +317,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);
@@ -539,21 +544,19 @@ 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;
+               dev = inode->i_private;
        if (!dev) {
                kfree(ps);
                goto out;
@@ -572,13 +575,13 @@ 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;
+       mutex_unlock(&usbfs_mutex);
+       return ret;
 }
 
 static int usbdev_release(struct inode *inode, struct file *file)
@@ -588,7 +591,12 @@ static int usbdev_release(struct inode *inode, struct file *file)
        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))
@@ -597,9 +605,8 @@ static int usbdev_release(struct inode *inode, struct file *file)
        destroy_all_async(ps);
        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)
@@ -823,8 +830,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg)
 
 static int proc_resetdevice(struct dev_state *ps)
 {
-       return usb_reset_device(ps->dev);
-
+       return usb_reset_composite_device(ps->dev, NULL);
 }
 
 static int proc_setintf(struct dev_state *ps, void __user *arg)
@@ -923,8 +929,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
                                != USB_ENDPOINT_XFER_CONTROL)
                        return -EINVAL;
-               /* min 8 byte setup packet, max arbitrary */
-               if (uurb->buffer_length < 8 || uurb->buffer_length > PAGE_SIZE)
+               /* min 8 byte setup packet, max 8 byte setup plus an arbitrary data stage */
+               if (uurb->buffer_length < 8 || uurb->buffer_length > (8 + MAX_USBFS_BUFFER_SIZE))
                        return -EINVAL;
                if (!(dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL)))
                        return -ENOMEM;
@@ -982,7 +988,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        return -EFAULT;
                }
                for (totlen = u = 0; u < uurb->number_of_packets; u++) {
-                       if (isopkt[u].length > 1023) {
+                       /* arbitrary limit, sufficient for USB 2.0 high-bandwidth iso */
+                       if (isopkt[u].length > 8192) {
                                kfree(isopkt);
                                return -EINVAL;
                        }
@@ -1053,6 +1060,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);