Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-12631

CVE-2026-12631: Zephyr Kernel DoS Vulnerability

CVE-2026-12631 is a denial of service flaw in Zephyr kernel that allows unprivileged user threads to crash the system. This post covers the technical details, affected versions, impact, and mitigation steps.

Published:

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.

c
 #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.
bash
# 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.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.