diff --git a/third_party/libusb/src/libusb/os/darwin_usb.c b/third_party/libusb/src/libusb/os/darwin_usb.c --- a/third_party/libusb/src/libusb/os/darwin_usb.c +++ b/third_party/libusb/src/libusb/os/darwin_usb.c @@ -1668,22 +1668,35 @@ static int cancel_control_transfer(struct usbi_transfer *itransfer) { static int darwin_abort_transfers (struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv; struct darwin_interface *cInterface; uint8_t pipeRef, iface; IOReturn kresult; + /* Serialise against libusb_release_interface(), which holds dev_handle->lock + * across darwin_release_interface(). libusb_cancel_transfer() only holds + * itransfer->lock here, so without this we can race the Release()/NULLing of + * cInterface->interface and dereference a freed IOKit COM proxy. No path + * takes dev_handle->lock then itransfer->lock, so the itransfer->lock -> + * dev_handle->lock order introduced here is deadlock-free. */ + usbi_mutex_lock(&transfer->dev_handle->lock); + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) { usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + usbi_mutex_unlock(&transfer->dev_handle->lock); return LIBUSB_ERROR_NOT_FOUND; } cInterface = &priv->interfaces[iface]; - if (!dpriv->device) + if (!dpriv->device || !cInterface->interface) { + usbi_mutex_unlock(&transfer->dev_handle->lock); return LIBUSB_ERROR_NO_DEVICE; + } usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions on interface %d pipe %d", iface, pipeRef); @@ -1694,6 +1707,8 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) { /* newer versions of darwin support clearing additional bits on the device's endpoint */ kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef); + + usbi_mutex_unlock(&transfer->dev_handle->lock); return darwin_to_libusb (kresult); }