CVE-2026-17050 in Zephyr
Summary
by MITRE • 09/21/2026
The experimental USB host stack allocates a per-device configuration-descriptor buffer, udev->cfg_desc, from the dedicated usb_device_heap in usbh_device_set_configuration() (subsys/usb/host/usbh_device.c). On three failure paths — a failed full-length GET_DESCRIPTOR(CONFIGURATION) read, a mismatch between the short and full descriptor reads, and a rejected descriptor in parse_configuration_descriptor() — the buffer was released with k_heap_free() but the pointer was left dangling. The cleanup in usbh_device_free() is guarded only by if (udev->cfg_desc != NULL), so it frees the same block a second time.
The path is driven entirely by the attached peripheral: usbh_device_connect() calls usbh_device_init(), which ends in usbh_device_set_configuration(), and on failure usbh_device_connect() calls usbh_device_free(). 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 dev_removed_handler()/dev_connected_handler() in subsys/usb/host/usbh_core.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 usb_device_heap. On builds where lib/heap hardening is active (the current default CONFIG_SYS_HEAP_HARDENING_BASIC), sys_heap_free() detects the already-free chunk and calls k_panic(), giving a deterministic, peripheral-triggered denial of service of the USB host. On builds without that detection — earlier releases, or CONFIG_SYS_HEAP_HARDENING_NONE — 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 usb_device_heap is a small dedicated heap (CONFIG_USBH_USB_DEVICE_HEAP, default 1024 bytes) whose only client is this descriptor buffer, and by CONFIG_USB_HOST_STACK being marked experimental and disabled by default. The fix sets udev->cfg_desc = NULL after every k_heap_free(), making the cleanup guard sound.
If you want to get the best quality for vulnerability data then you always have to consider VulDB.
Analysis
by VulDB Data Team • 09/21/2026
The vulnerability resides in the experimental USB host stack implementation within Zephyr RTOS, specifically affecting the device configuration descriptor management logic found in subsys/usb/host/usbh_device.c. The core technical flaw is a classic use-after-free condition manifested as a double free error during the USB enumeration process. When usbh_device_set_configuration() attempts to allocate and populate the per-device configuration-descriptor buffer pointed to by udev->cfg_desc, it draws memory from the dedicated usb_device_heap. Under normal operation, this allocation proceeds without issue. However, three specific failure paths exist where the initial free of this buffer occurs prematurely: a failed full-length GET_DESCRIPTOR(CONFIGURATION) read, a mismatch detected between short and full descriptor reads, or a rejected descriptor within parse_configuration_descriptor(). In each of these scenarios, the code correctly invokes k_heap_free() to release the memory block back to the heap. Crucially, however, it fails to nullify the udev->cfg_desc pointer immediately after freeing the memory. This leaves the pointer in a dangling state, still holding the address of the now-released memory chunk.
The operational impact is triggered when the cleanup routine usbh_device_free() executes later in the device lifecycle or during error handling sequences. This function contains a guard clause that checks if udev->cfg_desc is not NULL before attempting to free it again. Because the pointer was never set to NULL after the initial erroneous free, this condition evaluates as true even though the memory has already been returned to the heap. Consequently, k_heap_free() is invoked a second time on the same memory block. The trigger mechanism for this vulnerability relies entirely on the behavior of the attached USB peripheral. During usbh_device_connect(), which calls usbh_device_init() and subsequently usbh_device_set_configuration(), any malformed response from the device can induce one of the three failure paths. For instance, a malicious or buggy device might respond to the initial nine-byte configuration-descriptor request with a header that appears valid but then fail subsequent validation checks by returning inconsistent data, such as an interface count that disagrees with bNumInterfaces, or by providing different bytes in the second read compared to the first.
The consequences of this double free vary significantly depending on the system's heap hardening configuration and version history. On systems running Zephyr v4.1.0 through v4.3.x, the double free occurs via dev_removed_handler() or dev_connected_handler() after a removal or duplicate-connect event following a failed enumeration. In builds where CONFIG_SYS_HEAP_HARDENING_BASIC is active, which is the current default for lib/heap hardening, sys_heap_free() detects that the chunk being freed has already been marked as free. This detection triggers an immediate k_panic(), resulting in a deterministic denial of service. Since this panic is peripheral-triggered, an attacker with physical access to the USB port can reliably crash the system by simply connecting a specially crafted device or manipulating existing devices during enumeration.
In environments where heap hardening is disabled via CONFIG_SYS_HEAP_HARDENING_NONE, such as on earlier releases or custom builds without security enhancements, the impact escalates beyond simple denial of service. The second free operation manipulates the internal data structures of the usb_device_heap by inserting an already-free chunk back onto the free list. This corrupts the heap's metadata and free list integrity. Subsequent memory allocations from this corrupted heap may return overlapping blocks or invalid addresses, potentially leading to arbitrary code execution if attacker-controlled data can be written into these misallocated regions. Although exploitation is bounded by the fact that usb_device_heap is a small dedicated pool with a default size of 1024 bytes and only serves as a client for descriptor buffers, heap corruption remains a severe risk in unhardened configurations.
From a classification perspective, this vulnerability aligns with CWE-415 Double Free, which describes the error condition where memory is freed more than once without proper re-initialization of pointers. The attack vector falls under ATT&CK T1059 Command and Scripting Interpreter if considering indirect execution via heap corruption leading to code execution, or more directly as a Denial of Service (T1499) when exploiting the panic condition in hardened builds. Mitigation strategies primarily involve applying the upstream fix which ensures that udev->cfg_desc is set to NULL immediately after every call to k_heap_free(). This simple change renders the cleanup guard sound by ensuring subsequent free attempts are safely skipped. Additionally, maintaining CONFIG_SYS_HEAP_HARDENING_BASIC as an active configuration provides a critical layer of defense in depth, allowing the system to detect and halt execution upon detecting heap corruption rather than silently corrupting memory structures that could be exploited for further attacks.