Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiri Slaby <jslaby@suse.cz>2019-05-17 06:38:43 +0200
committerJiri Slaby <jslaby@suse.cz>2019-05-17 06:38:46 +0200
commit310811c4873657542e8e6f0aea36559e80cd045e (patch)
tree31be243c0165dbc6171462ed7bbb7735cf2de456
parent8a6caf254408bc96d10187cd026e6159e4fb12de (diff)
USB: serial: fix unthrottle races (bnc#1012628).
-rw-r--r--patches.kernel.org/5.1.3-010-USB-serial-fix-unthrottle-races.patch137
-rw-r--r--series.conf1
2 files changed, 138 insertions, 0 deletions
diff --git a/patches.kernel.org/5.1.3-010-USB-serial-fix-unthrottle-races.patch b/patches.kernel.org/5.1.3-010-USB-serial-fix-unthrottle-races.patch
new file mode 100644
index 0000000000..bebf1a3d2a
--- /dev/null
+++ b/patches.kernel.org/5.1.3-010-USB-serial-fix-unthrottle-races.patch
@@ -0,0 +1,137 @@
+From: Johan Hovold <johan@kernel.org>
+Date: Thu, 25 Apr 2019 18:05:36 +0200
+Subject: [PATCH] USB: serial: fix unthrottle races
+References: bnc#1012628
+Patch-mainline: 5.1.3
+Git-commit: 3f5edd58d040bfa4b74fb89bc02f0bc6b9cd06ab
+
+commit 3f5edd58d040bfa4b74fb89bc02f0bc6b9cd06ab upstream.
+
+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: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Jiri Slaby <jslaby@suse.cz>
+---
+ 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 2274d9625f63..0fff4968ea1b 100644
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -376,6 +376,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;
+
+@@ -383,33 +384,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;
+@@ -484,6 +503,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.21.0
+
diff --git a/series.conf b/series.conf
index 437ab1da63..17a1b47e7e 100644
--- a/series.conf
+++ b/series.conf
@@ -95,6 +95,7 @@
patches.kernel.org/5.1.3-007-i2c-core-ratelimit-transfer-when-suspended-erro.patch
patches.kernel.org/5.1.3-008-kernfs-fix-barrier-usage-in-__kernfs_new_node.patch
patches.kernel.org/5.1.3-009-virt-vbox-Sanity-check-parameter-types-for-hgcm.patch
+ patches.kernel.org/5.1.3-010-USB-serial-fix-unthrottle-races.patch
########################################################
# Build fixes that apply to the vanilla kernel too.