]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
fixes for 3.18
authorSasha Levin <sashal@kernel.org>
Tue, 14 May 2019 18:25:04 +0000 (14:25 -0400)
committerSasha Levin <sashal@kernel.org>
Tue, 14 May 2019 18:50:11 +0000 (14:50 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-3.18/series
queue-3.18/usb-serial-fix-unthrottle-races.patch [new file with mode: 0644]
queue-3.18/usb-serial-use-variable-for-status.patch [new file with mode: 0644]

index 49976181a4a3f44ea332a8dc1921222d353ffafe..1ec655ae0f64b6f266a4618d843c7b1ad074bc22 100644 (file)
@@ -73,3 +73,5 @@ s390-ctcm-fix-ctcm_new_device-error-return-code.patch
 selftests-net-correct-the-return-value-for-run_netso.patch
 gpu-ipu-v3-dp-fix-csc-handling.patch
 don-t-jump-to-compute_result-state-from-check_result-state.patch
+usb-serial-use-variable-for-status.patch
+usb-serial-fix-unthrottle-races.patch
diff --git a/queue-3.18/usb-serial-fix-unthrottle-races.patch b/queue-3.18/usb-serial-fix-unthrottle-races.patch
new file mode 100644 (file)
index 0000000..b6bd2f2
--- /dev/null
@@ -0,0 +1,134 @@
+From 21cebf8a837cc4fdae7406f107dc4bcefbf41c01 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Thu, 25 Apr 2019 18:05:36 +0200
+Subject: USB: serial: fix unthrottle races
+
+[ Upstream commit 3f5edd58d040bfa4b74fb89bc02f0bc6b9cd06ab ]
+
+Fix two long-standing bugs which could potentially lead to memory
+corruption or leave the port throttled until it is reopened (on weakly
+ordered systems), respectively, when read-URB completion races with
+unthrottle().
+
+First, the URB must not be marked as free before processing is complete
+to prevent it from being submitted by unthrottle() on another CPU.
+
+       CPU 1                           CPU 2
+       ================                ================
+       complete()                      unthrottle()
+         process_urb();
+         smp_mb__before_atomic();
+         set_bit(i, free);               if (test_and_clear_bit(i, free))
+                                                 submit_urb();
+
+Second, the URB must be marked as free before checking the throttled
+flag to prevent unthrottle() on another CPU from failing to observe that
+the URB needs to be submitted if complete() sees that the throttled flag
+is set.
+
+       CPU 1                           CPU 2
+       ================                ================
+       complete()                      unthrottle()
+         set_bit(i, free);               throttled = 0;
+         smp_mb__after_atomic();         smp_mb();
+         if (throttled)                  if (test_and_clear_bit(i, free))
+                 return;                         submit_urb();
+
+Note that test_and_clear_bit() only implies barriers when the test is
+successful. To handle the case where the URB is still in use an explicit
+barrier needs to be added to unthrottle() for the second race condition.
+
+Fixes: d83b405383c9 ("USB: serial: add support for multiple read urbs")
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/serial/generic.c | 39 +++++++++++++++++++++++++++++-------
+ 1 file changed, 32 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
+index a648fdca938a2..0036d96277870 100644
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -350,6 +350,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb)
+       struct usb_serial_port *port = urb->context;
+       unsigned char *data = urb->transfer_buffer;
+       unsigned long flags;
++      bool stopped = false;
+       int status = urb->status;
+       int i;
+@@ -357,33 +358,51 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb)
+               if (urb == port->read_urbs[i])
+                       break;
+       }
+-      set_bit(i, &port->read_urbs_free);
+       dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i,
+                                                       urb->actual_length);
+       switch (status) {
+       case 0:
++              usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
++                                                      data);
++              port->serial->type->process_read_urb(urb);
+               break;
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ESHUTDOWN:
+               dev_dbg(&port->dev, "%s - urb stopped: %d\n",
+                                                       __func__, status);
+-              return;
++              stopped = true;
++              break;
+       case -EPIPE:
+               dev_err(&port->dev, "%s - urb stopped: %d\n",
+                                                       __func__, status);
+-              return;
++              stopped = true;
++              break;
+       default:
+               dev_dbg(&port->dev, "%s - nonzero urb status: %d\n",
+                                                       __func__, status);
+-              goto resubmit;
++              break;
+       }
+-      usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
+-      port->serial->type->process_read_urb(urb);
++      /*
++       * Make sure URB processing is done before marking as free to avoid
++       * racing with unthrottle() on another CPU. Matches the barriers
++       * implied by the test_and_clear_bit() in
++       * usb_serial_generic_submit_read_urb().
++       */
++      smp_mb__before_atomic();
++      set_bit(i, &port->read_urbs_free);
++      /*
++       * Make sure URB is marked as free before checking the throttled flag
++       * to avoid racing with unthrottle() on another CPU. Matches the
++       * smp_mb() in unthrottle().
++       */
++      smp_mb__after_atomic();
++
++      if (stopped)
++              return;
+-resubmit:
+       /* Throttle the device if requested by tty */
+       spin_lock_irqsave(&port->lock, flags);
+       port->throttled = port->throttle_req;
+@@ -458,6 +477,12 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty)
+       port->throttled = port->throttle_req = 0;
+       spin_unlock_irq(&port->lock);
++      /*
++       * Matches the smp_mb__after_atomic() in
++       * usb_serial_generic_read_bulk_callback().
++       */
++      smp_mb();
++
+       if (was_throttled)
+               usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
+ }
+-- 
+2.20.1
+
diff --git a/queue-3.18/usb-serial-use-variable-for-status.patch b/queue-3.18/usb-serial-use-variable-for-status.patch
new file mode 100644 (file)
index 0000000..d744561
--- /dev/null
@@ -0,0 +1,95 @@
+From 11c21cc26063217d3a066e8f74e10d0730c82399 Mon Sep 17 00:00:00 2001
+From: Oliver Neukum <oneukum@suse.com>
+Date: Thu, 14 Jul 2016 15:01:40 +0200
+Subject: USB: serial: use variable for status
+
+[ Upstream commit 3161da970d38cd6ed2ba8cadec93874d1d06e11e ]
+
+This patch turns status in a variable read once from the URB.
+The long term plan is to deliver status to the callback.
+In addition it makes the code a bit more elegant.
+
+Signed-off-by: Oliver Neukum <oneukum@suse.com>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/serial/generic.c | 18 ++++++++++--------
+ 1 file changed, 10 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
+index c44b911937e8d..a648fdca938a2 100644
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -350,6 +350,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb)
+       struct usb_serial_port *port = urb->context;
+       unsigned char *data = urb->transfer_buffer;
+       unsigned long flags;
++      int status = urb->status;
+       int i;
+       for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
+@@ -360,22 +361,22 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb)
+       dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i,
+                                                       urb->actual_length);
+-      switch (urb->status) {
++      switch (status) {
+       case 0:
+               break;
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ESHUTDOWN:
+               dev_dbg(&port->dev, "%s - urb stopped: %d\n",
+-                                                      __func__, urb->status);
++                                                      __func__, status);
+               return;
+       case -EPIPE:
+               dev_err(&port->dev, "%s - urb stopped: %d\n",
+-                                                      __func__, urb->status);
++                                                      __func__, status);
+               return;
+       default:
+               dev_dbg(&port->dev, "%s - nonzero urb status: %d\n",
+-                                                      __func__, urb->status);
++                                                      __func__, status);
+               goto resubmit;
+       }
+@@ -399,6 +400,7 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb)
+ {
+       unsigned long flags;
+       struct usb_serial_port *port = urb->context;
++      int status = urb->status;
+       int i;
+       for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) {
+@@ -410,22 +412,22 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb)
+       set_bit(i, &port->write_urbs_free);
+       spin_unlock_irqrestore(&port->lock, flags);
+-      switch (urb->status) {
++      switch (status) {
+       case 0:
+               break;
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ESHUTDOWN:
+               dev_dbg(&port->dev, "%s - urb stopped: %d\n",
+-                                                      __func__, urb->status);
++                                                      __func__, status);
+               return;
+       case -EPIPE:
+               dev_err_console(port, "%s - urb stopped: %d\n",
+-                                                      __func__, urb->status);
++                                                      __func__, status);
+               return;
+       default:
+               dev_err_console(port, "%s - nonzero urb status: %d\n",
+-                                                      __func__, urb->status);
++                                                      __func__, status);
+               goto resubmit;
+       }
+-- 
+2.20.1
+