From 3aa7a2352513fba20ba550917f7955797ed432a6 Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Sat, 12 Jun 2021 14:07:25 +0000 Subject: [PATCH] usb(4): Fix races between usbd_open_pipe* and usbd_set_interface. --- sys/dev/usb/usb_subr.c | 5 +++++ sys/dev/usb/usbdi.c | 21 +++++++++++++++------ sys/dev/usb/usbdivar.h | 1 + 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/sys/dev/usb/usb_subr.c b/sys/dev/usb/usb_subr.c index abdd27502fca..40434b8a8544 100644 --- a/sys/dev/usb/usb_subr.c +++ b/sys/dev/usb/usb_subr.c @@ -489,6 +489,7 @@ usbd_fill_iface_data(struct usbd_device *dev, int ifaceidx, int altidx) } #undef ed LIST_INIT(&ifc->ui_pipes); + mutex_init(&ifc->ui_pipelock, MUTEX_DEFAULT, IPL_NONE); return USBD_NORMAL_COMPLETION; bad: @@ -503,6 +504,10 @@ void usbd_free_iface_data(struct usbd_device *dev, int ifcno) { struct usbd_interface *ifc = &dev->ud_ifaces[ifcno]; + + KASSERT(LIST_EMPTY(&ifc->ui_pipes)); + mutex_destroy(&ifc->ui_pipelock); + if (ifc->ui_endpoints) { int nendpt = ifc->ui_idesc->bNumEndpoints; size_t sz = nendpt * sizeof(struct usbd_endpoint); diff --git a/sys/dev/usb/usbdi.c b/sys/dev/usb/usbdi.c index 59e4f42dc59b..ebaf27628ca8 100644 --- a/sys/dev/usb/usbdi.c +++ b/sys/dev/usb/usbdi.c @@ -244,7 +244,9 @@ usbd_open_pipe_ival(struct usbd_interface *iface, uint8_t address, err = usbd_setup_pipe_flags(iface->ui_dev, iface, ep, ival, &p, flags); if (err) return err; + mutex_enter(&iface->ui_pipelock); LIST_INSERT_HEAD(&iface->ui_pipes, p, up_next); + mutex_exit(&iface->ui_pipelock); *pipe = p; SDT_PROBE5(usb, device, pipe, open, iface, address, flags, ival, p); @@ -313,13 +315,14 @@ usbd_close_pipe(struct usbd_pipe *pipe) KASSERT(SIMPLEQ_EMPTY(&pipe->up_queue)); - LIST_REMOVE(pipe, up_next); - pipe->up_methods->upm_close(pipe); usbd_unlock_pipe(pipe); if (pipe->up_intrxfer != NULL) usbd_destroy_xfer(pipe->up_intrxfer); + mutex_enter(&pipe->up_iface->ui_pipelock); + LIST_REMOVE(pipe, up_next); + mutex_exit(&pipe->up_iface->ui_pipelock); usb_rem_task_wait(pipe->up_dev, &pipe->up_async_task, USB_TASKQ_DRIVER, NULL); usbd_endpoint_release(pipe->up_dev, pipe->up_endpoint); @@ -872,8 +875,11 @@ usbd_set_interface(struct usbd_interface *iface, int altidx) USBHIST_FUNC(); - if (LIST_FIRST(&iface->ui_pipes) != NULL) - return USBD_IN_USE; + mutex_enter(&iface->ui_pipelock); + if (LIST_FIRST(&iface->ui_pipes) != NULL) { + err = USBD_IN_USE; + goto out; + } endpoints = iface->ui_endpoints; int nendpt = iface->ui_idesc->bNumEndpoints; @@ -882,7 +888,7 @@ usbd_set_interface(struct usbd_interface *iface, int altidx) iface->ui_idesc->bNumEndpoints, 0); err = usbd_fill_iface_data(iface->ui_dev, iface->ui_index, altidx); if (err) - return err; + goto out; /* new setting works, we can free old endpoints */ if (endpoints != NULL) { @@ -897,7 +903,10 @@ usbd_set_interface(struct usbd_interface *iface, int altidx) USETW(req.wValue, iface->ui_idesc->bAlternateSetting); USETW(req.wIndex, iface->ui_idesc->bInterfaceNumber); USETW(req.wLength, 0); - return usbd_do_request(iface->ui_dev, &req, 0); + err = usbd_do_request(iface->ui_dev, &req, 0); + +out: mutex_exit(&iface->ui_pipelock); + return err; } int diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h index 9d48a56f05d3..19e4c2bcb8ff 100644 --- a/sys/dev/usb/usbdivar.h +++ b/sys/dev/usb/usbdivar.h @@ -231,6 +231,7 @@ struct usbd_interface { int ui_altindex; struct usbd_endpoint *ui_endpoints; void *ui_priv; + kmutex_t ui_pipelock; LIST_HEAD(, usbd_pipe) ui_pipes; };