CVE-2026-17050: Double free of the USB host configuration descriptor when device enumeration fails
The experimental USB host stack allocates a per-device configuration-descriptor buffer, udev->cfgdesc, from the dedicated usbdeviceheap in usbhdevicesetconfiguration() (subsys/usb/host/usbhdevice.c). On three failure paths — a failed full-length GETDESCRIPTOR(CONFIGURATION) read, a mismatch between the short and full descriptor reads, and a rejected descriptor in parseconfigurationdescriptor() — the buffer was released with kheapfree() but the pointer was left dangling. The cleanup in usbhdevicefree() is guarded only by if (udev->cfgdesc != NULL), so it frees the same block a second time.
The path is driven entirely by the attached peripheral: usbhdeviceconnect() calls usbhdeviceinit(), which ends in usbhdevicesetconfiguration(), and on failure usbhdeviceconnect() calls usbhdevicefree(). On v4.4.x this happens during the same enumeration, with no unplug required; on v4.1.0–v4.3.x the second free instead arrives via devremovedhandler()/devconnectedhandler() in subsys/usb/host/usbhcore.c, so it requires a removal or duplicate-connect event after the failed enumeration — a sequence the attached device fully controls. A malicious or malformed USB device only has to answer the first 9-byte configuration-descriptor request with a well-formed header and then fail any of the three checks, for example by returning a full descriptor whose interface count disagrees with bNumInterfaces, or by answering the second read with different bytes.
The result is a double free on usbdeviceheap. On builds where lib/heap hardening is active (the current default CONFIGSYSHEAPHARDENINGBASIC), sysheapfree() detects the already-free chunk and calls kpanic(), giving a deterministic, peripheral-triggered denial of service of the USB host. On builds without that detection — earlier releases, or CONFIGSYSHEAPHARDENINGNONE — the second free manipulates a chunk already on the free list, corrupting the heap's free list so that later allocations can return overlapping or invalid blocks.
Exploitation beyond denial of service is bounded by the fact that usbdeviceheap is a small dedicated heap (CONFIGUSBHUSBDEVICEHEAP, default 1024 bytes) whose only client is this descriptor buffer, and by CONFIGUSBHOSTSTACK being marked experimental and disabled by default. The fix sets udev->cfgdesc = NULL after every kheapfree(), making the cleanup guard sound.
Affected Software
Event History
Frequently Asked Questions
What access does an attacker need to trigger the issue?
An attacker needs control of an attached USB peripheral, or needs to cause a malformed USB device to be connected. No privileges or user interaction are required because the affected descriptor reads occur during device enumeration.
Which enumeration failures can lead to the double free?
The issue is reached when a full-length GET_DESCRIPTOR(CONFIGURATION) read fails, when the short and full descriptor reads do not match, or when parse_configuration_descriptor() rejects the descriptor. In each case, the configuration-descriptor buffer is freed while udev->cfg_desc remains non-NULL.
Does exploitation require unplugging the device after enumeration fails?
On v4.4.x, no: the second free occurs during the same enumeration attempt. On v4.1.0 through v4.3.x, the second free requires a removal or duplicate-connect event after the failed enumeration; the attached device can control that sequence.
What can be done before a fix is applied?
Limit connection of untrusted or malformed USB peripherals to systems using the experimental USB host stack. The vulnerable path is driven entirely by the connected device during enumeration.
How can an affected code path be recognized?
Review usbh_device_set_configuration() for failure paths that call k_heap_free() on udev->cfg_desc without clearing the pointer, followed by usbh_device_free() freeing it again when udev->cfg_desc is non-NULL. Relevant paths involve failed or mismatched configuration-descriptor reads and descriptor parsing rejection.