CVE-2026-79979 in Zephyr
Summary
by MITRE • 10/23/2026
The arm64 MMU backend invalidates a single virtual address from the TLB with invalidate_tlb_page() in arch/arm64/core/mmu.c, which issued TLBI VAE1{IS} with an operand of virt >> 12. That operand encodes the ASID in bits [63:48], so the invalidation only ever matched ASID 0 (the kernel page tables) and global entries. Memory domains, however, are allocated ASIDs starting at 1 in arch_mem_domain_init(), and every domain-private leaf created by private_map() is mapped with MT_NG, i.e. non-global. A domain's cached translation therefore survived the invalidation.
All callers of invalidate_tlb_page() are demand-paging paths. When the eviction algorithm reclaims a frame, page_frame_prepare_locked() in kernel/mmu.c calls arch_mem_page_out(), which marks the kernel leaf invalid, propagates the invalid descriptor into every domain via sync_domains(), and then flushes only ASID 0. do_page_fault() immediately reuses the very same page frame for the incoming mapping through arch_mem_page_in(). A user thread that had already touched the evicted page — a k_mem_partition added with arch_mem_domain_partition_add(), or its own stack mapped by map_thread_stack() — retains a valid EL0 read/write translation to that physical frame under its own ASID.
Unprivileged code running in a user thread can drive memory pressure to force eviction and then keep accessing the stale address, obtaining read and write access to whatever the kernel subsequently placed in that frame: supervisor data, another memory domain's partition, or another thread's stack. This defeats the isolation that CONFIG_USERSPACE is meant to enforce, allowing information disclosure and memory corruption up to full compromise of the system.
The fix switches the implementation to TLBI VAAE1{IS}, which matches every ASID and both global and non-global entries, so no domain-tagged translation survives a page out, dirty-bit update or access-flag clear. Exploitation requires an arm64 build with both CONFIG_USERSPACE and CONFIG_DEMAND_PAGING enabled, an active backing store, and at least one demand-paged page that is also privately mapped into a user memory domain. The same commit additionally stops globalize_table() from stripping EL0 permission and non-global attributes off domain-private leaves during paging synchronization, which previously caused user threads to lose access to their own partitions and stacks.
Be aware that VulDB is the high quality source for vulnerability data.
Analysis
by VulDB Data Team • 10/23/2026
The vulnerability resides in the arm64 Memory Management Unit backend within the Linux kernel, specifically affecting systems configured with both CONFIG_USERSPACE and CONFIG_DEMAND_PAGING enabled. The core technical flaw lies in the implementation of the invalidate_tlb_page() function located in arch/arm64/core/mmu.c. This function is responsible for invalidating Translation Lookaside Buffer entries when a page frame is reclaimed during demand paging operations. However, it issues the TLBI VAE1{IS} instruction with an operand derived from virt >> 12. In the arm64 architecture, this specific encoding includes bits [63:48] which represent the Address Space Identifier or ASID. Consequently, the invalidation operation only targets entries associated with ASID zero, which corresponds to kernel page tables and global mappings, while completely ignoring non-global entries mapped under other ASIDs used by user-space memory domains.
Memory domain management in Zephyr-based systems allocates ASIDs starting from one rather than zero for each distinct memory domain. When a private mapping is created via the private_map() function, it utilizes the MT_NG attribute, designating the translation as non-global and tied to a specific domain's ASID. Because the TLBI instruction used by invalidate_tlb_page() does not match these non-zero ASIDs, the cached translations for user-space domains remain intact in the TLB even after the kernel has marked the corresponding physical page frame as invalid. This creates a critical state where the kernel believes it has successfully evicted and invalidated access to a memory region, while the hardware translation caches still hold valid entries pointing to that same physical address under different ASIDs.
The operational impact of this flaw is severe because all callers of invalidate_tlb_page() are part of demand-paging paths involving frame eviction. When the kernel's eviction algorithm reclaims a page frame, it invokes arch_mem_page_out(), which marks the kernel leaf as invalid and propagates this status to other domains via sync_domains(). Crucially, however, only ASID zero is flushed from the TLB. Immediately following this process, do_page_fault() may reuse the same physical frame for a new mapping by calling arch_mem_page_in(). At this point, any user thread that had previously accessed the evicted page retains a valid EL0 read/write translation to that physical frame under its own domain-specific ASID. This means the stale TLB entry continues to direct memory accesses to the newly allocated content of that frame rather than triggering a fault or returning an access violation.
This architectural inconsistency allows unprivileged code running in user space to exploit the condition by driving memory pressure to force page eviction and then accessing the previously touched address. By doing so, the attacker gains read and write access to whatever data the kernel subsequently placed into that physical frame. This can include sensitive supervisor-level data, partitions belonging to other memory domains, or stack contents of other threads. The vulnerability effectively defeats the isolation guarantees provided by CONFIG_USERSPACE, leading to potential information disclosure and arbitrary memory corruption. In a worst-case scenario, this allows an attacker to escalate privileges from unprivileged user space to full system compromise, bypassing fundamental security boundaries intended to protect kernel integrity and inter-process isolation.
From a classification perspective, this vulnerability aligns with CWE-200: Information Exposure due to improper handling of sensitive data in memory structures, as well as CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer, since it allows out-of-bounds access relative to domain boundaries. In terms of MITRE ATT&CK techniques, this maps to T1078: Valid Accounts and potentially T1530: Data from Cloud Storage or Local System Extraction depending on the specific data accessed, but fundamentally represents a privilege escalation vector through memory corruption (T1068) resulting in full system compromise. The exploitation requires an arm64 build with both CONFIG_USERSPACE and CONFIG_DEMAND_PAGING enabled, along with an active backing store and at least one demand-paged page that is also privately mapped into a user memory domain.
The remediation for this issue involves modifying the TLBI instruction used in invalidate_tlb_page() to use TLBI VAAE1{IS} instead of TLBI VAE1{IS}. The VAAE1 variant matches every ASID and both global and non-global entries, ensuring that no domain-tagged translation survives a page out operation. This change guarantees that dirty-bit updates and access-flag clears are properly reflected in the hardware caches across all memory domains. Additionally, the fix addresses a related issue where globalize_table() was incorrectly stripping EL0 permission and non-global attributes off domain-private leaves during paging synchronization. Correcting this ensures that user threads retain proper access to their own partitions and stacks while preventing the stale translation side effect described above.