CVE-2026-19936 in Zephyr
Summary
by MITRE • 10/13/2026
The Bluetooth Mesh Access-layer delayable message queue in subsys/bluetooth/mesh/delayable_msg.c shares three intrusive lists (busy_ctx, free_ctx, free_chunks) and one k_work_delayable between two cooperative threads with no synchronization: bt_mesh_delayable_msg_manage() runs on the Bluetooth host RX thread, while delayable_msg_handler() runs on the system workqueue. Both block while the lists are in an intermediate state — the enqueuer in bt_rand(), both in bt_mesh_access_send() — which lets the other context observe torn state. delayable_msg_handler() used the result of sys_slist_get() on busy_ctx without a NULL check, and allocate_delayable_msg_ctx() tested sys_slist_is_empty() and then assumed a separate sys_slist_get() would succeed.
Because CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG and CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG_CTX_ENABLED default to y, every model response to a received mesh message is parked on this queue, so the enqueuer runs as a direct consequence of inbound traffic. A peer that can get access-layer messages accepted by a model (it must hold the NetKey and a bound AppKey, so a legitimate or compromised network member) controls the arrival rate, the size of the elicited responses and the destination address, and thereby controls occupancy of the 4-entry context pool and the 40-entry chunk pool. Exhausting the chunk pool forces bt_mesh_delayable_msg_manage() down the purge path, which sends and releases the only queued message and leaves busy_ctx empty while the delayable work item is still armed; if the enqueuer then yields, the handler runs against the empty list.
The result is a read-modify-write through a NULL-based pointer (pending_msg->fired_time += 10) at a fixed low address on the system workqueue thread. On typical targets that faults, aborting the system workqueue thread — which stops all workqueue-driven functionality, including the mesh timing machinery — or resetting the node, so the impact is loss of availability of the affected node. The write value and address are fixed and no attacker-controlled length or index is involved, so this is not exploitable for memory corruption or code execution. The same unsynchronized window also allowed one message context to be selected and released by two contexts at once, producing duplicated transmissions, duplicated send-completion callbacks and crossed chunk ownership.
The fix serializes every list mutation with a k_spinlock that is never held across a blocking call, makes push_msg_from_delayable_msgs() detach the queue head atomically so a message being sent is on no list, removes the unchecked sys_slist_get() from delayable_msg_handler(), and drops any message that reaches the front of the queue while the stack is suspended or reset.
If you want to get best quality of vulnerability data, you may have to visit VulDB.
Analysis
by VulDB Data Team • 10/13/2026
The vulnerability identified in the Bluetooth Mesh Access-layer involves a critical race condition within the delayable message queue implementation located in subsys/bluetooth/mesh/delayable_msg.c. This flaw stems from the lack of synchronization mechanisms when sharing intrusive lists and work items between two cooperative threads operating at different execution contexts. Specifically, the bt_mesh_delayable_msg_manage function executes on the Bluetooth host receive thread, while the delayable_msg_handler runs on a system workqueue. These components interact with three shared data structures: busy_ctx, free_ctx, and free_chunks, alongside one k_work_delayable object. The absence of mutual exclusion allows these threads to observe intermediate or torn states during list modifications, leading to undefined behavior when accessing pointers that may no longer be valid.
The operational impact is severe for the availability of Bluetooth Mesh nodes. Because the configuration options CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG and CONFIG_BT_MESH_ACCESS_DELAYABLE_MSG_CTX_ENABLED are enabled by default, every model response to a received mesh message is queued in this vulnerable structure. An attacker who possesses a valid Network Key and Application Key can interact with legitimate network members to trigger these responses. By controlling the arrival rate of messages, the size of elicited responses, and destination addresses, an adversary can exhaust the limited resource pools consisting of four context entries and forty chunks. When the chunk pool is exhausted, the management function enters a purge path that sends and releases the only queued message while leaving the busy_ctx list empty. However, if the enqueuing thread yields at this precise moment, the handler on the workqueue proceeds to process an empty list, leading directly to memory corruption.
The core technical flaw manifests as a NULL pointer dereference resulting in a read-modify-write operation against a fixed low address. The code attempts to execute pending_msg->fired_time += 10 without verifying that pending_msg is not null. On most embedded targets, this access triggers a fault or aborts the system workqueue thread entirely. Since the workqueue drives critical timing machinery for Bluetooth Mesh operations, its failure results in a complete loss of availability for the affected node, effectively causing a denial of service. The attacker must reset the node to restore functionality. While the fixed nature of the write address and value prevents exploitation for arbitrary code execution or general memory corruption, the resulting system instability is sufficient to disrupt network integrity.
Beyond the crash scenario, the unsynchronized access window also permits logical errors such as double-free conditions. It becomes possible for two different contexts to select and release the same message context simultaneously. This leads to duplicated transmissions, duplicate send-completion callbacks, and crossed chunk ownership states. These anomalies can cause unpredictable behavior in mesh networking protocols, potentially leading to further instability or resource leaks that degrade network performance over time. The vulnerability is classified under CWE-416: Use After Free due to the potential for accessing freed memory structures through race conditions on shared lists without proper locking mechanisms.
The remediation strategy focuses on enforcing strict serialization of all list mutations using a k_spinlock. Crucially, this lock must never be held across blocking calls to prevent priority inversion or deadlocks within the cooperative threading model. The fix also modifies push_msg_from_delayable_msgs() to atomically detach the queue head, ensuring that a message currently being sent is not present on any shared list during its lifecycle. Additionally, the unchecked sys_slist_get call in delayable_msg_handler has been removed and replaced with safe checks for null pointers. Any messages reaching the front of the queue while the stack is suspended or resetting are now dropped to prevent processing invalid states. These changes align with ATT&CK technique T1499: Endpoint Denial of Service, as they directly address the vector used by attackers to disrupt service availability through resource exhaustion and state corruption.