]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 23 Feb 2013 01:15:13 +0000 (17:15 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 23 Feb 2013 01:15:13 +0000 (17:15 -0800)
added patches:
tty-prevent-deadlock-in-n_gsm-driver.patch
tty-set_termios-set_termiox-should-not-return-eintr.patch
usb-serial-fix-null-pointer-dereferences-on-disconnect.patch

queue-3.4/series
queue-3.4/tty-prevent-deadlock-in-n_gsm-driver.patch [new file with mode: 0644]
queue-3.4/tty-set_termios-set_termiox-should-not-return-eintr.patch [new file with mode: 0644]
queue-3.4/usb-serial-fix-null-pointer-dereferences-on-disconnect.patch [new file with mode: 0644]

index 997169a90e41bdb21723ef3451f4b766d4b6b806..4fa9b7ee2d40e6068997f96f22f6c0a43a6c1df9 100644 (file)
@@ -9,3 +9,6 @@ hrtimer-prevent-hrtimer_enqueue_reprogram-race.patch
 x86-hyper-v-register-clocksource-only-if-its-advertised.patch
 alsa-ali5451-remove-irq-enabling-in-pointer-callback.patch
 alsa-rme32.c-irq-enabling-after-spin_lock_irq.patch
+tty-prevent-deadlock-in-n_gsm-driver.patch
+tty-set_termios-set_termiox-should-not-return-eintr.patch
+usb-serial-fix-null-pointer-dereferences-on-disconnect.patch
diff --git a/queue-3.4/tty-prevent-deadlock-in-n_gsm-driver.patch b/queue-3.4/tty-prevent-deadlock-in-n_gsm-driver.patch
new file mode 100644 (file)
index 0000000..cc37a26
--- /dev/null
@@ -0,0 +1,195 @@
+From 4d9b109060f690f5c835130ff54165ae157b3087 Mon Sep 17 00:00:00 2001
+From: Dirkjan Bussink <d.bussink@gmail.com>
+Date: Wed, 30 Jan 2013 11:44:50 +0100
+Subject: tty: Prevent deadlock in n_gsm driver
+
+From: Dirkjan Bussink <d.bussink@gmail.com>
+
+commit 4d9b109060f690f5c835130ff54165ae157b3087 upstream.
+
+This change fixes a deadlock when the multiplexer is closed while there
+are still client side ports open.
+
+When the multiplexer is closed and there are active tty's it tries to
+close them with tty_vhangup. This has a problem though, because
+tty_vhangup needs the tty_lock. This patch changes it to unlock the
+tty_lock before attempting the hangup and relocks afterwards. The
+additional call to tty_port_tty_set is needed because otherwise the
+port stays active because of the reference counter.
+
+This change also exposed another problem that other code paths don't
+expect that the multiplexer could have been closed. This patch also adds
+checks for these cases in the gsmtty_ class of function that could be
+called.
+
+The documentation explicitly states that "first close all virtual ports
+before closing the physical port" but we've found this to not always
+reality in our field situations. The GPRS / UTMS modem sometimes crashes
+and needs a power cycle in that case which means cleanly shutting down
+everything is not always possible. This change makes it much more robust
+for our situation where at least the system is recoverable with this patch
+and doesn't hang in a deadlock situation inside the kernel.
+
+The patch is against the long term support kernel (3.4.27) and should
+apply cleanly to more recent branches. Tested with a Telit GE864-QUADV2
+and Telit HE910 modem.
+
+Signed-off-by: Dirkjan Bussink <dirkjan.bussink@nedap.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/n_gsm.c |   42 +++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 41 insertions(+), 1 deletion(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -1692,6 +1692,8 @@ static inline void dlci_put(struct gsm_d
+       kref_put(&dlci->ref, gsm_dlci_free);
+ }
++static void gsm_destroy_network(struct gsm_dlci *dlci);
++
+ /**
+  *    gsm_dlci_release                -       release DLCI
+  *    @dlci: DLCI to destroy
+@@ -1705,9 +1707,19 @@ static void gsm_dlci_release(struct gsm_
+ {
+       struct tty_struct *tty = tty_port_tty_get(&dlci->port);
+       if (tty) {
++              mutex_lock(&dlci->mutex);
++              gsm_destroy_network(dlci);
++              mutex_unlock(&dlci->mutex);
++
++              /* tty_vhangup needs the tty_lock, so unlock and
++                 relock after doing the hangup. */
++              tty_unlock();
+               tty_vhangup(tty);
++              tty_lock();
++              tty_port_tty_set(&dlci->port, NULL);
+               tty_kref_put(tty);
+       }
++      dlci->state = DLCI_CLOSED;
+       dlci_put(dlci);
+ }
+@@ -2933,6 +2945,8 @@ static void gsmtty_close(struct tty_stru
+       if (dlci == NULL)
+               return;
++      if (dlci->state == DLCI_CLOSED)
++              return;
+       mutex_lock(&dlci->mutex);
+       gsm_destroy_network(dlci);
+       mutex_unlock(&dlci->mutex);
+@@ -2951,6 +2965,8 @@ out:
+ static void gsmtty_hangup(struct tty_struct *tty)
+ {
+       struct gsm_dlci *dlci = tty->driver_data;
++      if (dlci->state == DLCI_CLOSED)
++              return;
+       tty_port_hangup(&dlci->port);
+       gsm_dlci_begin_close(dlci);
+ }
+@@ -2958,9 +2974,12 @@ static void gsmtty_hangup(struct tty_str
+ static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf,
+                                                                   int len)
+ {
++      int sent;
+       struct gsm_dlci *dlci = tty->driver_data;
++      if (dlci->state == DLCI_CLOSED)
++              return -EINVAL;
+       /* Stuff the bytes into the fifo queue */
+-      int sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock);
++      sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock);
+       /* Need to kick the channel */
+       gsm_dlci_data_kick(dlci);
+       return sent;
+@@ -2969,18 +2988,24 @@ static int gsmtty_write(struct tty_struc
+ static int gsmtty_write_room(struct tty_struct *tty)
+ {
+       struct gsm_dlci *dlci = tty->driver_data;
++      if (dlci->state == DLCI_CLOSED)
++              return -EINVAL;
+       return TX_SIZE - kfifo_len(dlci->fifo);
+ }
+ static int gsmtty_chars_in_buffer(struct tty_struct *tty)
+ {
+       struct gsm_dlci *dlci = tty->driver_data;
++      if (dlci->state == DLCI_CLOSED)
++              return -EINVAL;
+       return kfifo_len(dlci->fifo);
+ }
+ static void gsmtty_flush_buffer(struct tty_struct *tty)
+ {
+       struct gsm_dlci *dlci = tty->driver_data;
++      if (dlci->state == DLCI_CLOSED)
++              return;
+       /* Caution needed: If we implement reliable transport classes
+          then the data being transmitted can't simply be junked once
+          it has first hit the stack. Until then we can just blow it
+@@ -2999,6 +3024,8 @@ static void gsmtty_wait_until_sent(struc
+ static int gsmtty_tiocmget(struct tty_struct *tty)
+ {
+       struct gsm_dlci *dlci = tty->driver_data;
++      if (dlci->state == DLCI_CLOSED)
++              return -EINVAL;
+       return dlci->modem_rx;
+ }
+@@ -3008,6 +3035,8 @@ static int gsmtty_tiocmset(struct tty_st
+       struct gsm_dlci *dlci = tty->driver_data;
+       unsigned int modem_tx = dlci->modem_tx;
++      if (dlci->state == DLCI_CLOSED)
++              return -EINVAL;
+       modem_tx &= ~clear;
+       modem_tx |= set;
+@@ -3026,6 +3055,8 @@ static int gsmtty_ioctl(struct tty_struc
+       struct gsm_netconfig nc;
+       int index;
++      if (dlci->state == DLCI_CLOSED)
++              return -EINVAL;
+       switch (cmd) {
+       case GSMIOC_ENABLE_NET:
+               if (copy_from_user(&nc, (void __user *)arg, sizeof(nc)))
+@@ -3052,6 +3083,9 @@ static int gsmtty_ioctl(struct tty_struc
+ static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old)
+ {
++      struct gsm_dlci *dlci = tty->driver_data;
++      if (dlci->state == DLCI_CLOSED)
++              return;
+       /* For the moment its fixed. In actual fact the speed information
+          for the virtual channel can be propogated in both directions by
+          the RPN control message. This however rapidly gets nasty as we
+@@ -3063,6 +3097,8 @@ static void gsmtty_set_termios(struct tt
+ static void gsmtty_throttle(struct tty_struct *tty)
+ {
+       struct gsm_dlci *dlci = tty->driver_data;
++      if (dlci->state == DLCI_CLOSED)
++              return;
+       if (tty->termios->c_cflag & CRTSCTS)
+               dlci->modem_tx &= ~TIOCM_DTR;
+       dlci->throttled = 1;
+@@ -3073,6 +3109,8 @@ static void gsmtty_throttle(struct tty_s
+ static void gsmtty_unthrottle(struct tty_struct *tty)
+ {
+       struct gsm_dlci *dlci = tty->driver_data;
++      if (dlci->state == DLCI_CLOSED)
++              return;
+       if (tty->termios->c_cflag & CRTSCTS)
+               dlci->modem_tx |= TIOCM_DTR;
+       dlci->throttled = 0;
+@@ -3084,6 +3122,8 @@ static int gsmtty_break_ctl(struct tty_s
+ {
+       struct gsm_dlci *dlci = tty->driver_data;
+       int encode = 0; /* Off */
++      if (dlci->state == DLCI_CLOSED)
++              return -EINVAL;
+       if (state == -1)        /* "On indefinitely" - we can't encode this
+                                   properly */
diff --git a/queue-3.4/tty-set_termios-set_termiox-should-not-return-eintr.patch b/queue-3.4/tty-set_termios-set_termiox-should-not-return-eintr.patch
new file mode 100644 (file)
index 0000000..aaeef66
--- /dev/null
@@ -0,0 +1,77 @@
+From 183d95cdd834381c594d3aa801c1f9f9c0c54fa9 Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@redhat.com>
+Date: Tue, 29 Jan 2013 20:07:41 +0100
+Subject: tty: set_termios/set_termiox should not return -EINTR
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+commit 183d95cdd834381c594d3aa801c1f9f9c0c54fa9 upstream.
+
+See https://bugzilla.redhat.com/show_bug.cgi?id=904907
+read command causes bash to abort with double free or corruption (out).
+
+A simple test-case from Roman:
+
+       // Compile the reproducer and send sigchld ti that process.
+       // EINTR occurs even if SA_RESTART flag is set.
+
+       void handler(int sig)
+       {
+       }
+
+       main()
+       {
+         struct sigaction act;
+         act.sa_handler = handler;
+         act.sa_flags = SA_RESTART;
+         sigaction (SIGCHLD, &act, 0);
+         struct termio ttp;
+         ioctl(0, TCGETA, &ttp);
+         while(1)
+         {
+           if (ioctl(0, TCSETAW, ttp) < 0)
+             {
+               if (errno == EINTR)
+               {
+                 fprintf(stderr, "BUG!"); return(1);
+               }
+             }
+         }
+       }
+
+Change set_termios/set_termiox to return -ERESTARTSYS to fix this
+particular problem.
+
+I didn't dare to change other EINTR's in drivers/tty/, but they look
+equally wrong.
+
+Reported-by: Roman Rakus <rrakus@redhat.com>
+Reported-by: Lingzhu Xiang <lxiang@redhat.com>
+Signed-off-by: Oleg Nesterov <oleg@redhat.com>
+Cc: Jiri Slaby <jslaby@suse.cz>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/tty_ioctl.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/tty/tty_ioctl.c
++++ b/drivers/tty/tty_ioctl.c
+@@ -617,7 +617,7 @@ static int set_termios(struct tty_struct
+       if (opt & TERMIOS_WAIT) {
+               tty_wait_until_sent(tty, 0);
+               if (signal_pending(current))
+-                      return -EINTR;
++                      return -ERESTARTSYS;
+       }
+       tty_set_termios(tty, &tmp_termios);
+@@ -684,7 +684,7 @@ static int set_termiox(struct tty_struct
+       if (opt & TERMIOS_WAIT) {
+               tty_wait_until_sent(tty, 0);
+               if (signal_pending(current))
+-                      return -EINTR;
++                      return -ERESTARTSYS;
+       }
+       mutex_lock(&tty->termios_mutex);
diff --git a/queue-3.4/usb-serial-fix-null-pointer-dereferences-on-disconnect.patch b/queue-3.4/usb-serial-fix-null-pointer-dereferences-on-disconnect.patch
new file mode 100644 (file)
index 0000000..2725a65
--- /dev/null
@@ -0,0 +1,207 @@
+From b2ca699076573c94fee9a73cb0d8645383b602a0 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Wed, 13 Feb 2013 17:53:28 +0100
+Subject: USB: serial: fix null-pointer dereferences on disconnect
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit b2ca699076573c94fee9a73cb0d8645383b602a0 upstream.
+
+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>
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/ftdi_sio.c   |   20 +++++++++-----------
+ drivers/usb/serial/mct_u232.c   |   22 +++++++++-------------
+ drivers/usb/serial/sierra.c     |    8 +-------
+ drivers/usb/serial/ssu100.c     |   19 ++++++++-----------
+ drivers/usb/serial/usb-serial.c |   14 ++++++++++++--
+ drivers/usb/serial/usb_wwan.c   |    8 +++-----
+ 6 files changed, 42 insertions(+), 49 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -1919,24 +1919,22 @@ static void ftdi_dtr_rts(struct usb_seri
+ {
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
+-      mutex_lock(&port->serial->disc_mutex);
+-      if (!port->serial->disconnected) {
+-              /* Disable flow control */
+-              if (!on && usb_control_msg(port->serial->dev,
++      /* Disable flow control */
++      if (!on) {
++              if (usb_control_msg(port->serial->dev,
+                           usb_sndctrlpipe(port->serial->dev, 0),
+                           FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+                           FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+                           0, priv->interface, NULL, 0,
+                           WDR_TIMEOUT) < 0) {
+-                          dev_err(&port->dev, "error from flowcontrol urb\n");
++                      dev_err(&port->dev, "error from flowcontrol urb\n");
+               }
+-              /* drop RTS and DTR */
+-              if (on)
+-                      set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+-              else
+-                      clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+       }
+-      mutex_unlock(&port->serial->disc_mutex);
++      /* drop RTS and DTR */
++      if (on)
++              set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
++      else
++              clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+ }
+ /*
+--- a/drivers/usb/serial/mct_u232.c
++++ b/drivers/usb/serial/mct_u232.c
+@@ -514,19 +514,15 @@ static void mct_u232_dtr_rts(struct usb_
+       unsigned int control_state;
+       struct mct_u232_private *priv = usb_get_serial_port_data(port);
+-      mutex_lock(&port->serial->disc_mutex);
+-      if (!port->serial->disconnected) {
+-              /* drop DTR and RTS */
+-              spin_lock_irq(&priv->lock);
+-              if (on)
+-                      priv->control_state |= TIOCM_DTR | TIOCM_RTS;
+-              else
+-                      priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+-              control_state = priv->control_state;
+-              spin_unlock_irq(&priv->lock);
+-              mct_u232_set_modem_ctrl(port->serial, control_state);
+-      }
+-      mutex_unlock(&port->serial->disc_mutex);
++      spin_lock_irq(&priv->lock);
++      if (on)
++              priv->control_state |= TIOCM_DTR | TIOCM_RTS;
++      else
++              priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
++      control_state = priv->control_state;
++      spin_unlock_irq(&priv->lock);
++
++      mct_u232_set_modem_ctrl(port->serial, control_state);
+ }
+ static void mct_u232_close(struct usb_serial_port *port)
+--- a/drivers/usb/serial/sierra.c
++++ b/drivers/usb/serial/sierra.c
+@@ -890,19 +890,13 @@ static int sierra_open(struct tty_struct
+ static void sierra_dtr_rts(struct usb_serial_port *port, int on)
+ {
+-      struct usb_serial *serial = port->serial;
+       struct sierra_port_private *portdata;
+       portdata = usb_get_serial_port_data(port);
+       portdata->rts_state = on;
+       portdata->dtr_state = on;
+-      if (serial->dev) {
+-              mutex_lock(&serial->disc_mutex);
+-              if (!serial->disconnected)
+-                      sierra_send_setup(port);
+-              mutex_unlock(&serial->disc_mutex);
+-      }
++      sierra_send_setup(port);
+ }
+ static int sierra_startup(struct usb_serial *serial)
+--- a/drivers/usb/serial/ssu100.c
++++ b/drivers/usb/serial/ssu100.c
+@@ -532,19 +532,16 @@ static void ssu100_dtr_rts(struct usb_se
+       dbg("%s\n", __func__);
+-      mutex_lock(&port->serial->disc_mutex);
+-      if (!port->serial->disconnected) {
+-              /* Disable flow control */
+-              if (!on &&
+-                  ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
++      /* Disable flow control */
++      if (!on) {
++              if (ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
+                       dev_err(&port->dev, "error from flowcontrol urb\n");
+-              /* drop RTS and DTR */
+-              if (on)
+-                      set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+-              else
+-                      clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+       }
+-      mutex_unlock(&port->serial->disc_mutex);
++      /* drop RTS and DTR */
++      if (on)
++              set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
++      else
++              clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+ }
+ static void ssu100_update_msr(struct usb_serial_port *port, u8 msr)
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -699,10 +699,20 @@ static int serial_carrier_raised(struct
+ static void serial_dtr_rts(struct tty_port *port, int on)
+ {
+       struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
+-      struct usb_serial_driver *drv = p->serial->type;
++      struct usb_serial *serial = p->serial;
++      struct usb_serial_driver *drv = serial->type;
+-      if (drv->dtr_rts)
++      if (!drv->dtr_rts)
++              return;
++      /*
++       * Work-around bug in the tty-layer which can result in dtr_rts
++       * being called after a disconnect (and tty_unregister_device
++       * has returned). Remove once bug has been squashed.
++       */
++      mutex_lock(&serial->disc_mutex);
++      if (!serial->disconnected)
+               drv->dtr_rts(p, on);
++      mutex_unlock(&serial->disc_mutex);
+ }
+ static const struct tty_port_operations serial_port_ops = {
+--- a/drivers/usb/serial/usb_wwan.c
++++ b/drivers/usb/serial/usb_wwan.c
+@@ -41,7 +41,6 @@ static bool debug;
+ void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
+ {
+-      struct usb_serial *serial = port->serial;
+       struct usb_wwan_port_private *portdata;
+       struct usb_wwan_intf_private *intfdata;
+@@ -54,12 +53,11 @@ void usb_wwan_dtr_rts(struct usb_serial_
+               return;
+       portdata = usb_get_serial_port_data(port);
+-      mutex_lock(&serial->disc_mutex);
++      /* FIXME: locking */
+       portdata->rts_state = on;
+       portdata->dtr_state = on;
+-      if (serial->dev)
+-              intfdata->send_setup(port);
+-      mutex_unlock(&serial->disc_mutex);
++
++      intfdata->send_setup(port);
+ }
+ EXPORT_SYMBOL(usb_wwan_dtr_rts);