core: Fix unlocked access to timeout_flags
authorHans de Goede <hdegoede@redhat.com>
Mon, 6 Jun 2016 15:43:23 +0000 (17:43 +0200)
committerNathan Hjelm <hjelmn@me.com>
Wed, 17 Aug 2016 18:52:40 +0000 (12:52 -0600)
There is a race between handle_timeout() and the completion functions.
When one thread is in handle_timeout() and another thread wakes
up from a poll(), there exists a window where the transfer has been
cancelled, but USBI_TRANSFER_TIMED_OUT is not yet set in timeout_flags.
Therefore, usbi_handle_transfer_completion() is sometimes called
with LIBUSB_TRANSFER_CANCELLED instead of the expected
LIBUSB_TRANSFER_TIMED_OUT.

timeout_flags is protected by the flying_transfers_lock, this commit
makes usbi_handle_transfer_cancellation() take that lock before
checking for USBI_TRANSFER_TIMED_OUT in timeout_flags, fixing this.

Reported-by: Joost Muller <joostmuller@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
libusb/io.c
libusb/version_nano.h

index aa6cfca..b3f7df0 100644 (file)
@@ -1684,8 +1684,15 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
  * will attempt to take the lock. */
 int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer)
 {
+       struct libusb_context *ctx = ITRANSFER_CTX(transfer);
+       uint8_t timed_out;
+
+       usbi_mutex_lock(&ctx->flying_transfers_lock);
+       timed_out = transfer->timeout_flags & USBI_TRANSFER_TIMED_OUT;
+       usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
        /* if the URB was cancelled due to timeout, report timeout to the user */
-       if (transfer->timeout_flags & USBI_TRANSFER_TIMED_OUT) {
+       if (timed_out) {
                usbi_dbg("detected timeout cancellation");
                return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_TIMED_OUT);
        }
index 6995b33..bb7624f 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 11135
+#define LIBUSB_NANO 11137