]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 13 May 2019 11:35:49 +0000 (13:35 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 13 May 2019 11:35:49 +0000 (13:35 +0200)
added patches:
usb-serial-fix-unthrottle-races.patch

queue-4.14/series
queue-4.14/usb-serial-fix-unthrottle-races.patch [new file with mode: 0644]

index d115c047537147c7459976ec7ad4ed7ade93aabd..b438f6cc3356e700fd546c1998c439aaec1ba570 100644 (file)
@@ -3,3 +3,4 @@ platform-x86-sony-laptop-fix-unintentional-fall-through.patch
 platform-x86-thinkpad_acpi-disable-bluetooth-for-some-machines.patch
 hwmon-pwm-fan-disable-pwm-if-fetching-cooling-data-fails.patch
 kernfs-fix-barrier-usage-in-__kernfs_new_node.patch
+usb-serial-fix-unthrottle-races.patch
diff --git a/queue-4.14/usb-serial-fix-unthrottle-races.patch b/queue-4.14/usb-serial-fix-unthrottle-races.patch
new file mode 100644 (file)
index 0000000..6ad1b6a
--- /dev/null
@@ -0,0 +1,132 @@
+From 3f5edd58d040bfa4b74fb89bc02f0bc6b9cd06ab 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
+
+From: Johan Hovold <johan@kernel.org>
+
+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>
+
+---
+ drivers/usb/serial/generic.c |   39 ++++++++++++++++++++++++++++++++-------
+ 1 file changed, 32 insertions(+), 7 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -379,6 +379,7 @@ void usb_serial_generic_read_bulk_callba
+       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;
+@@ -386,33 +387,51 @@ void usb_serial_generic_read_bulk_callba
+               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;
+@@ -487,6 +506,12 @@ void usb_serial_generic_unthrottle(struc
+       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);
+ }