| commit d5240ff3e8485745e10b498236834177cd99edb9 |
| Author: Fletcher Woodruff <fletcherw@chromium.org> |
| Date: Mon Jun 29 13:23:23 2020 -0600 |
| |
| usb: fix race condition in usb_conn_acquire |
| |
| If we do not have an available usb connection at the start of |
| usb_conn_acquire, we sleep and poll for several seconds until a |
| connection becomes available. |
| |
| However, this check is done without holding the pool_manage_lock. This |
| allows for a race condition where we read num_avail and get a non-zero |
| value, but then some other thread acquires the pool_manage_lock first |
| and claims that free interface, causing us to error out when we then try |
| to claim the interface. |
| |
| To fix this, acquire the pool_manage_lock before checking num_avail. This way, |
| when we exit our poll loop that waits for an available interface, we will be |
| certain that interface will continue to be available until we claim it. |
| |
| diff --git a/src/usb.c b/src/usb.c |
| index 905b90b..9412bb0 100644 |
| --- a/src/usb.c |
| +++ b/src/usb.c |
| @@ -656,19 +656,6 @@ struct usb_conn_t *usb_conn_acquire(struct usb_sock_t *usb) |
| { |
| int i; |
| |
| - if (usb->num_avail <= 0) { |
| - NOTE("All USB interfaces busy, waiting ..."); |
| - for (i = 0; i < 30 && usb->num_avail <= 0; i ++) { |
| - if (get_terminate(&g_options)) |
| - return NULL; |
| - usleep(100000); |
| - } |
| - if (usb->num_avail <= 0) { |
| - ERR("Timed out waiting for a free USB interface"); |
| - return NULL; |
| - } |
| - } |
| - |
| struct usb_conn_t *conn = calloc(1, sizeof(*conn)); |
| if (conn == NULL) { |
| ERR("Failed to alloc space for usb connection"); |
| @@ -677,6 +664,23 @@ struct usb_conn_t *usb_conn_acquire(struct usb_sock_t *usb) |
| |
| sem_wait(&usb->pool_manage_lock); |
| { |
| + if (usb->num_avail <= 0) { |
| + NOTE("All USB interfaces busy, waiting ..."); |
| + for (i = 0; i < 30 && usb->num_avail <= 0; i ++) { |
| + if (get_terminate(&g_options)) |
| + goto acquire_error; |
| + |
| + // Release the lock while we sleep. |
| + sem_post(&usb->pool_manage_lock); |
| + usleep(100000); |
| + sem_wait(&usb->pool_manage_lock); |
| + } |
| + if (usb->num_avail <= 0) { |
| + ERR("Timed out waiting for a free USB interface"); |
| + goto acquire_error; |
| + } |
| + } |
| + |
| conn->parent = usb; |
| |
| uint32_t slot = usb->num_taken; |