]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
mm/huge_memory: Fix initialization of huge zero folio
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 18 Nov 2025 16:21:27 +0000 (08:21 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 18 Nov 2025 16:21:27 +0000 (08:21 -0800)
The recent fix to properly initialize the tags of the huge zero folio
had an unfortunate not-so-subtle side effect: it caused the actual
*contents* of the huge zero folio to not be initialized at all when the
hardware didn't support the memory tagging.

The reason was the unfortunate semantics of tag_clear_highpage(): on
hardware that didn't do the tagging, it would silently just not do
anything at all.  And since this is done only on arm64 with MTE support,
that basically meant most hardware.

It wasn't necessarily immediately obvious since the huge zero page isn't
necessarily very heavily used - or because it might already be zero
because all-zeroes is the most common pattern.  But it ends up causing
random odd user space failures when you do hit it.

The unfortunate semantics have been around for a while, but became a
real bug only when we started actively using __GFP_ZEROTAGS in the
generic get_huge_zero_folio() function - before that, it had only ever
been used in code that checked that the hardware supported it.

Fix this by simply changing the semantics of tag_clear_highpage() to
return whether it actually successfully did something or not.  While at
it, also make it initialize multiple pages in one go, since that's
actually what the only caller wants it to do and it simplifies the whole
logic.

Fixes: adfb6609c680 ("mm/huge_memory: initialise the tags of the huge zero folio")
Link: https://lore.kernel.org/all/20251117082023.90176-1-00107082@163.com/
Reviewed-by: David Hildenbrand (Red Hat) <david@kernel.org>
Reported-and-tested-by: David Wang <00107082@163.com>
Reported-and-tested-by: Carlos Llamas <cmllamas@google.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
arch/arm64/include/asm/page.h
arch/arm64/mm/fault.c
include/linux/highmem.h
mm/page_alloc.c

index 2312e6ee595fda5b818e4ea9e2f057b44ffd735c..258cca4b4873445a3264dec92145647b01a8aa67 100644 (file)
@@ -33,8 +33,8 @@ struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma,
                                                unsigned long vaddr);
 #define vma_alloc_zeroed_movable_folio vma_alloc_zeroed_movable_folio
 
-void tag_clear_highpage(struct page *to);
-#define __HAVE_ARCH_TAG_CLEAR_HIGHPAGE
+bool tag_clear_highpages(struct page *to, int numpages);
+#define __HAVE_ARCH_TAG_CLEAR_HIGHPAGES
 
 #define clear_user_page(page, vaddr, pg)       clear_page(page)
 #define copy_user_page(to, from, vaddr, pg)    copy_page(to, from)
index 125dfa6c613b222c828c8b4745af615546e9fc98..a193b6a5d1e65f0257096f24f51829cebcc51ec6 100644 (file)
@@ -967,20 +967,21 @@ struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma,
        return vma_alloc_folio(flags, 0, vma, vaddr);
 }
 
-void tag_clear_highpage(struct page *page)
+bool tag_clear_highpages(struct page *page, int numpages)
 {
        /*
         * Check if MTE is supported and fall back to clear_highpage().
         * get_huge_zero_folio() unconditionally passes __GFP_ZEROTAGS and
-        * post_alloc_hook() will invoke tag_clear_highpage().
+        * post_alloc_hook() will invoke tag_clear_highpages().
         */
-       if (!system_supports_mte()) {
-               clear_highpage(page);
-               return;
-       }
+       if (!system_supports_mte())
+               return false;
 
-       /* Newly allocated page, shouldn't have been tagged yet */
-       WARN_ON_ONCE(!try_page_mte_tagging(page));
-       mte_zero_clear_page_tags(page_address(page));
-       set_page_mte_tagged(page);
+       /* Newly allocated pages, shouldn't have been tagged yet */
+       for (int i = 0; i < numpages; i++, page++) {
+               WARN_ON_ONCE(!try_page_mte_tagging(page));
+               mte_zero_clear_page_tags(page_address(page));
+               set_page_mte_tagged(page);
+       }
+       return true;
 }
index 105cc4c00cc34de3931be49985982cb85a8c1ec5..abc20f9810fd4219626d1342a84e628418b294c4 100644 (file)
@@ -249,10 +249,12 @@ static inline void clear_highpage_kasan_tagged(struct page *page)
        kunmap_local(kaddr);
 }
 
-#ifndef __HAVE_ARCH_TAG_CLEAR_HIGHPAGE
+#ifndef __HAVE_ARCH_TAG_CLEAR_HIGHPAGES
 
-static inline void tag_clear_highpage(struct page *page)
+/* Return false to let people know we did not initialize the pages */
+static inline bool tag_clear_highpages(struct page *page, int numpages)
 {
+       return false;
 }
 
 #endif
index 600d9e981c23d75fdd4aec118e34f3f49d3de2e0..ed82ee55e66afffafcf6360b13da410119f2898d 100644 (file)
@@ -1822,14 +1822,9 @@ inline void post_alloc_hook(struct page *page, unsigned int order,
         * If memory tags should be zeroed
         * (which happens only when memory should be initialized as well).
         */
-       if (zero_tags) {
-               /* Initialize both memory and memory tags. */
-               for (i = 0; i != 1 << order; ++i)
-                       tag_clear_highpage(page + i);
+       if (zero_tags)
+               init = !tag_clear_highpages(page, 1 << order);
 
-               /* Take note that memory was initialized by the loop above. */
-               init = false;
-       }
        if (!should_skip_kasan_unpoison(gfp_flags) &&
            kasan_unpoison_pages(page, order, init)) {
                /* Take note that memory was initialized by KASAN. */