From 416c0ea84f822af672b8a60dc0e1de92bae70d25 Mon Sep 17 00:00:00 2001 From: "William A. Kennington III" Date: Tue, 5 May 2026 06:12:49 +0000 Subject: [PATCH] [transport] usb: Fix htool crash on USB disconnect Previously, libhoth_usb_fifo_run_transfers() could return while one or more libusb transfers were still pending. This happened if the second transfer submission failed, or if the event handling loop was interrupted by a signal (e.g. SIGINT). When htool subsequently attempted to close and reopen the transport, it would call libusb_free_transfer() on these pending transfers, triggering an assertion failure in libusb: 'usbi_mutex_lock: Assertion pthread_mutex_lock(mutex) == 0 failed' This change ensures that: 1. All submitted transfers are completed (success, error, or cancel) before the function returns. 2. If a transfer submission fails, any other successfully submitted transfer is cancelled and waited for. 3. Signal interruptions do not cause an early return while transfers are pending. 4. Completion flags are correctly initialized on open. Verified on yutulis-ru4-bmc-01 with 1000 iterations of target reset spam without a crash. Signed-off-by: William A. Kennington III --- transports/libhoth_usb_fifo.c | 42 +++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/transports/libhoth_usb_fifo.c b/transports/libhoth_usb_fifo.c index b41f3f9..6538f22 100644 --- a/transports/libhoth_usb_fifo.c +++ b/transports/libhoth_usb_fifo.c @@ -29,31 +29,50 @@ static int libhoth_usb_fifo_run_transfers(struct libhoth_usb_device* dev, bool out, bool in) { + int status; struct libhoth_usb_fifo* drvdata = &dev->driver_data.fifo; - drvdata->all_transfers_completed = 0; - drvdata->out_transfer_completed = !out; - drvdata->in_transfer_completed = !in; + drvdata->all_transfers_completed = 1; + drvdata->out_transfer_completed = true; + drvdata->in_transfer_completed = true; if (in) { - int status = libusb_submit_transfer(drvdata->in_transfer); + status = libusb_submit_transfer(drvdata->in_transfer); if (status != LIBUSB_SUCCESS) { - return status; + goto out; } + drvdata->all_transfers_completed = 0; + drvdata->in_transfer_completed = false; } if (out) { int status = libusb_submit_transfer(drvdata->out_transfer); if (status != LIBUSB_SUCCESS) { - return status; + goto out; } + drvdata->all_transfers_completed = 0; + drvdata->out_transfer_completed = false; } while (drvdata->all_transfers_completed == 0) { int status = libusb_handle_events_completed( dev->ctx, &drvdata->all_transfers_completed); - if (status == LIBUSB_ERROR_INTERRUPTED) { - return status; + if (status != LIBUSB_SUCCESS && status != LIBUSB_ERROR_INTERRUPTED) { + goto out; } } - return LIBHOTH_OK; + drvdata->in_transfer_completed = true; + drvdata->out_transfer_completed = true; + status = LIBHOTH_OK; + +out: + if (!drvdata->in_transfer_completed) { + libusb_cancel_transfer(drvdata->in_transfer); + } + if (!drvdata->out_transfer_completed) { + libusb_cancel_transfer(drvdata->out_transfer); + } + while (drvdata->all_transfers_completed == 0) { + libusb_handle_events_completed(dev->ctx, &drvdata->all_transfers_completed); + } + return status; } static void fifo_transfer_callback(struct libusb_transfer* transfer) { @@ -63,6 +82,7 @@ static void fifo_transfer_callback(struct libusb_transfer* transfer) { if (transfer == drvdata->in_transfer) { drvdata->in_transfer_completed = true; if (transfer->status != LIBUSB_TRANSFER_COMPLETED && + transfer->status != LIBUSB_TRANSFER_CANCELLED && !drvdata->out_transfer_completed) { // Cancel pending OUT transfer so we don't get stuck waiting forever // inside libusb_handle_events_completed(). @@ -72,6 +92,7 @@ static void fifo_transfer_callback(struct libusb_transfer* transfer) { if (transfer == drvdata->out_transfer) { drvdata->out_transfer_completed = true; if (transfer->status != LIBUSB_TRANSFER_COMPLETED && + transfer->status != LIBUSB_TRANSFER_CANCELLED && !drvdata->in_transfer_completed) { // Cancel pending IN transfer so we don't get stuck waiting forever // inside libusb_handle_events_completed(). @@ -159,6 +180,9 @@ int libhoth_usb_fifo_open(struct libhoth_usb_device* dev, goto err_out; } drvdata->prng_state = prng_seed; + drvdata->in_transfer_completed = true; + drvdata->out_transfer_completed = true; + drvdata->all_transfers_completed = 1; return LIBHOTH_OK; err_out: if (drvdata->in_buffer != NULL) free(drvdata->in_buffer);