ChangeSet 1.2000.6.5, 2004/10/20 15:33:03-07:00, [EMAIL PROTECTED]

USB: fix DoS in the visor driver by rate limiting sends.

Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>


 drivers/usb/serial/visor.c |   81 ++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 69 insertions(+), 12 deletions(-)


diff -Nru a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
--- a/drivers/usb/serial/visor.c        2004-10-22 16:15:46 -07:00
+++ b/drivers/usb/serial/visor.c        2004-10-22 16:15:46 -07:00
@@ -381,10 +381,17 @@
        .read_bulk_callback =   visor_read_bulk_callback,
 };
 
+struct visor_private {
+       spinlock_t lock;
+       int bytes_in;
+       int bytes_out;
+       int outstanding_urbs;
+};
 
-static int bytes_in;
-static int bytes_out;
+/* number of outstanding urbs to prevent userspace DoS from happening */
+#define URB_UPPER_LIMIT        42
 
+static int stats;
 
 /******************************************************************************
  * Handspring Visor specific driver functions
@@ -392,6 +399,8 @@
 static int visor_open (struct usb_serial_port *port, struct file *filp)
 {
        struct usb_serial *serial = port->serial;
+       struct visor_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
        int result = 0;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
@@ -402,8 +411,11 @@
                return -ENODEV;
        }
 
-       bytes_in = 0;
-       bytes_out = 0;
+       spin_lock_irqsave(&priv->lock, flags);
+       priv->bytes_in = 0;
+       priv->bytes_out = 0;
+       priv->outstanding_urbs = 0;
+       spin_unlock_irqrestore(&priv->lock, flags);
 
        /*
         * Force low_latency on so that our tty_push actually forces the data
@@ -441,6 +453,7 @@
 
 static void visor_close (struct usb_serial_port *port, struct file * filp)
 {
+       struct visor_private *priv = usb_get_serial_port_data(port);
        unsigned char *transfer_buffer;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
@@ -461,20 +474,32 @@
                kfree (transfer_buffer);
        }
 
-       /* Uncomment the following line if you want to see some statistics in your 
syslog */
-       /* dev_info (&port->dev, "Bytes In = %d  Bytes Out = %d\n", bytes_in, 
bytes_out); */
+       if (stats)
+               dev_info(&port->dev, "Bytes In = %d  Bytes Out = %d\n",
+                        priv->bytes_in, priv->bytes_out);
 }
 
 
 static int visor_write (struct usb_serial_port *port, int from_user, const unsigned 
char *buf, int count)
 {
+       struct visor_private *priv = usb_get_serial_port_data(port);
        struct usb_serial *serial = port->serial;
        struct urb *urb;
        unsigned char *buffer;
+       unsigned long flags;
        int status;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
+       spin_lock_irqsave(&priv->lock, flags);
+       if (priv->outstanding_urbs > URB_UPPER_LIMIT) {
+               spin_unlock_irqrestore(&priv->lock, flags);
+               dev_dbg(&port->dev, "write limit hit\n");
+               return 0;
+       }
+       ++priv->outstanding_urbs;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
        buffer = kmalloc (count, GFP_ATOMIC);
        if (!buffer) {
                dev_err(&port->dev, "out of memory\n");
@@ -514,7 +539,10 @@
                count = status;
                kfree (buffer);
        } else {
-               bytes_out += count;
+               spin_lock_irqsave(&priv->lock, flags);
+               ++priv->outstanding_urbs;
+               priv->bytes_out += count;
+               spin_unlock_irqrestore(&priv->lock, flags);
        }
 
        /* we are done with this urb, so let the host driver
@@ -555,6 +583,8 @@
 static void visor_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
 {
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+       struct visor_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
 
        /* free up the transfer buffer, as usb_free_urb() does not do this */
        kfree (urb->transfer_buffer);
@@ -565,6 +595,10 @@
                dbg("%s - nonzero write bulk status received: %d",
                    __FUNCTION__, urb->status);
 
+       spin_lock_irqsave(&priv->lock, flags);
+       --priv->outstanding_urbs;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
        schedule_work(&port->work);
 }
 
@@ -572,8 +606,10 @@
 static void visor_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
 {
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
-       struct tty_struct *tty;
+       struct visor_private *priv = usb_get_serial_port_data(port);
        unsigned char *data = urb->transfer_buffer;
+       struct tty_struct *tty;
+       unsigned long flags;
        int i;
        int result;
 
@@ -598,7 +634,9 @@
                }
                tty_flip_buffer_push(tty);
        }
-       bytes_in += urb->actual_length;
+       spin_lock_irqsave(&priv->lock, flags);
+       priv->bytes_in += urb->actual_length;
+       spin_unlock_irqrestore(&priv->lock, flags);
 
        /* Continue trying to always read  */
        usb_fill_bulk_urb (port->read_urb, port->serial->dev,
@@ -833,6 +871,22 @@
        return num_ports;
 }
 
+static int generic_startup(struct usb_serial *serial)
+{
+       struct visor_private *priv;
+       int i;
+
+       for (i = 0; i < serial->num_ports; ++i) {
+               priv = kmalloc (sizeof(*priv), GFP_KERNEL);
+               if (!priv)
+                       return -ENOMEM;
+               memset (priv, 0x00, sizeof(*priv));
+               spin_lock_init(&priv->lock);
+               usb_set_serial_port_data(serial->port[i], priv);
+       }
+       return 0;
+}
+
 static int clie_3_5_startup (struct usb_serial *serial)
 {
        struct device *dev = &serial->dev->dev;
@@ -872,7 +926,7 @@
                return -EIO;
        }
 
-       return 0;
+       return generic_startup(serial);
 }
  
 static int treo_attach (struct usb_serial *serial)
@@ -911,7 +965,7 @@
        COPY_PORT(serial->port[1], swap_port);
        kfree(swap_port);
 
-       return 0;
+       return generic_startup(serial);
 }
 
 static int clie_5_attach (struct usb_serial *serial)
@@ -932,7 +986,7 @@
        /* port 0 now uses the modified endpoint Address */
        serial->port[0]->bulk_out_endpointAddress = 
serial->port[1]->bulk_out_endpointAddress;
 
-       return 0;
+       return generic_startup(serial);
 }
 
 static void visor_shutdown (struct usb_serial *serial)
@@ -1088,8 +1142,11 @@
 
 module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
+module_param(stats, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(stats, "Enables statistics or not");
 
 module_param(vendor, ushort, 0);
 MODULE_PARM_DESC(vendor, "User specified vendor ID");
 module_param(product, ushort, 0);
 MODULE_PARM_DESC(product, "User specified product ID");
+



-------------------------------------------------------
This SF.net email is sponsored by: IT Product Guide on ITManagersJournal
Use IT products in your business? Tell us what you think of them. Give us
Your Opinions, Get Free ThinkGeek Gift Certificates! Click to find out more
http://productguide.itmanagersjournal.com/guidepromo.tmpl
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to