diff options
author | hselasky <hselasky@FreeBSD.org> | 2014-01-24 08:10:08 +0000 |
---|---|---|
committer | hselasky <hselasky@FreeBSD.org> | 2014-01-24 08:10:08 +0000 |
commit | 6e5156d6bb6755f251e480703c56db26ea69c02a (patch) | |
tree | 7e6f2f512b9261046f389034ef2aafa0d782d0e8 /sys/dev/usb/usb_device.c | |
parent | f692196495136a8dd592a7f06a940e6fae03fd6d (diff) | |
download | FreeBSD-src-6e5156d6bb6755f251e480703c56db26ea69c02a.zip FreeBSD-src-6e5156d6bb6755f251e480703c56db26ea69c02a.tar.gz |
MFC r260808 and r260814:
- Close a minor deadlock.
- Fix a possible memory use after free and leak situation associated
with USB device detach when using character device handles. This also
includes LibUSB. It turns out that "usb_close()" cannot always get a
reference to clean up its USB transfers and such, if called during the
kernel USB device detach.
Diffstat (limited to 'sys/dev/usb/usb_device.c')
-rw-r--r-- | sys/dev/usb/usb_device.c | 24 |
1 files changed, 14 insertions, 10 deletions
diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c index 3af48d8..76d493c 100644 --- a/sys/dev/usb/usb_device.c +++ b/sys/dev/usb/usb_device.c @@ -2070,6 +2070,8 @@ usb_free_device(struct usb_device *udev, uint8_t flag) DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no); bus = udev->bus; + + /* set DETACHED state to prevent any further references */ usb_set_device_state(udev, USB_STATE_DETACHED); #if USB_HAVE_DEVCTL @@ -2085,16 +2087,7 @@ usb_free_device(struct usb_device *udev, uint8_t flag) usb_free_symlink(udev->ugen_symlink); udev->ugen_symlink = NULL; } -#endif - /* - * Unregister our device first which will prevent any further - * references: - */ - usb_bus_port_set_device(bus, udev->parent_hub ? - udev->parent_hub->hub->ports + udev->port_index : NULL, - NULL, USB_ROOT_HUB_ADDR); -#if USB_HAVE_UGEN /* wait for all pending references to go away: */ mtx_lock(&usb_ref_lock); udev->refcount--; @@ -2114,6 +2107,11 @@ usb_free_device(struct usb_device *udev, uint8_t flag) /* the following will get the device unconfigured in software */ usb_unconfigure(udev, USB_UNCFG_FLAG_FREE_EP0); + /* final device unregister after all character devices are closed */ + usb_bus_port_set_device(bus, udev->parent_hub ? + udev->parent_hub->hub->ports + udev->port_index : NULL, + NULL, USB_ROOT_HUB_ADDR); + /* unsetup any leftover default USB transfers */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); @@ -2647,8 +2645,14 @@ usb_set_device_state(struct usb_device *udev, enum usb_dev_state state) DPRINTF("udev %p state %s -> %s\n", udev, usb_statestr(udev->state), usb_statestr(state)); - udev->state = state; +#if USB_HAVE_UGEN + mtx_lock(&usb_ref_lock); +#endif + udev->state = state; +#if USB_HAVE_UGEN + mtx_unlock(&usb_ref_lock); +#endif if (udev->bus->methods->device_state_change != NULL) (udev->bus->methods->device_state_change) (udev); } |