aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2021-01-03 19:12:41 +0000
committerMichael Brown <mcb30@ipxe.org>2021-01-03 20:23:51 +0000
commit5aa389593dded9a45a2d2b83f3c1c65b2770fb82 (patch)
treec89e3b4c015795f20ad6f8d583881994a65c5435
parent7ce3b8405002ac58c4c2d24b90a601b1802c1d83 (diff)
downloadipxe-5aa389593dded9a45a2d2b83f3c1c65b2770fb82.zip
ipxe-5aa389593dded9a45a2d2b83f3c1c65b2770fb82.tar.gz
ipxe-5aa389593dded9a45a2d2b83f3c1c65b2770fb82.tar.bz2
[efi] Leave asynchronous USB endpoints open until device is removed
Some UEFI device drivers will react to an asynchronous USB transfer failure by dubiously terminating the scheduled transfer from within the completion handler. We already have code from commit fbb776f ("[efi] Leave USB endpoint descriptors in existence until device is removed") that avoids freeing memory in this situation, in order to avoid use-after-free bugs. This is not sufficient to avoid potential problems, since with an xHCI controller the act of closing the endpoint requires issuing a command and awaiting completion via the event ring, which may in turn dispatch further USB transfer completion events. Avoid these problems by leaving the USB endpoint open (but with the refill timer stopped) until the device is finally removed, as is already done for control and bulk transfers. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/interface/efi/efi_usb.c24
1 files changed, 13 insertions, 11 deletions
diff --git a/src/interface/efi/efi_usb.c b/src/interface/efi/efi_usb.c
index df66df4..28dfc86 100644
--- a/src/interface/efi/efi_usb.c
+++ b/src/interface/efi/efi_usb.c
@@ -415,9 +415,11 @@ static void efi_usb_async_complete ( struct usb_endpoint *ep,
/* Construct status */
status = ( ( rc == 0 ) ? 0 : EFI_USB_ERR_SYSTEM );
- /* Report completion */
- usbep->callback ( iobuf->data, iob_len ( iobuf ), usbep->context,
- status );
+ /* Report completion, if applicable */
+ if ( usbep->callback ) {
+ usbep->callback ( iobuf->data, iob_len ( iobuf ),
+ usbep->context, status );
+ }
drop:
/* Recycle or free I/O buffer */
@@ -456,11 +458,9 @@ static int efi_usb_async_start ( struct efi_usb_interface *usbintf,
EFI_STATUS efirc;
int rc;
- /* Fail if endpoint is already open */
- if ( efi_usb_is_open ( usbintf, endpoint ) ) {
- rc = -EINVAL;
- goto err_already_open;
- }
+ /* Close endpoint, if applicable */
+ if ( efi_usb_is_open ( usbintf, endpoint ) )
+ efi_usb_close ( usbintf->endpoint[index] );
/* Open endpoint */
if ( ( rc = efi_usb_open ( usbintf, endpoint,
@@ -497,9 +497,10 @@ static int efi_usb_async_start ( struct efi_usb_interface *usbintf,
bs->SetTimer ( usbep->event, TimerCancel, 0 );
err_timer:
err_prefill:
+ usbep->callback = NULL;
+ usbep->context = NULL;
efi_usb_close ( usbep );
err_open:
- err_already_open:
return rc;
}
@@ -523,8 +524,9 @@ static void efi_usb_async_stop ( struct efi_usb_interface *usbintf,
/* Stop timer */
bs->SetTimer ( usbep->event, TimerCancel, 0 );
- /* Close endpoint */
- efi_usb_close ( usbep );
+ /* Clear callback parameters */
+ usbep->callback = NULL;
+ usbep->context = NULL;
}
/******************************************************************************