Commit e33fef6f authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

[PATCH] USB: fix potential races in mct_u232 now that there's no locks in the usb-serial core.

parent ebfb0e22
...@@ -171,6 +171,7 @@ static struct usb_serial_device_type mct_u232_device = { ...@@ -171,6 +171,7 @@ static struct usb_serial_device_type mct_u232_device = {
struct mct_u232_private { struct mct_u232_private {
spinlock_t lock;
unsigned long control_state; /* Modem Line Setting (TIOCM) */ unsigned long control_state; /* Modem Line Setting (TIOCM) */
unsigned char last_lcr; /* Line Control Register */ unsigned char last_lcr; /* Line Control Register */
unsigned char last_lsr; /* Line Status Register */ unsigned char last_lsr; /* Line Status Register */
...@@ -306,8 +307,9 @@ static int mct_u232_startup (struct usb_serial *serial) ...@@ -306,8 +307,9 @@ static int mct_u232_startup (struct usb_serial *serial)
/* allocate the private data structure */ /* allocate the private data structure */
priv = kmalloc(sizeof(struct mct_u232_private), GFP_KERNEL); priv = kmalloc(sizeof(struct mct_u232_private), GFP_KERNEL);
if (!priv) if (!priv)
return (-1); /* error */ return -ENOMEM;
/* set initial values for control structures */ /* set initial values for control structures */
spin_lock_init(&priv->lock);
priv->control_state = 0; priv->control_state = 0;
priv->last_lsr = 0; priv->last_lsr = 0;
priv->last_msr = 0; priv->last_msr = 0;
...@@ -339,6 +341,10 @@ static int mct_u232_open (struct usb_serial_port *port, struct file *filp) ...@@ -339,6 +341,10 @@ static int mct_u232_open (struct usb_serial_port *port, struct file *filp)
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
struct mct_u232_private *priv = usb_get_serial_port_data(port); struct mct_u232_private *priv = usb_get_serial_port_data(port);
int retval = 0; int retval = 0;
unsigned long control_state;
unsigned long flags;
unsigned char last_lcr;
unsigned char last_msr;
dbg("%s port %d", __FUNCTION__, port->number); dbg("%s port %d", __FUNCTION__, port->number);
...@@ -355,20 +361,27 @@ static int mct_u232_open (struct usb_serial_port *port, struct file *filp) ...@@ -355,20 +361,27 @@ static int mct_u232_open (struct usb_serial_port *port, struct file *filp)
* sure if this is really necessary. But it should not harm * sure if this is really necessary. But it should not harm
* either. * either.
*/ */
spin_lock_irqsave(&priv->lock, flags);
if (port->tty->termios->c_cflag & CBAUD) if (port->tty->termios->c_cflag & CBAUD)
priv->control_state = TIOCM_DTR | TIOCM_RTS; priv->control_state = TIOCM_DTR | TIOCM_RTS;
else else
priv->control_state = 0; priv->control_state = 0;
mct_u232_set_modem_ctrl(serial, priv->control_state);
priv->last_lcr = (MCT_U232_DATA_BITS_8 | priv->last_lcr = (MCT_U232_DATA_BITS_8 |
MCT_U232_PARITY_NONE | MCT_U232_PARITY_NONE |
MCT_U232_STOP_BITS_1); MCT_U232_STOP_BITS_1);
mct_u232_set_line_ctrl(serial, priv->last_lcr); control_state = priv->control_state;
last_lcr = priv->last_lcr;
spin_unlock_irqrestore(&priv->lock, flags);
mct_u232_set_modem_ctrl(serial, control_state);
mct_u232_set_line_ctrl(serial, last_lcr);
/* Read modem status and update control state */ /* Read modem status and update control state */
mct_u232_get_modem_stat(serial, &priv->last_msr); mct_u232_get_modem_stat(serial, &last_msr);
spin_lock_irqsave(&priv->lock, flags);
priv->last_msr = last_msr;
mct_u232_msr_to_state(&priv->control_state, priv->last_msr); mct_u232_msr_to_state(&priv->control_state, priv->last_msr);
spin_unlock_irqrestore(&priv->lock, flags);
{ {
/* Puh, that's dirty */ /* Puh, that's dirty */
...@@ -523,6 +536,7 @@ static void mct_u232_read_int_callback (struct urb *urb, struct pt_regs *regs) ...@@ -523,6 +536,7 @@ static void mct_u232_read_int_callback (struct urb *urb, struct pt_regs *regs)
struct tty_struct *tty; struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer; unsigned char *data = urb->transfer_buffer;
int status; int status;
unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number); dbg("%s - port %d", __FUNCTION__, port->number);
...@@ -567,6 +581,7 @@ static void mct_u232_read_int_callback (struct urb *urb, struct pt_regs *regs) ...@@ -567,6 +581,7 @@ static void mct_u232_read_int_callback (struct urb *urb, struct pt_regs *regs)
* The interrupt-in pipe signals exceptional conditions (modem line * The interrupt-in pipe signals exceptional conditions (modem line
* signal changes and errors). data[0] holds MSR, data[1] holds LSR. * signal changes and errors). data[0] holds MSR, data[1] holds LSR.
*/ */
spin_lock_irqsave(&priv->lock, flags);
priv->last_msr = data[MCT_U232_MSR_INDEX]; priv->last_msr = data[MCT_U232_MSR_INDEX];
/* Record Control Line states */ /* Record Control Line states */
...@@ -597,6 +612,7 @@ static void mct_u232_read_int_callback (struct urb *urb, struct pt_regs *regs) ...@@ -597,6 +612,7 @@ static void mct_u232_read_int_callback (struct urb *urb, struct pt_regs *regs)
} }
} }
#endif #endif
spin_unlock_irqrestore(&priv->lock, flags);
exit: exit:
status = usb_submit_urb (urb, GFP_ATOMIC); status = usb_submit_urb (urb, GFP_ATOMIC);
if (status) if (status)
...@@ -614,7 +630,16 @@ static void mct_u232_set_termios (struct usb_serial_port *port, ...@@ -614,7 +630,16 @@ static void mct_u232_set_termios (struct usb_serial_port *port,
unsigned int old_iflag = old_termios->c_iflag; unsigned int old_iflag = old_termios->c_iflag;
unsigned int cflag = port->tty->termios->c_cflag; unsigned int cflag = port->tty->termios->c_cflag;
unsigned int old_cflag = old_termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag;
unsigned long flags;
unsigned long control_state;
unsigned char last_lcr;
/* get a local copy of the current port settings */
spin_lock_irqsave(&priv->lock, flags);
control_state = priv->control_state;
last_lcr = priv->last_lcr;
spin_unlock_irqrestore(&priv->lock, flags);
/* /*
* Update baud rate * Update baud rate
*/ */
...@@ -622,12 +647,12 @@ static void mct_u232_set_termios (struct usb_serial_port *port, ...@@ -622,12 +647,12 @@ static void mct_u232_set_termios (struct usb_serial_port *port,
/* reassert DTR and (maybe) RTS on transition from B0 */ /* reassert DTR and (maybe) RTS on transition from B0 */
if( (old_cflag & CBAUD) == B0 ) { if( (old_cflag & CBAUD) == B0 ) {
dbg("%s: baud was B0", __FUNCTION__); dbg("%s: baud was B0", __FUNCTION__);
priv->control_state |= TIOCM_DTR; control_state |= TIOCM_DTR;
/* don't set RTS if using hardware flow control */ /* don't set RTS if using hardware flow control */
if (!(old_cflag & CRTSCTS)) { if (!(old_cflag & CRTSCTS)) {
priv->control_state |= TIOCM_RTS; control_state |= TIOCM_RTS;
} }
mct_u232_set_modem_ctrl(serial, priv->control_state); mct_u232_set_modem_ctrl(serial, control_state);
} }
switch(cflag & CBAUD) { switch(cflag & CBAUD) {
...@@ -659,8 +684,8 @@ static void mct_u232_set_termios (struct usb_serial_port *port, ...@@ -659,8 +684,8 @@ static void mct_u232_set_termios (struct usb_serial_port *port,
if ((cflag & CBAUD) == B0 ) { if ((cflag & CBAUD) == B0 ) {
dbg("%s: baud is B0", __FUNCTION__); dbg("%s: baud is B0", __FUNCTION__);
/* Drop RTS and DTR */ /* Drop RTS and DTR */
priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); control_state &= ~(TIOCM_DTR | TIOCM_RTS);
mct_u232_set_modem_ctrl(serial, priv->control_state); mct_u232_set_modem_ctrl(serial, control_state);
} }
} }
...@@ -672,36 +697,36 @@ static void mct_u232_set_termios (struct usb_serial_port *port, ...@@ -672,36 +697,36 @@ static void mct_u232_set_termios (struct usb_serial_port *port,
|| (cflag & CSTOPB) != (old_cflag & CSTOPB) ) { || (cflag & CSTOPB) != (old_cflag & CSTOPB) ) {
priv->last_lcr = 0; last_lcr = 0;
/* set the parity */ /* set the parity */
if (cflag & PARENB) if (cflag & PARENB)
priv->last_lcr |= (cflag & PARODD) ? last_lcr |= (cflag & PARODD) ?
MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN; MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
else else
priv->last_lcr |= MCT_U232_PARITY_NONE; last_lcr |= MCT_U232_PARITY_NONE;
/* set the number of data bits */ /* set the number of data bits */
switch (cflag & CSIZE) { switch (cflag & CSIZE) {
case CS5: case CS5:
priv->last_lcr |= MCT_U232_DATA_BITS_5; break; last_lcr |= MCT_U232_DATA_BITS_5; break;
case CS6: case CS6:
priv->last_lcr |= MCT_U232_DATA_BITS_6; break; last_lcr |= MCT_U232_DATA_BITS_6; break;
case CS7: case CS7:
priv->last_lcr |= MCT_U232_DATA_BITS_7; break; last_lcr |= MCT_U232_DATA_BITS_7; break;
case CS8: case CS8:
priv->last_lcr |= MCT_U232_DATA_BITS_8; break; last_lcr |= MCT_U232_DATA_BITS_8; break;
default: default:
err("CSIZE was not CS5-CS8, using default of 8"); err("CSIZE was not CS5-CS8, using default of 8");
priv->last_lcr |= MCT_U232_DATA_BITS_8; last_lcr |= MCT_U232_DATA_BITS_8;
break; break;
} }
/* set the number of stop bits */ /* set the number of stop bits */
priv->last_lcr |= (cflag & CSTOPB) ? last_lcr |= (cflag & CSTOPB) ?
MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1; MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
mct_u232_set_line_ctrl(serial, priv->last_lcr); mct_u232_set_line_ctrl(serial, last_lcr);
} }
/* /*
...@@ -714,11 +739,17 @@ static void mct_u232_set_termios (struct usb_serial_port *port, ...@@ -714,11 +739,17 @@ static void mct_u232_set_termios (struct usb_serial_port *port,
/* Drop DTR/RTS if no flow control otherwise assert */ /* Drop DTR/RTS if no flow control otherwise assert */
if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS) ) if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS) )
priv->control_state |= TIOCM_DTR | TIOCM_RTS; control_state |= TIOCM_DTR | TIOCM_RTS;
else else
priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); control_state &= ~(TIOCM_DTR | TIOCM_RTS);
mct_u232_set_modem_ctrl(serial, priv->control_state); mct_u232_set_modem_ctrl(serial, control_state);
} }
/* save off the modified port settings */
spin_lock_irqsave(&priv->lock, flags);
priv->control_state = control_state;
priv->last_lcr = last_lcr;
spin_unlock_irqrestore(&priv->lock, flags);
} /* mct_u232_set_termios */ } /* mct_u232_set_termios */
...@@ -726,10 +757,15 @@ static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) ...@@ -726,10 +757,15 @@ static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state )
{ {
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
struct mct_u232_private *priv = usb_get_serial_port_data(port); struct mct_u232_private *priv = usb_get_serial_port_data(port);
unsigned char lcr = priv->last_lcr; unsigned char lcr;
unsigned long flags;
dbg("%sstate=%d", __FUNCTION__, break_state); dbg("%sstate=%d", __FUNCTION__, break_state);
spin_lock_irqsave(&priv->lock, flags);
lcr = priv->last_lcr;
spin_unlock_irqrestore(&priv->lock, flags);
if (break_state) if (break_state)
lcr |= MCT_U232_SET_BREAK; lcr |= MCT_U232_SET_BREAK;
...@@ -743,13 +779,19 @@ static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file, ...@@ -743,13 +779,19 @@ static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file,
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
struct mct_u232_private *priv = usb_get_serial_port_data(port); struct mct_u232_private *priv = usb_get_serial_port_data(port);
int mask; int mask;
unsigned long control_state;
unsigned long flags;
dbg("%scmd=0x%x", __FUNCTION__, cmd); dbg("%scmd=0x%x", __FUNCTION__, cmd);
spin_lock_irqsave(&priv->lock, flags);
control_state = priv->control_state;
spin_unlock_irqrestore(&priv->lock, flags);
/* Based on code from acm.c and others */ /* Based on code from acm.c and others */
switch (cmd) { switch (cmd) {
case TIOCMGET: case TIOCMGET:
return put_user(priv->control_state, (unsigned long *) arg); return put_user(control_state, (unsigned long *) arg);
break; break;
case TIOCMSET: /* Turns on and off the lines as specified by the mask */ case TIOCMSET: /* Turns on and off the lines as specified by the mask */
...@@ -762,20 +804,24 @@ static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file, ...@@ -762,20 +804,24 @@ static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file,
/* RTS needs set */ /* RTS needs set */
if( ((cmd == TIOCMSET) && (mask & TIOCM_RTS)) || if( ((cmd == TIOCMSET) && (mask & TIOCM_RTS)) ||
(cmd == TIOCMBIS) ) (cmd == TIOCMBIS) )
priv->control_state |= TIOCM_RTS; control_state |= TIOCM_RTS;
else else
priv->control_state &= ~TIOCM_RTS; control_state &= ~TIOCM_RTS;
} }
if ((cmd == TIOCMSET) || (mask & TIOCM_DTR)) { if ((cmd == TIOCMSET) || (mask & TIOCM_DTR)) {
/* DTR needs set */ /* DTR needs set */
if( ((cmd == TIOCMSET) && (mask & TIOCM_DTR)) || if( ((cmd == TIOCMSET) && (mask & TIOCM_DTR)) ||
(cmd == TIOCMBIS) ) (cmd == TIOCMBIS) )
priv->control_state |= TIOCM_DTR; control_state |= TIOCM_DTR;
else else
priv->control_state &= ~TIOCM_DTR; control_state &= ~TIOCM_DTR;
} }
mct_u232_set_modem_ctrl(serial, priv->control_state); mct_u232_set_modem_ctrl(serial, control_state);
spin_lock_irqsave(&priv->lock, flags);
priv->control_state = control_state;
spin_unlock_irqrestore(&priv->lock, flags);
break; break;
case TIOCMIWAIT: case TIOCMIWAIT:
......
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