CVE-2026-12631 Overview
CVE-2026-12631 affects the Zephyr real-time operating system (RTOS) kernel. The flaw resides in thread_obj_validate() within kernel/thread.c, which validates access for the k_thread_join() and k_thread_abort() system calls. The access-denied branch incorrectly invoked K_OOPS(K_SYSCALL_VERIFY_MSG(ret, "access denied")), where a non-zero error code was treated as verification success. This bypassed the intended kernel oops and allowed control to reach CODE_UNREACHABLE. Unprivileged user-mode threads running under CONFIG_USERSPACE can trigger the path against thread objects they do not own, leading to a kernel denial of service on Clang builds or an authorization bypass on GCC builds [CWE-862].
Critical Impact
A local unprivileged user thread can deterministically crash the Zephyr kernel or bypass access control on thread system calls, escaping the userspace sandbox.
Affected Products
- Zephyr RTOS kernel (kernel/thread.c) with CONFIG_USERSPACE enabled
- Builds compiled with Clang (deterministic denial of service)
- Builds compiled with GCC (potential access-control bypass via undefined behavior)
Discovery Timeline
- 2026-08-18 - CVE-2026-12631 published to the National Vulnerability Database (NVD)
- 2026-08-19 - Last updated in NVD database
Technical Details for CVE-2026-12631
Vulnerability Analysis
The Zephyr kernel declares k_thread_join() and k_thread_abort() as __syscall entries in include/zephyr/kernel.h. Both syscalls funnel through thread_obj_validate(), which calls k_object_validate() to confirm the caller owns the target thread object. When validation fails, k_object_validate() returns -EPERM (no grant) or -EBADF (wrong object type).
The default switch branch handles these denials. It invoked K_OOPS(K_SYSCALL_VERIFY_MSG(ret, "access denied")). The K_SYSCALL_VERIFY_MSG macro treats a truthy expression as verification success. Passing the non-zero error code ret made the macro report success, suppressing the oops and letting control fall through to CODE_UNREACHABLE.
Root Cause
The root cause is inverted verification semantics. K_SYSCALL_VERIFY_MSG requires an expression that is true when the state is valid. Supplying a non-zero error code as the verification predicate inverted the check, so the denial path never oopsed. This is a missing authorization defect [CWE-862] introduced by a subtle macro contract violation.
Attack Vector
An unprivileged user thread invokes k_thread_join() or k_thread_abort() with a thread object it was never granted access to. On Clang builds, CODE_UNREACHABLE compiles to an illegal-instruction trap; the user thread deterministically crashes the kernel in supervisor mode. On GCC builds, CODE_UNREACHABLE is undefined behavior and the compiler may discard the return-value handling for thread_obj_validate(). If the function returns an undefined false, the caller proceeds into the real k_thread_join() or k_thread_abort() implementation against a thread the caller was never authorized to touch.
#ifdef CONFIG_LOG
k_object_dump_error(ret, thread, ko, K_OBJ_THREAD);
#endif /* CONFIG_LOG */
- K_OOPS(K_SYSCALL_VERIFY_MSG(ret, "access denied"));
+ /* ret is a non-zero error code here (the 0 and -EINVAL cases
+ * are handled above), so this branch must always oops. Passing
+ * ret as the "verify" expression would treat the failure code as
+ * success and fall through to CODE_UNREACHABLE; verify ret == 0
+ * so the oops is actually raised.
+ */
+ K_OOPS(K_SYSCALL_VERIFY_MSG(ret == 0, "access denied"));
}
CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
}
Source: Zephyr commit bd18286. The patch changes the verify predicate to ret == 0, so any non-zero denial correctly raises K_OOPS and terminates the offending caller.
Detection Methods for CVE-2026-12631
Indicators of Compromise
- Unexpected kernel oops, illegal-instruction traps, or resets originating from thread_obj_validate() or the k_thread_join/k_thread_abort syscall handlers.
- User threads issuing k_thread_join() or k_thread_abort() against thread objects not present in their k_object grant list.
- Repeated device reboots on Clang-compiled Zephyr images shortly after loading untrusted user applications.
Detection Strategies
- Enable CONFIG_LOG so k_object_dump_error() records -EPERM and -EBADF returns from k_object_validate() on the denial path.
- Instrument syscall entry points to log the calling thread ID and target thread handle for k_thread_join and k_thread_abort.
- Review device crash telemetry for supervisor-mode illegal-instruction faults tied to the syscall handler address range.
Monitoring Recommendations
- Aggregate Zephyr device logs and crash reports centrally to identify recurring oopses tied to the affected syscalls.
- Track deployed Zephyr firmware versions and compiler toolchains across your fleet to identify unpatched Clang and GCC builds.
- Alert on user applications issuing thread syscalls with handles outside their permitted object set.
How to Mitigate CVE-2026-12631
Immediate Actions Required
- Apply Zephyr commit bd1828652dfc217ba9f3a2221a7499cd8914ed9c to kernel/thread.c and rebuild affected firmware images.
- Prioritize patching devices built with Clang, which are deterministically crashable by any local unprivileged thread.
- Audit user applications for calls to k_thread_join() and k_thread_abort() against non-owned thread objects.
Patch Information
The fix is published in the Zephyr project as GHSA-crfw-75jw-hjm3 and delivered via commit bd18286. The change replaces K_SYSCALL_VERIFY_MSG(ret, ...) with K_SYSCALL_VERIFY_MSG(ret == 0, ...) so that any non-zero error code correctly triggers K_OOPS and terminates the offending user thread before reaching CODE_UNREACHABLE.
Workarounds
- Disable CONFIG_USERSPACE on affected builds if untrusted user-mode threads are not required, removing the syscall entry point for unprivileged callers.
- Restrict which applications can be loaded onto the device to trusted, signed images until the patched kernel is deployed.
- On GCC builds, review compiler flags to minimize aggressive optimization of undefined-behavior paths pending firmware update.
# Apply the upstream fix to a local Zephyr checkout
cd zephyr
git fetch origin
git cherry-pick bd1828652dfc217ba9f3a2221a7499cd8914ed9c
west build -b <your_board> <your_app> --pristine
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

