Commit 4d483d05 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

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

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