Commit 8a4613f01f5bb850cab34e3db572d97251d997b3

Authored by Luiz Fernando Capitulino
Committed by Greg Kroah-Hartman
1 parent 487f9c6710

[PATCH] USB: usbserial: race-condition fix.

There is a race-condition in usb-serial driver that can be triggered if
a processes does 'port->tty->driver_data = NULL' in serial_close() while
other processes is in kernel-space about to call serial_ioctl() on the
same port.

This happens because a process can open the device while there is
another one closing it.

The patch below fixes that by adding a semaphore to ensure that no
process will open the device while another process is closing it.

Note that we can't use spinlocks here, since serial_open() and
serial_close() can sleep.

Signed-off-by: Luiz Capitulino <lcapitulino@mandriva.com.br>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Showing 2 changed files with 17 additions and 1 deletions Side-by-side Diff

drivers/usb/serial/usb-serial.c
... ... @@ -30,6 +30,7 @@
30 30 #include <linux/list.h>
31 31 #include <linux/smp_lock.h>
32 32 #include <asm/uaccess.h>
  33 +#include <asm/semaphore.h>
33 34 #include <linux/usb.h>
34 35 #include "usb-serial.h"
35 36 #include "pl2303.h"
... ... @@ -190,6 +191,9 @@
190 191 port = serial->port[portNumber];
191 192 if (!port)
192 193 return -ENODEV;
  194 +
  195 + if (down_interruptible(&port->sem))
  196 + return -ERESTARTSYS;
193 197  
194 198 ++port->open_count;
195 199  
... ... @@ -215,6 +219,7 @@
215 219 goto bailout_module_put;
216 220 }
217 221  
  222 + up(&port->sem);
218 223 return 0;
219 224  
220 225 bailout_module_put:
... ... @@ -222,6 +227,7 @@
222 227 bailout_kref_put:
223 228 kref_put(&serial->kref, destroy_serial);
224 229 port->open_count = 0;
  230 + up(&port->sem);
225 231 return retval;
226 232 }
227 233  
228 234  
... ... @@ -234,8 +240,10 @@
234 240  
235 241 dbg("%s - port %d", __FUNCTION__, port->number);
236 242  
  243 + down(&port->sem);
  244 +
237 245 if (port->open_count == 0)
238   - return;
  246 + goto out;
239 247  
240 248 --port->open_count;
241 249 if (port->open_count == 0) {
... ... @@ -253,6 +261,9 @@
253 261 }
254 262  
255 263 kref_put(&port->serial->kref, destroy_serial);
  264 +
  265 +out:
  266 + up(&port->sem);
256 267 }
257 268  
258 269 static int serial_write (struct tty_struct * tty, const unsigned char *buf, int count)
... ... @@ -774,6 +785,7 @@
774 785 port->number = i + serial->minor;
775 786 port->serial = serial;
776 787 spin_lock_init(&port->lock);
  788 + sema_init(&port->sem, 1);
777 789 INIT_WORK(&port->work, usb_serial_port_softint, port);
778 790 serial->port[i] = port;
779 791 }
drivers/usb/serial/usb-serial.h
... ... @@ -16,6 +16,7 @@
16 16  
17 17 #include <linux/config.h>
18 18 #include <linux/kref.h>
  19 +#include <asm/semaphore.h>
19 20  
20 21 #define SERIAL_TTY_MAJOR 188 /* Nice legal number now */
21 22 #define SERIAL_TTY_MINORS 255 /* loads of devices :) */
... ... @@ -30,6 +31,8 @@
30 31 * @serial: pointer back to the struct usb_serial owner of this port.
31 32 * @tty: pointer to the corresponding tty for this port.
32 33 * @lock: spinlock to grab when updating portions of this structure.
  34 + * @sem: semaphore used to synchronize serial_open() and serial_close()
  35 + * access for this port.
33 36 * @number: the number of the port (the minor number).
34 37 * @interrupt_in_buffer: pointer to the interrupt in buffer for this port.
35 38 * @interrupt_in_urb: pointer to the interrupt in struct urb for this port.
... ... @@ -60,6 +63,7 @@
60 63 struct usb_serial * serial;
61 64 struct tty_struct * tty;
62 65 spinlock_t lock;
  66 + struct semaphore sem;
63 67 unsigned char number;
64 68  
65 69 unsigned char * interrupt_in_buffer;