]> www.infradead.org Git - users/willy/pagecache.git/commitdiff
x86: Clear _PAGE_DIRTY when we clear _PAGE_RW
authorMatthew Wilcox (Oracle) <willy@infradead.org>
Tue, 25 Feb 2025 19:25:12 +0000 (14:25 -0500)
committerMatthew Wilcox (Oracle) <willy@infradead.org>
Tue, 25 Feb 2025 19:47:20 +0000 (14:47 -0500)
The bit pattern of _PAGE_DIRTY set and _PAGE_RW clear is used to
mark shadow stacks.  This is currently checked for in mk_pte() but
not pfn_pte().  If we add the check to pfn_pte(), it catches vfree()
calling set_direct_map_invalid_noflush() which calls __change_page_attr()
which loads the old protection bits from the PTE, clears the specified
bits and uses pfn_pte() to construct the new PTE.

We should, therefore, clear the _PAGE_DIRTY bit whenever we clear
_PAGE_RW.  I opted to do it in the callers in case we want to use
__change_page_attr() to create shadow stacks inside the kernel at some
point in the future.  Arguably, we might also want to clear _PAGE_ACCESSED
here.

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
Reported-by: kernel test robot <oliver.sang@intel.com>
Closes: https://lore.kernel.org/oe-lkp/202502241646.719f4651-lkp@intel.com
arch/x86/mm/pat/set_memory.c

index ef4514d64c0524e5854fa106e3f37ff1e1ba10a2..b491d8190a6c5c944cdfde2a63c41a8a5d7f28a5 100644 (file)
@@ -2420,7 +2420,7 @@ static int __set_pages_np(struct page *page, int numpages)
                                .pgd = NULL,
                                .numpages = numpages,
                                .mask_set = __pgprot(0),
-                               .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW),
+                               .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY),
                                .flags = CPA_NO_CHECK_ALIAS };
 
        /*
@@ -2507,7 +2507,7 @@ int __init kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address,
                .pgd = pgd,
                .numpages = numpages,
                .mask_set = __pgprot(0),
-               .mask_clr = __pgprot(~page_flags & (_PAGE_NX|_PAGE_RW)),
+               .mask_clr = __pgprot(~page_flags & (_PAGE_NX|_PAGE_RW|_PAGE_DIRTY)),
                .flags = CPA_NO_CHECK_ALIAS,
        };
 
@@ -2550,7 +2550,7 @@ int __init kernel_unmap_pages_in_pgd(pgd_t *pgd, unsigned long address,
                .pgd            = pgd,
                .numpages       = numpages,
                .mask_set       = __pgprot(0),
-               .mask_clr       = __pgprot(_PAGE_PRESENT | _PAGE_RW),
+               .mask_clr       = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY),
                .flags          = CPA_NO_CHECK_ALIAS,
        };