Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8a4613f0 authored by Luiz Fernando Capitulino's avatar Luiz Fernando Capitulino Committed by Greg Kroah-Hartman
Browse files

[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: default avatarLuiz Capitulino <lcapitulino@mandriva.com.br>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 487f9c67
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <linux/list.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#include <linux/usb.h>
#include "usb-serial.h"
#include "pl2303.h"
@@ -191,6 +192,9 @@ static int serial_open (struct tty_struct *tty, struct file * filp)
	if (!port)
		return -ENODEV;

	if (down_interruptible(&port->sem))
		return -ERESTARTSYS;
	 
	++port->open_count;

	if (port->open_count == 1) {
@@ -215,6 +219,7 @@ static int serial_open (struct tty_struct *tty, struct file * filp)
			goto bailout_module_put;
	}

	up(&port->sem);
	return 0;

bailout_module_put:
@@ -222,6 +227,7 @@ static int serial_open (struct tty_struct *tty, struct file * filp)
bailout_kref_put:
	kref_put(&serial->kref, destroy_serial);
	port->open_count = 0;
	up(&port->sem);
	return retval;
}

@@ -234,8 +240,10 @@ static void serial_close(struct tty_struct *tty, struct file * filp)

	dbg("%s - port %d", __FUNCTION__, port->number);

	down(&port->sem);

	if (port->open_count == 0)
		return;
		goto out;

	--port->open_count;
	if (port->open_count == 0) {
@@ -253,6 +261,9 @@ static void serial_close(struct tty_struct *tty, struct file * filp)
	}

	kref_put(&port->serial->kref, destroy_serial);

out:
	up(&port->sem);
}

static int serial_write (struct tty_struct * tty, const unsigned char *buf, int count)
@@ -774,6 +785,7 @@ int usb_serial_probe(struct usb_interface *interface,
		port->number = i + serial->minor;
		port->serial = serial;
		spin_lock_init(&port->lock);
		sema_init(&port->sem, 1);
		INIT_WORK(&port->work, usb_serial_port_softint, port);
		serial->port[i] = port;
	}
+4 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#include <linux/config.h>
#include <linux/kref.h>
#include <asm/semaphore.h>

#define SERIAL_TTY_MAJOR	188	/* Nice legal number now */
#define SERIAL_TTY_MINORS	255	/* loads of devices :) */
@@ -30,6 +31,8 @@
 * @serial: pointer back to the struct usb_serial owner of this port.
 * @tty: pointer to the corresponding tty for this port.
 * @lock: spinlock to grab when updating portions of this structure.
 * @sem: semaphore used to synchronize serial_open() and serial_close()
 *	access for this port.
 * @number: the number of the port (the minor number).
 * @interrupt_in_buffer: pointer to the interrupt in buffer for this port.
 * @interrupt_in_urb: pointer to the interrupt in struct urb for this port.
@@ -60,6 +63,7 @@ struct usb_serial_port {
	struct usb_serial *	serial;
	struct tty_struct *	tty;
	spinlock_t		lock;
	struct semaphore        sem;
	unsigned char		number;

	unsigned char *		interrupt_in_buffer;