Description
When CONFIG_THREAD_STACK_MEM_MAPPED is enabled, each thread stack is mapped into the kernel virtual address space with a guard page on either side, and the mapping is released during abort cleanup. k_thread_abort_cleanup() in kernel/thread.c has two paths: a deferred one for self-abort, which stashes the mapping in thread_cleanup_stack_addr/thread_cleanup_stack_sz, and an immediate one for a thread aborted by some other thread. The immediate path called do_thread_cleanup(), which ignored its thread argument and only ever unmapped the stashed address — a value populated exclusively by the self-abort path. The mapped stack of a thread aborted by another thread was therefore never unmapped.

Every k_thread_abort() of a non-current thread leaks the thread's stack region plus its two guard pages from the kernel virtual address space. Threads that end by returning from their entry function take the working self-abort path, so the leak is specific to explicit cancellation — k_thread_abort(), pthread_cancel(), osThreadTerminate(), or the kernel thread kill shell command. Both k_thread_create() and k_thread_abort() are system calls, so with CONFIG_USERSPACE an unprivileged user-mode thread that has been granted a thread object and a user-capable stack object can drive the leak from user mode, consuming a kernel-side resource it cannot otherwise touch.

With CONFIG_KERNEL_VM_SIZE defaulting to 8 MB, a few hundred create/abort cycles exhaust the runtime mapping region. k_mem_map_phys_guard() then returns NULL, and map_thread_stack() guards that only with __ASSERT_NO_MSG(), which is compiled out of production builds; thread creation proceeds to write through a NULL-derived stack base and faults in kernel mode. The impact is a persistent denial of service — a kernel fatal error requiring a reboot — with no memory disclosure or corruption, and it is limited to configurations where memory-mapped thread stacks are enabled (x86 targets with X86_MMU and demand paging disabled, where the option is implied by default).
Published: 2026-11-09
Score: 5.5 Medium
EPSS: n/a
KEV: No
Impact: Denial of Service via kernel memory exhaustion
Action: Immediate Patch
AI Analysis

Impact

The vulnerability occurs when memory-mapped thread stacks are configured. Aborting a thread that was not the current thread fails to unmap its stack page and guard pages, leaving a persistent allocation in kernel virtual address space. Each aborted non-current thread leaks a full stack region, eventually exhausting the limited kernel VM area. The effect is a kernel fatal error that forces a system reboot; no data is disclosed or corrupted, but the system becomes unusable.

Affected Systems

Zephyr RTOS releases that enable CONFIG_THREAD_STACK_MEM_MAPPED in an x86 configuration with X86_MMU and demand paging disabled are affected. Particularly, builds with the default CONFIG_KERNEL_VM_SIZE of 8 MiB are vulnerable, as they allow a few hundred abort loops to deplete the mapping region. The flaw manifests only when explicit cancellation APIs such as k_thread_abort(), pthread_cancel(), osThreadTerminate(), or the kill shell command are used.

Risk and Exploitability

With a CVSS score of 5.5 the vulnerability is considered moderate. EPSS is currently unavailable, and the issue has not been listed as a KEV. The leak can be triggered by any thread that can issue k_thread_abort() on a non-current thread. In typical Zephyr setups, an unprivileged user-mode thread that has been granted a thread object can drive the exploit from user space, consuming kernel resources it cannot otherwise access. The attack requires only normal API usage and does not need privileged escalation, so the likelihood of exploitation is reasonably high in configurations that enable the vulnerable option.

Generated by OpenCVE AI on November 9, 2026 at 23:27 UTC.

Remediation

No solution or workaround provided in the CVE record.

OpenCVE Recommended Actions

  • Apply the recent patch that corrects thread stack cleanup during abort (as referenced in the GitHub commit or GHSA advisory).
  • Disable CONFIG_THREAD_STACK_MEM_MAPPED in the kernel configuration or rebuild the kernel without this option.
  • Avoid using k_thread_abort(), pthread_cancel(), osThreadTerminate(), or the kill shell command for thread cancellation; instead, let threads exit normally or use alternative termination patterns.

Generated by OpenCVE AI on November 9, 2026 at 23:27 UTC.

Tracking

Sign in to view the affected projects.

Advisories

No advisories yet.

History

Mon, 09 Nov 2026 23:45:00 +0000

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

Mon, 09 Nov 2026 22:15:00 +0000

Type Values Removed Values Added
Description When CONFIG_THREAD_STACK_MEM_MAPPED is enabled, each thread stack is mapped into the kernel virtual address space with a guard page on either side, and the mapping is released during abort cleanup. k_thread_abort_cleanup() in kernel/thread.c has two paths: a deferred one for self-abort, which stashes the mapping in thread_cleanup_stack_addr/thread_cleanup_stack_sz, and an immediate one for a thread aborted by some other thread. The immediate path called do_thread_cleanup(), which ignored its thread argument and only ever unmapped the stashed address — a value populated exclusively by the self-abort path. The mapped stack of a thread aborted by another thread was therefore never unmapped. Every k_thread_abort() of a non-current thread leaks the thread's stack region plus its two guard pages from the kernel virtual address space. Threads that end by returning from their entry function take the working self-abort path, so the leak is specific to explicit cancellation — k_thread_abort(), pthread_cancel(), osThreadTerminate(), or the kernel thread kill shell command. Both k_thread_create() and k_thread_abort() are system calls, so with CONFIG_USERSPACE an unprivileged user-mode thread that has been granted a thread object and a user-capable stack object can drive the leak from user mode, consuming a kernel-side resource it cannot otherwise touch. With CONFIG_KERNEL_VM_SIZE defaulting to 8 MB, a few hundred create/abort cycles exhaust the runtime mapping region. k_mem_map_phys_guard() then returns NULL, and map_thread_stack() guards that only with __ASSERT_NO_MSG(), which is compiled out of production builds; thread creation proceeds to write through a NULL-derived stack base and faults in kernel mode. The impact is a persistent denial of service — a kernel fatal error requiring a reboot — with no memory disclosure or corruption, and it is limited to configurations where memory-mapped thread stacks are enabled (x86 targets with X86_MMU and demand paging disabled, where the option is implied by default).
Title Memory-mapped thread stack is never unmapped when a thread is aborted by another thread, exhausting kernel virtual address space
Weaknesses CWE-401
References
Metrics cvssV3_1

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


Subscriptions

Zephyrproject Zephyr
cve-icon MITRE

Status: PUBLISHED

Assigner: zephyr

Published:

Updated: 2026-11-09T22:04:01.652Z

Reserved: 2026-09-12T18:04:43.338Z

Link: CVE-2026-90589

cve-icon Vulnrichment

No data.

cve-icon NVD

No data.

cve-icon Redhat

No data.

cve-icon OpenCVE Enrichment

Updated: 2026-11-09T23:30:18Z

Weaknesses
  • CWE-401

    Missing Release of Memory after Effective Lifetime