Subject: [RFC PATCH 2/3] arm64/mm: make huge zero folio read-only in linear map

From: Xueyuan chen <[email-masked]>
Date: Wed, 27 May 2026 11:56:06 +0800

From: Xueyuan Chen <[email-masked]>

Implement arch_make_huge_zero_folio_readonly() for arm64. Once allocated,
try to make the folio read-only in the linear map so unexpected writes
fault instead of corrupting shared zero contents.

Respect can_set_direct_map() before touching the linear map, and treat the
pageattr update as best effort: it can still fail while splitting a leaf
mapping or applying new permissions. If that happens, generic THP keeps
using the writable persistent huge zero folio.

Co-developed-by: Lance Yang <[email-masked]>
Signed-off-by: Lance Yang <[email-masked]>
Signed-off-by: Xueyuan Chen <[email-masked]>
---
 arch/arm64/Kconfig       |  1 +
 arch/arm64/mm/pageattr.c | 16 ++++++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index fe60738e5943..3cd705dd5251 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -44,6 +44,7 @@ config ARM64
 	select ARCH_HAS_PREEMPT_LAZY
 	select ARCH_HAS_PTDUMP
 	select ARCH_HAS_PTE_SPECIAL
+	select ARCH_HAS_READONLY_HUGE_ZERO_FOLIO
 	select ARCH_HAS_HW_PTE_YOUNG
 	select ARCH_HAS_SETUP_DMA_OPS
 	select ARCH_HAS_SET_DIRECT_MAP
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index ce035e1b4eaf..51ce31e74a18 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -3,7 +3,9 @@
  * Copyright (c) 2014, The Linux Foundation. All rights reserved.
  */
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <linux/mm.h>
+#include <linux/huge_mm.h>
 #include <linux/module.h>
 #include <linux/mem_encrypt.h>
 #include <linux/sched.h>
@@ -147,6 +149,20 @@ static int __change_memory_common(unsigned long start, unsigned long size,
 	return ret;
 }
 
+#ifdef CONFIG_READONLY_HUGE_ZERO_FOLIO
+bool __init arch_make_huge_zero_folio_readonly(struct folio *folio)
+{
+	unsigned long addr = (unsigned long)folio_address(folio);
+
+	if (!can_set_direct_map())
+		return false;
+
+	return !__change_memory_common(addr, PMD_SIZE,
+				       __pgprot(PTE_RDONLY),
+				       __pgprot(PTE_WRITE));
+}
+#endif
+
 static int change_memory_common(unsigned long addr, int numpages,
 				pgprot_t set_mask, pgprot_t clear_mask)
 {
-- 
2.47.3