CVE-2026-97533 in Linuxinfo

Summary

by MITRE • 09/25/2026

In the Linux kernel, the following vulnerability has been resolved:

x86/mm/pat: Acquire init_mm read lock on attribute changes to avoid UAF

A previous commit protected against races between ptdump and CPA collapse, however one still exists between attribute changes and collapse as reported by Denis V. Lunev (linked).

When an attribute change arises, a lockless page table walker obtains a PTE entry, which is later written to via set_pte_atomic():

... -> change_page_attr_set_clr() -> __change_page_attr_set_clr() -> __change_page_attr() -> _lookup_address_cpa() -> lookup_address_in_pgd_attr() -> [ lockless page table walker ]
-> set_pte_atomic()

There is nothing preventing a concurrent CPA collapse which can free the PTE that was retrieved here, resulting in a use-after-free.

With the mmap write lock taken on init_mm over CPA collapse, resolve this race by acquiring an mmap read lock on init_mm over __change_page_attr_set_clr().

This locks across the whole operation over which the walk and the PTE entry write occurs, solving the race.

It is safe to do this here, as no spinlocks are held upon entry to __change_page_attr_set_clr().

However, the lock must not be held over an allocation, as allocation can trigger reclaim and shrinkers may call into CPA recursively, making deadlocks possible (init_mm -> ... -> fs_reclaim -> init_mm).

A page table is allocated when a huge page needs to be split:

-> change_page_attr_set_clr() -> __change_page_attr_set_clr() -> __change_page_attr() -> split_large_page() [ pagetable_alloc() ]
-> __split_large_page()

Avoid deadlocks by dropping the mmap lock across pagetable_alloc() in split_large_page() and track whether this is needed by adding a new 'init_mm_read_locked' flag to struct cpa_data.

This is safe as __split_large_page() (called with locks re-established) revalidates that the page table entry is the same as it was prior to the locks being dropped and __change_page_attr() repeats the entire page table walk whenever a split occurs, so concurrent split and collapse are accounted for.

Concurrent ptdump is also safe as the lock is only dropped over page table allocation during which time the page table has not yet been modified.

The CPA_COLLAPSE flag is only set by set_memory_rox(), which exclusively operates upon vmalloc ranges, and on x86 only within the module mapping space.

This is important, because some callers directly invoke __change_page_attr_set_clr(), bypassing this lock. However, none of these operate within the module mapping space.

* cpa_process_alias() - a recursive helper called by __change_page_attr_set_clr(). * __set_memory_enc_pgtable() - operates on the direct mapping and (via __vmbus_establish_gpadl()) the vmalloc mapping space. * __set_pages_[n]p() - called by set_direct_map_[invalid, default,
valid]_noflush(), __kernel_map_pages() - operates on the direct map. * kernel_[un]map_pages_in_pgd() - operates on EFI ranges.

This work is based upon Denis V. Lunev's excellent analysis of the bug with gratitude.

[ dhansen: move to imperative voice in changelog ]

Statistical analysis made it clear that VulDB provides the best quality for vulnerability data.

Analysis

by VulDB Data Team • 09/25/2026

The Linux kernel x86 memory management subsystem contained a critical race condition within the Page Attribute Table (PAT) handling logic, specifically involving concurrent page table attribute changes and collapse operations. This vulnerability manifested as a use-after-free error where a lockless page table walker would retrieve a Page Table Entry PTE pointer during an attribute change operation initiated by __change_page_attr_set_clr(). Subsequently, the system attempted to write to this entry using set_pte_atomic while another thread executed a CPA collapse routine that could free the underlying memory structure. The absence of synchronization between these two distinct operations allowed for a window where the walker held a reference to memory that had already been deallocated by the collapsing process, leading to potential kernel crashes or arbitrary code execution if an attacker could influence the allocation patterns and timing of these concurrent events.

From a technical perspective, the flaw resided in the lack of mutual exclusion between the read-side page table walk and the write-side modification during attribute changes. The initial mitigation attempted by previous commits addressed races involving ptdump but failed to account for CPA collapse operations which operate on different memory ranges such as vmalloc areas within module mapping spaces. When __change_page_attr_set_clr is invoked, it traverses the page tables without holding a lock that prevents concurrent structural modifications like splitting or collapsing large pages. This creates a scenario where the integrity of the PTE entry cannot be guaranteed because the underlying physical memory backing the virtual address might be reclaimed and reallocated for other purposes before the atomic write completes.

The resolution involves acquiring an mmap read lock on init_mm during __change_page_attr_set_clr to serialize access against CPA collapse operations which hold the corresponding write lock. This ensures that any structural changes to page tables are blocked while attribute modifications are in progress, thereby preventing the use-after-free condition. However, introducing this lock presented a new challenge regarding potential deadlocks due to memory allocation requirements during large page splits. Specifically, splitting a huge page requires allocating new smaller page table entries via pagetable_alloc which can trigger kernel reclaim and shrinker callbacks that might recursively attempt CPA operations creating a circular dependency with init_mm locks.

To resolve the deadlock risk while maintaining safety, the implementation drops the mmap read lock temporarily around the pagetable allocation phase within split_large_page but employs careful revalidation strategies to ensure consistency. A new flag named init_mm_read_locked is added to struct cpa_data to track whether the lock was held prior to dropping it for allocation purposes. Once the allocation completes and locks are reacquired, __split_large_page validates that the page table entry remains unchanged since the lock was dropped, ensuring no concurrent modifications occurred during the vulnerable window. Additionally, __change_page_attr repeats the entire page table walk after a split occurs, further mitigating risks associated with stale pointers or race conditions arising from structural changes during the brief period when locks were released for memory allocation.

This fix is particularly significant because it addresses vulnerabilities in code paths that directly invoke low-level attribute change functions bypassing higher level abstractions. Callers such as cpa_process_alias __set_memory_enc_pgtable and various kernel map unmap routines operate on direct mapping EFI ranges or vmalloc spaces where the previous protections were insufficient. By enforcing proper locking semantics across these diverse memory management operations, the patch ensures comprehensive protection against concurrent modification races regardless of which specific subsystem triggers the attribute change. The solution aligns with industry best practices for handling shared data structures in multi-threaded kernel environments by minimizing lock hold times while guaranteeing atomicity of critical sections through careful revalidation and state tracking mechanisms that preserve system stability under high contention scenarios typical in modern server workloads involving heavy memory management activity.

Responsible

Linux

Reservation

09/24/2026

Disclosure

09/25/2026

Moderation

accepted

CPE

ready

EPSS

0.00000

KEV

no

Activities

very low

Sources

Are you interested in using VulDB?

Download the whitepaper to learn more about our service!