Repository navigation
usb: host: ctx->root left dangling after root device disconnect #111020
Description
Describe the bug
In the USB host stack, usbh_device_disconnect()
(subsys/usb/host/usbh_device.c) frees the disconnected device but never clears
the context's record of the root device:
void usbh_device_disconnect(struct usbh_context *ctx, struct usb_device *udev)
{
usbh_class_remove_all(udev);
usbh_device_free(udev); /* slab object returned to the pool */
LOG_DBG("Device removed");
}usbh_device_free() returns the device object to its fixed k_mem_slab pool, but
ctx->root still points at that freed slab entry. After the root device is
disconnected, ctx->root is a dangling pointer.
Removal events are handled in dev_removed_handler()
(subsys/usb/host/usbh_core.c), which decides what to tear down purely from
ctx->root:
udev = usbh_device_get_root(ctx); /* returns ctx->root */
if (udev != NULL) {
usbh_device_disconnect(ctx, udev);
} else {
LOG_DBG("Spurious device removed event");
}The handler only checks ctx->root != NULL; it cannot tell that the object was
already freed. UHC drivers synthesize UHC_EVT_DEV_REMOVED directly from the
electrical bus state (see uhc_max3421e.c emitting it on SE0, and the MCUX
detach callback) with no debounce, so attach/detach jitter, a bus bounce, or an
unplug/replug glitch readily produces a second removal event for an
already-disconnected root device.
On that second event the stale ctx->root passes the NULL check and
usbh_device_disconnect() runs again on the freed object:
usbh_class_remove_all(freed)locksfreed->mutexand walks its class list —
reading and writing memory that has already been returned to the slab.usbh_device_free(freed)readsfreed->ctx/freed->addr, does
sys_dlist_remove(&freed->node), and callsk_mem_slab_free()on the same
block a second time — corrupting the device list and double-freeing the slab
entry.
If the slab block was re-handed-out to a newly attached device in between the two
removals, these writes land on a live device object instead, corrupting it.
Expected behavior
When the root device is disconnected, ctx->root should be cleared before the
device object is freed, so a subsequent removal event takes the "spurious"
branch instead of dereferencing and re-freeing a stale pointer. Root-device
bookkeeping (both setting and clearing ctx->root) belongs alongside the
connect/disconnect of the device object rather than split out into the event
handlers.
Affected code
subsys/usb/host/usbh_device.c — usbh_device_disconnect() (and the matching
ctx->root assignment in usbh_device_connect());
subsys/usb/host/usbh_core.c — dev_removed_handler().
Activity
github-actions commentedon Jul 12, 2026
This issue has been marked as stale because it has been open (more than) 30 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this issue will automatically be closed in 7 days. Note, that you can always re-open a closed issue at any time.
tmon-nordic commentedon Jul 17, 2026
Is there anything left to be done here? Is there any particular reason why #108796 did not close this?
github-actions commentedon Aug 18, 2026
This issue has been marked as stale because it has been open (more than) 30 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this issue will automatically be closed in 7 days. Note, that you can always re-open a closed issue at any time.