Description
The kernel helper z_waitq_head() (kernel/include/wait_q.h) reads the head of a thread wait queue, but on SMP systems it was called from several places without holding the scheduler spinlock _sched_spinlock that serializes wait-queue mutation. The consequential call site is z_timer_expiration_handler() in kernel/timer.c, which held only the timer subsystem lock timer_lock while reading timer->wait_q and then unpending, setting the return value of, and readying the thread it obtained. halt_thread() in kernel/sched.c removes an aborted thread from its wait queue while holding only _sched_spinlock, so the two paths are not mutually exclusive.

When the last waiter is removed on another CPU inside that window, sys_dlist_peek_head() can return the list sentinel — a pointer into the k_timer object itself — typed as a struct k_thread * (with CONFIG_WAITQ_SCALABLE, rb_get_min() can return an arbitrary node of a tree being restructured), or it can return a pointer to a thread that has already been unpended and, for dynamically allocated threads, freed. The handler then calls unpend_thread_no_timeout(), which performs a sys_dlist_remove() through pointers read from that object, and arch_thread_return_value_set(), which writes unconditionally through the pointer.

The result is out-of-bounds and use-after-free writes in supervisor context, plus scheduler state corruption; a milder outcome is a thread that has re-pended elsewhere being woken with a success return value from an unrelated blocking call. All of the primitives needed to drive the interleaving — k_timer_start(), k_timer_status_sync(), k_thread_create() and k_thread_abort() — are system calls, so with CONFIG_USERSPACE enabled an unprivileged user-mode thread can attempt the race against kernel-owned data.

Exploitation requires a multi-CPU build (CONFIG_MP_MAX_NUM_CPUS greater than one; the affected region is a no-op on uniprocessor targets), local code execution, and winning a narrow timing window, so the practical outcome is most often a kernel crash rather than controlled corruption. The fix splits the helper into z_waitq_head_locked() for callers that already hold the scheduler spinlock and a z_waitq_head() that acquires it, and brings the timer expiration handler's head read, unpend and ready under a single LOCK_SCHED_SPINLOCK region.
Published: 2026-10-21
Score: 7 High
EPSS: < 1% Very Low
KEV: No
Impact: Kernel out‑of‑bounds and use‑after‑free leading to potential system crash or state corruption
Action: Update Kernel
AI Analysis

Impact

The Zephyr RTOS kernel contains a race condition in the wait‑queue helper z_waitq_head() when used from the timer expiration handler on SMP systems. Because the helper was executed without holding the scheduler spinlock, an unprivileged user thread can orchestrate a narrow timing window where the last waiter is removed on another CPU, causing sys_dlist_peek_head() to return either a sentinel pointer or a pointer to a freed thread. The handler then performs unpend_thread_no_timeout(), sys_dlist_remove(), and arch_thread_return_value_set() using that corrupted pointer, resulting in out‑of‑bounds or use‑after‑free writes in supervisor context and corrupting scheduler state. The primary impact is a kernel crash or unpredictable behaviour; a milder outcome is improper thread wake‑up signals. This flaw is identified as a race condition on kernel data structures (CWE‑362).

Affected Systems

All Zephyr RTOS releases that enable SMP (CONFIG_MP_MAX_NUM_CPUS > 1) and use the affected wait‑queue logic before the patch referenced in commit a9b21f. No explicit version range is provided, but the flaw exists in builds that have not incorporated the recent commit fixing the locking. Boards and applications compiled from Zephyr sources prior to this commit are potentially affected if they run on multi‑processor configurations.

Risk and Exploitability

The CVSS score is 7.0, indicating a high severity. The EPSS score is not available, and the vulnerability is not listed in CISA’s KEV catalog. Exploitation requires a multi‑CPU build, local code execution from an unprivileged thread, and precise timing to induce the race; thus the practical result is likely a kernel crash rather than deliberate corruption. The attack vector is local, with attacker control limited to user‑mode code running on the same device. Overall, the risk is moderate to high for systems with SMP enabled, particularly those exposed to untrusted user processes.

Generated by OpenCVE AI on October 22, 2026 at 04:51 UTC.

Remediation

No solution or workaround provided in the CVE record.

OpenCVE Recommended Actions

  • Apply the latest Zephyr RTOS update that includes commit a9b21fb354e8468dd15db307c3e599dc68b7ef33 or later, which adds proper locking around z_waitq_head() calls
  • Ensure that builds are configured for SMP only when required; for single‑core use, configure CONFIG_MP_MAX_NUM_CPUS=1 to make the affected code path a no‑op
  • Verify that the configuration option CONFIG_WAITQ_SCALABLE is set appropriately or adjust it if it introduces additional tree restructuring logic that could interact with the fixed code

Generated by OpenCVE AI on October 22, 2026 at 04:51 UTC.

Tracking

Sign in to view the affected projects.

Advisories

No advisories yet.

History

Fri, 23 Oct 2026 22:30:00 +0000

Type Values Removed Values Added
Metrics ssvc

{'options': {'Automatable': 'no', 'Exploitation': 'none', 'Technical Impact': 'total'}, 'version': '2.0.3'}


Thu, 22 Oct 2026 10:45:00 +0000

Type Values Removed Values Added
First Time appeared Zephyrproject
Zephyrproject zephyr
Vendors & Products Zephyrproject
Zephyrproject zephyr

Wed, 21 Oct 2026 18:30:00 +0000

Type Values Removed Values Added
Description The kernel helper z_waitq_head() (kernel/include/wait_q.h) reads the head of a thread wait queue, but on SMP systems it was called from several places without holding the scheduler spinlock _sched_spinlock that serializes wait-queue mutation. The consequential call site is z_timer_expiration_handler() in kernel/timer.c, which held only the timer subsystem lock timer_lock while reading timer->wait_q and then unpending, setting the return value of, and readying the thread it obtained. halt_thread() in kernel/sched.c removes an aborted thread from its wait queue while holding only _sched_spinlock, so the two paths are not mutually exclusive. When the last waiter is removed on another CPU inside that window, sys_dlist_peek_head() can return the list sentinel — a pointer into the k_timer object itself — typed as a struct k_thread * (with CONFIG_WAITQ_SCALABLE, rb_get_min() can return an arbitrary node of a tree being restructured), or it can return a pointer to a thread that has already been unpended and, for dynamically allocated threads, freed. The handler then calls unpend_thread_no_timeout(), which performs a sys_dlist_remove() through pointers read from that object, and arch_thread_return_value_set(), which writes unconditionally through the pointer. The result is out-of-bounds and use-after-free writes in supervisor context, plus scheduler state corruption; a milder outcome is a thread that has re-pended elsewhere being woken with a success return value from an unrelated blocking call. All of the primitives needed to drive the interleaving — k_timer_start(), k_timer_status_sync(), k_thread_create() and k_thread_abort() — are system calls, so with CONFIG_USERSPACE enabled an unprivileged user-mode thread can attempt the race against kernel-owned data. Exploitation requires a multi-CPU build (CONFIG_MP_MAX_NUM_CPUS greater than one; the affected region is a no-op on uniprocessor targets), local code execution, and winning a narrow timing window, so the practical outcome is most often a kernel crash rather than controlled corruption. The fix splits the helper into z_waitq_head_locked() for callers that already hold the scheduler spinlock and a z_waitq_head() that acquires it, and brings the timer expiration handler's head read, unpend and ready under a single LOCK_SCHED_SPINLOCK region.
Title Race condition on kernel wait queues: unlocked z_waitq_head() read in the timer expiration handler can yield a stale or sentinel thread pointer on SMP
Weaknesses CWE-362
References
Metrics cvssV3_1

{'score': 7, 'vector': 'CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H'}


Subscriptions

Zephyrproject Zephyr
cve-icon MITRE

Status: PUBLISHED

Assigner: zephyr

Published:

Updated: 2026-10-23T21:28:10.701Z

Reserved: 2026-08-22T20:23:01.098Z

Link: CVE-2026-78118

cve-icon Vulnrichment

Updated: 2026-10-23T20:45:40.891Z

cve-icon NVD

Status : Awaiting Analysis

Published: 2026-10-21T19:17:03.787

Modified: 2026-10-23T22:17:00.450

Link: CVE-2026-78118

cve-icon Redhat

No data.

cve-icon OpenCVE Enrichment

Updated: 2026-10-22T10:30:04Z

Weaknesses
  • CWE-362

    Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')