diff options
author | Johan Hovold <jhovold@gmail.com> | 2013-02-13 11:53:28 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-02-13 12:20:06 -0500 |
commit | b2ca699076573c94fee9a73cb0d8645383b602a0 (patch) | |
tree | b9579ce8ed1511a5b3a234b03392b3c095de9231 /drivers/usb | |
parent | cd565279e51bedee1b2988e84f9b3bef485adeb6 (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.c | 20 | ||||
-rw-r--r-- | drivers/usb/serial/mct_u232.c | 22 | ||||
-rw-r--r-- | drivers/usb/serial/quatech2.c | 18 | ||||
-rw-r--r-- | drivers/usb/serial/sierra.c | 8 | ||||
-rw-r--r-- | drivers/usb/serial/ssu100.c | 19 | ||||
-rw-r--r-- | drivers/usb/serial/usb-serial.c | 14 | ||||
-rw-r--r-- | drivers/usb/serial/usb_wwan.c | 8 |
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 | ||
517 | static void mct_u232_close(struct usb_serial_port *port) | 513 | static 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 | ||
963 | static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch) | 961 | static 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 | ||
862 | static void sierra_dtr_rts(struct usb_serial_port *port, int on) | 862 | static 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 | ||
879 | static int sierra_startup(struct usb_serial *serial) | 873 | static 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 | ||
524 | static void ssu100_update_msr(struct usb_serial_port *port, u8 msr) | 521 | static 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) | |||
694 | static void serial_dtr_rts(struct tty_port *port, int on) | 694 | static 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 | ||
703 | static const struct tty_port_operations serial_port_ops = { | 713 | static 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 | ||
39 | void usb_wwan_dtr_rts(struct usb_serial_port *port, int on) | 39 | void 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 | } |
58 | EXPORT_SYMBOL(usb_wwan_dtr_rts); | 56 | EXPORT_SYMBOL(usb_wwan_dtr_rts); |
59 | 57 | ||