[PATCH mm-hotfixes v3 2/4] x86/mm/pat: acquire mmap lock on page table free to avoid ptdump UAF

David Hildenbrand (Arm) david at kernel.org
Thu Jul 16 02:55:10 PDT 2026


On 7/14/26 19:24, Lorenzo Stoakes wrote:
> x86 implements page attribute modification using its Change Page
> Attributes (CPA) mechanism.
> 
> This tracks properties of ranges such as cache mode through x86 page
> attributes, and as part of that logic manipulates kernel page tables.
> 
> Since commit 41d88484c71c ("x86/mm/pat: restore large ROX pages after
> fragmentation") ranges of kernel page table entries can be collapsed into
> huge page table entries as part of this logic.
> 
> As part of this collapse, it frees the page tables which the collapsed
> entries previously pointed to, and it does so without any relevant locks
> being held to preclude concurrent kernel page table walkers.
> 
> The only way this code can be reached is if CPA_COLLAPSE is specified, and
> this is only set in set_memory_rox() via:
> 
> set_memory_rox()
> -> change_page_attr_set_clr()
> -> cpa_flush()
> -> cpa_collapse_large_pages()
> 
> Notable users of this are execmem and bpf when manipulating executable
> mappings.
> 
> However, this is problematic for ptdump as it walks ranges it does not own
> and thus runs the risk of a use-after-free on page tables freed underneath
> it.
> 
> Resolve the issue by acquiring the mmap read lock on init_mm which prevents
> a concurrent ptdump as it acquires the write lock.
> 
> It is safe to acquire a sleeping lock as all the callers invoke
> set_memory_rox() from process context and in any case,
> change_page_attr_set_clr() calls vm_unmap_alias() which ultimately takes a
> mutex, disallowing atomic context here.
> 
> Fixes: 41d88484c71c ("x86/mm/pat: restore large ROX pages after fragmentation")
> Cc: stable at vger.kernel.org
> Reviewed-by: Mike Rapoport (Microsoft) <rppt at kernel.org>
> Reviewed-by: Kiryl Shutsemau (Meta) <kas at kernel.org>
> Signed-off-by: Lorenzo Stoakes <ljs at kernel.org>
> ---
>  arch/x86/mm/pat/set_memory.c | 14 +++++++++++---
>  1 file changed, 11 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c
> index d023a40a1e03..4c4b8244502f 100644
> --- a/arch/x86/mm/pat/set_memory.c
> +++ b/arch/x86/mm/pat/set_memory.c
> @@ -22,6 +22,7 @@
>  #include <linux/cc_platform.h>
>  #include <linux/set_memory.h>
>  #include <linux/memregion.h>
> +#include <linux/cleanup.h>
>  
>  #include <asm/e820/api.h>
>  #include <asm/processor.h>
> @@ -436,9 +437,16 @@ static void cpa_collapse_large_pages(struct cpa_data *cpa)
>  
>  	flush_tlb_all();
>  
> -	list_for_each_entry_safe(ptdesc, tmp, &pgtables, pt_list) {
> -		list_del(&ptdesc->pt_list);
> -		pagetable_free(ptdesc);
> +	/*
> +	 * ptdump might read these page tables, so avoid a use-after-free by
> +	 * acquiring the mmap read lock on init_mm (ptdump acquires the mmap
> +	 * write lock).
> +	 */
> +	scoped_guard(mmap_read_lock, &init_mm) {
> +		list_for_each_entry_safe(ptdesc, tmp, &pgtables, pt_list) {
> +			list_del(&ptdesc->pt_list);
> +			pagetable_free(ptdesc);
> +		}
>  	}
>  }
>  
> 

Reviewed-by: David Hildenbrand (Arm) <david at kernel.org>

-- 
Cheers,

David



More information about the linux-arm-kernel mailing list