Sceawere
Vulnerability Detail
CVE-2026-12366UPDATED Verified Sceawere Triage Sources: NVD / CISA KEV
Zephyr Kernel Timer Use-After-Free
Vulnerability Metadata
- Severity
- High
- Score / CVSS
- 8.8
- Creation Date
- 3h ago
- Vendor
- zephyrproject
- Product
- zephyr
- Attack Type
- use-after-free
- Vector String
- CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
- Attack Complexity
- LOW
Narrative and Response
Description
Zephyr's dynamic kernel-object disposal path unref_check() in kernel/userspace/userspace.c frees an object's storage (k_free(dyn->data)) once its reference count reaches zero, after running a per-object-type cleanup. The cleanup switch handled only K_OBJ_MSGQ and K_OBJ_STACK; there was no K_OBJ_TIMER case. A dynamically-allocated, initialized, and armed k_timer keeps its embedded struct _timeout dnode linked in the global timeout queue (_timeout_q), so freeing the timer storage without cancelling the timeout leaves a dangling node in that queue. When the timer next expires, the timeout machinery walks _timeout_q and invokes z_timer_expiration_handler() on the freed node, dereferencing and writing freed (and reusable) kernel heap in kernel/ISR context. This is a deterministic use-after-free that does not depend on SMP: the queued node is simply never unlinked at free time. The disposal is reachable from an unprivileged user thread under CONFIG_USERSPACE + CONFIG_DYNAMIC_OBJECTS: a thread that holds the last permission on such a timer drops it via the k_object_release() syscall (or by exiting, through k_thread_perms_all_clear()), and can arm the timer itself via the k_timer_start() syscall. The free and the expiration handler run at kernel privilege while the actor is a user thread, so the bug is a sandbox-escape memory-corruption primitive usable for privilege escalation. The fix adds k_timer_cleanup() (cancel the timeout and wait for any in-flight handler) and calls it for K_OBJ_TIMER before freeing.
Executive Summary
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Technical Details
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Mitigations
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
References
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Additional Metadata
{
"score": "8.8",
"pubDate": "2026-08-14T18:17:22.043Z",
"pubdate": "2026-08-14T18:17:22.043Z",
"executiveSummary": "A use-after-free vulnerability exists in the dynamic kernel-object disposal path of Zephyr within kernel/userspace/userspace.c. The vulnerability arises from an incomplete cleanup switch in unref_check() when handling dynamic objects whose reference count reaches zero. Specifically, the cleanup mechanism lacked a case for K_OBJ_TIMER, causing dynamically allocated, armed timers to be freed via k_free(dyn->data) without canceling their embedded struct _timeout dnode. This leaves a dangling pointer in the global timeout queue (_timeout_q). When the timer subsequently expires, the timeout machinery dereferences and writes to the freed kernel heap in kernel or ISR context. This vulnerability can be exploited by an unprivileged user thread under CONFIG_USERSPACE and CONFIG_DYNAMIC_OBJECTS. By holding the last permission on a dynamic timer and releasing it via the k_object_release() system call or thread exit, an attacker can trigger the premature freeing of an armed timer. Subsequent expiration leads to deterministic kernel memory corruption. Because the memory disposal and expiration handler execute at kernel privilege while the triggering actor is unprivileged, this flaw provides a sandbox-escape memory-corruption primitive that enables local privilege escalation.",
"technicalDetails": "The vulnerability resides in Zephyr's object reference-counting and disposal mechanism implemented in unref_check() located at kernel/userspace/userspace.c. When a kernel object's reference count drops to zero, unref_check() performs a per-object-type cleanup before releasing the underlying storage using k_free(dyn->data). Historically, the cleanup switch statement handled only K_OBJ_MSGQ and K_OBJ_STACK object types, omitting K_OBJ_TIMER.\nA dynamically allocated, initialized, and armed k_timer maintains an embedded struct _timeout dnode that remains linked within the global timeout queue (_timeout_q). Because the K_OBJ_TIMER cleanup case was missing from unref_check(), freeing the timer storage via k_free() does not remove or cancel the corresponding timeout node from _timeout_q. This leaves a dangling reference to the freed kernel heap block inside the active timeout queue.\nWhen the timer eventually reaches its expiration time, the system timeout machinery walks _timeout_q and invokes z_timer_expiration_handler() on the freed node. This operation dereferences and writes to the memory block that has already been returned to the kernel heap and potentially reallocated for other uses. This results in a deterministic use-after-free condition that does not rely on SMP mechanics or race conditions; the queued node is simply bypassed during the release phase.\nThe attack flow is reachable from an unprivileged user thread when CONFIG_USERSPACE and CONFIG_DYNAMIC_OBJECTS are enabled. An adversary operating within a restricted user thread context can create or obtain permissions on a dynamic timer, arm it using the k_timer_start() system call, and subsequently relinquish all references by invoking the k_object_release() system call or by terminating the thread to trigger k_thread_perms_all_clear(). Upon reference exhaustion, unref_check() frees the timer without purging it from the timeout queue.\nSubsequent expiration of the orphaned timer causes the kernel or ISR context to write into the freed heap allocation. As the disposal and expiration execution flows execute with elevated kernel privileges while initiated by an unprivileged user thread, this memory corruption primitive facilitates arbitrary kernel state manipulation, sandbox escape, and local privilege escalation.\nThe official fix introduces the k_timer_cleanup() function, which properly cancels the active timeout and waits for any in-flight expiration handlers to complete, and ensures this routine is invoked for K_OBJ_TIMER objects prior to executing k_free()."
}