aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorJohan Hovold <jhovold@gmail.com>2013-02-13 11:53:28 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-02-13 12:20:06 -0500
commitb2ca699076573c94fee9a73cb0d8645383b602a0 (patch)
treeb9579ce8ed1511a5b3a234b03392b3c095de9231 /drivers/usb
parentcd565279e51bedee1b2988e84f9b3bef485adeb6 (diff)
USB: serial: fix null-pointer dereferences on disconnect
Make sure serial-driver dtr_rts is called with disc_mutex held after checking the disconnected flag. Due to a bug in the tty layer, dtr_rts may get called after a device has been disconnected and the tty-device unregistered. Some drivers have had individual checks for disconnect to make sure the disconnected interface was not accessed, but this should really be handled in usb-serial core (at least until the long-standing tty-bug has been fixed). Note that the problem has been made more acute with commit 0998d0631001 ("device-core: Ensure drvdata = NULL when no driver is bound") as the port data is now also NULL when dtr_rts is called resulting in further oopses. Reported-by: Chris Ruehl <chris.ruehl@gtsys.com.hk> Cc: stable <stable@vger.kernel.org> Signed-off-by: Johan Hovold <jhovold@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/serial/ftdi_sio.c20
-rw-r--r--drivers/usb/serial/mct_u232.c22
-rw-r--r--drivers/usb/serial/quatech2.c18
-rw-r--r--drivers/usb/serial/sierra.c8
-rw-r--r--drivers/usb/serial/ssu100.c19
-rw-r--r--drivers/usb/serial/usb-serial.c14
-rw-r--r--drivers/usb/serial/usb_wwan.c8
7 files changed, 50 insertions, 59 deletions
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 90ceef1776c3..d07fccf3bab5 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1886,24 +1886,22 @@ static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
1886{ 1886{
1887 struct ftdi_private *priv = usb_get_serial_port_data(port); 1887 struct ftdi_private *priv = usb_get_serial_port_data(port);
1888 1888
1889 mutex_lock(&port->serial->disc_mutex); 1889 /* Disable flow control */
1890 if (!port->serial->disconnected) { 1890 if (!on) {
1891 /* Disable flow control */ 1891 if (usb_control_msg(port->serial->dev,
1892 if (!on && usb_control_msg(port->serial->dev,
1893 usb_sndctrlpipe(port->serial->dev, 0), 1892 usb_sndctrlpipe(port->serial->dev, 0),
1894 FTDI_SIO_SET_FLOW_CTRL_REQUEST, 1893 FTDI_SIO_SET_FLOW_CTRL_REQUEST,
1895 FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, 1894 FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
1896 0, priv->interface, NULL, 0, 1895 0, priv->interface, NULL, 0,
1897 WDR_TIMEOUT) < 0) { 1896 WDR_TIMEOUT) < 0) {
1898 dev_err(&port->dev, "error from flowcontrol urb\n"); 1897 dev_err(&port->dev, "error from flowcontrol urb\n");
1899 } 1898 }
1900 /* drop RTS and DTR */
1901 if (on)
1902 set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
1903 else
1904 clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
1905 } 1899 }
1906 mutex_unlock(&port->serial->disc_mutex); 1900 /* drop RTS and DTR */
1901 if (on)
1902 set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
1903 else
1904 clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
1907} 1905}
1908 1906
1909/* 1907/*
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index b6911757c855..d9c86516fed4 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -499,19 +499,15 @@ static void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
499 unsigned int control_state; 499 unsigned int control_state;
500 struct mct_u232_private *priv = usb_get_serial_port_data(port); 500 struct mct_u232_private *priv = usb_get_serial_port_data(port);
501 501
502 mutex_lock(&port->serial->disc_mutex); 502 spin_lock_irq(&priv->lock);
503 if (!port->serial->disconnected) { 503 if (on)
504 /* drop DTR and RTS */ 504 priv->control_state |= TIOCM_DTR | TIOCM_RTS;
505 spin_lock_irq(&priv->lock); 505 else
506 if (on) 506 priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
507 priv->control_state |= TIOCM_DTR | TIOCM_RTS; 507 control_state = priv->control_state;
508 else 508 spin_unlock_irq(&priv->lock);
509 priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); 509
510 control_state = priv->control_state; 510 mct_u232_set_modem_ctrl(port, control_state);
511 spin_unlock_irq(&priv->lock);
512 mct_u232_set_modem_ctrl(port, control_state);
513 }
514 mutex_unlock(&port->serial->disc_mutex);
515} 511}
516 512
517static void mct_u232_close(struct usb_serial_port *port) 513static void mct_u232_close(struct usb_serial_port *port)
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index d152be97d041..a8d5110d4cc5 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -945,19 +945,17 @@ static void qt2_dtr_rts(struct usb_serial_port *port, int on)
945 struct usb_device *dev = port->serial->dev; 945 struct usb_device *dev = port->serial->dev;
946 struct qt2_port_private *port_priv = usb_get_serial_port_data(port); 946 struct qt2_port_private *port_priv = usb_get_serial_port_data(port);
947 947
948 mutex_lock(&port->serial->disc_mutex); 948 /* Disable flow control */
949 if (!port->serial->disconnected) { 949 if (!on) {
950 /* Disable flow control */ 950 if (qt2_setregister(dev, port_priv->device_port,
951 if (!on && qt2_setregister(dev, port_priv->device_port,
952 UART_MCR, 0) < 0) 951 UART_MCR, 0) < 0)
953 dev_warn(&port->dev, "error from flowcontrol urb\n"); 952 dev_warn(&port->dev, "error from flowcontrol urb\n");
954 /* drop RTS and DTR */
955 if (on)
956 update_mctrl(port_priv, TIOCM_DTR | TIOCM_RTS, 0);
957 else
958 update_mctrl(port_priv, 0, TIOCM_DTR | TIOCM_RTS);
959 } 953 }
960 mutex_unlock(&port->serial->disc_mutex); 954 /* drop RTS and DTR */
955 if (on)
956 update_mctrl(port_priv, TIOCM_DTR | TIOCM_RTS, 0);
957 else
958 update_mctrl(port_priv, 0, TIOCM_DTR | TIOCM_RTS);
961} 959}
962 960
963static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch) 961static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch)
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index af06f2f5f38b..d4426c038c32 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -861,19 +861,13 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
861 861
862static void sierra_dtr_rts(struct usb_serial_port *port, int on) 862static void sierra_dtr_rts(struct usb_serial_port *port, int on)
863{ 863{
864 struct usb_serial *serial = port->serial;
865 struct sierra_port_private *portdata; 864 struct sierra_port_private *portdata;
866 865
867 portdata = usb_get_serial_port_data(port); 866 portdata = usb_get_serial_port_data(port);
868 portdata->rts_state = on; 867 portdata->rts_state = on;
869 portdata->dtr_state = on; 868 portdata->dtr_state = on;
870 869
871 if (serial->dev) { 870 sierra_send_setup(port);
872 mutex_lock(&serial->disc_mutex);
873 if (!serial->disconnected)
874 sierra_send_setup(port);
875 mutex_unlock(&serial->disc_mutex);
876 }
877} 871}
878 872
879static int sierra_startup(struct usb_serial *serial) 873static int sierra_startup(struct usb_serial *serial)
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
index 4543ea350229..d938396171e8 100644
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -506,19 +506,16 @@ static void ssu100_dtr_rts(struct usb_serial_port *port, int on)
506{ 506{
507 struct usb_device *dev = port->serial->dev; 507 struct usb_device *dev = port->serial->dev;
508 508
509 mutex_lock(&port->serial->disc_mutex); 509 /* Disable flow control */
510 if (!port->serial->disconnected) { 510 if (!on) {
511 /* Disable flow control */ 511 if (ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
512 if (!on &&
513 ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
514 dev_err(&port->dev, "error from flowcontrol urb\n"); 512 dev_err(&port->dev, "error from flowcontrol urb\n");
515 /* drop RTS and DTR */
516 if (on)
517 set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
518 else
519 clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
520 } 513 }
521 mutex_unlock(&port->serial->disc_mutex); 514 /* drop RTS and DTR */
515 if (on)
516 set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
517 else
518 clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
522} 519}
523 520
524static void ssu100_update_msr(struct usb_serial_port *port, u8 msr) 521static void ssu100_update_msr(struct usb_serial_port *port, u8 msr)
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 0a17f5942552..a19ed74d770d 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -694,10 +694,20 @@ static int serial_carrier_raised(struct tty_port *port)
694static void serial_dtr_rts(struct tty_port *port, int on) 694static void serial_dtr_rts(struct tty_port *port, int on)
695{ 695{
696 struct usb_serial_port *p = container_of(port, struct usb_serial_port, port); 696 struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
697 struct usb_serial_driver *drv = p->serial->type; 697 struct usb_serial *serial = p->serial;
698 struct usb_serial_driver *drv = serial->type;
698 699
699 if (drv->dtr_rts) 700 if (!drv->dtr_rts)
701 return;
702 /*
703 * Work-around bug in the tty-layer which can result in dtr_rts
704 * being called after a disconnect (and tty_unregister_device
705 * has returned). Remove once bug has been squashed.
706 */
707 mutex_lock(&serial->disc_mutex);
708 if (!serial->disconnected)
700 drv->dtr_rts(p, on); 709 drv->dtr_rts(p, on);
710 mutex_unlock(&serial->disc_mutex);
701} 711}
702 712
703static const struct tty_port_operations serial_port_ops = { 713static const struct tty_port_operations serial_port_ops = {
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 01c94aada56c..1355a6cd4508 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -38,7 +38,6 @@
38 38
39void usb_wwan_dtr_rts(struct usb_serial_port *port, int on) 39void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
40{ 40{
41 struct usb_serial *serial = port->serial;
42 struct usb_wwan_port_private *portdata; 41 struct usb_wwan_port_private *portdata;
43 struct usb_wwan_intf_private *intfdata; 42 struct usb_wwan_intf_private *intfdata;
44 43
@@ -48,12 +47,11 @@ void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
48 return; 47 return;
49 48
50 portdata = usb_get_serial_port_data(port); 49 portdata = usb_get_serial_port_data(port);
51 mutex_lock(&serial->disc_mutex); 50 /* FIXME: locking */
52 portdata->rts_state = on; 51 portdata->rts_state = on;
53 portdata->dtr_state = on; 52 portdata->dtr_state = on;
54 if (serial->dev) 53
55 intfdata->send_setup(port); 54 intfdata->send_setup(port);
56 mutex_unlock(&serial->disc_mutex);
57} 55}
58EXPORT_SYMBOL(usb_wwan_dtr_rts); 56EXPORT_SYMBOL(usb_wwan_dtr_rts);
59 57