Skip to content

usb: host: ctx->root left dangling after root device disconnect #111020

Closed as not planned

Description

@d3zd3z

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) locks freed->mutex and walks its class list —
    reading and writing memory that has already been returned to the slab.
  • usbh_device_free(freed) reads freed->ctx / freed->addr, does
    sys_dlist_remove(&freed->node), and calls k_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

  1. github-actions commented on Jul 12, 2026

    @github-actions

    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.

  2. tmon-nordic commented on Jul 17, 2026

    @tmon-nordic
    Contributor

    Is there anything left to be done here? Is there any particular reason why #108796 did not close this?

  3. github-actions commented on Aug 18, 2026

    @github-actions

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BackportBackport PR and backport failure issuesStalearea: USBUniversal Serial Buspriority: mediumMedium impact/importance bug

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions