* [RFC PATCH v2 0/3] make persistent huge zero folio read-only
@ 2026-06-09 14:37 Xueyuan Chen
2026-06-09 14:37 ` [RFC PATCH v2 1/3] mm/huge_memory: " Xueyuan Chen
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Xueyuan Chen @ 2026-06-09 14:37 UTC (permalink / raw)
To: akpm, linux-mm
Cc: linux-kernel, linux-arm-kernel, x86, catalin.marinas, will, tglx,
mingo, bp, dave.hansen, luto, peterz, hpa, david, ljs, liam,
vbabka, rppt, surenb, mhocko, ziy, baolin.wang, npache,
ryan.roberts, dev.jain, baohua, lance.yang, yang, jannh,
Xueyuan Chen
Hi all,
This series makes the persistent huge zero folio read-only in the direct
map where the architecture can support it.
The motivation comes from Jann Horn's read-only zero page work[1] and the
follow-up discussion[2] with Yang Shi. As Jann pointed out, the kernel has
had bugs, including security bugs, where pages taken with read-only
semantics were later written to. For the huge zero folio, making the direct
map read-only turns such writes into faults instead of silently corrupting
shared zero contents.
Patch 1 adds a generic arch_make_pages_readonly() hook and uses it after
the persistent huge zero folio is allocated. Patches 2 and 3 implement the
hook for arm64 and x86.
If the hook is not implemented, or the architecture cannot safely update
the mapping, the existing writable mapping is left in place.
[1] https://lore.kernel.org/linux-mm/20260508-ro-zeropage-v1-1-9808abc20b49@google.com/
[2] https://lore.kernel.org/linux-mm/CAHbLzkrXXe7r3n3jXgDKtwZhRqj=jDx9E6dLOULohnhBguvi9A@mail.gmail.com/
RFC v1 -> RFC v2:
- Patch #01: Drop the READONLY_HUGE_ZERO_FOLIO Kconfig option
(per Dave, thanks!).
- Patch #01: Replace the huge-zero-folio-specific hook with a generic
page-range hook (per David, thanks!)
- Patch #02 and #03: Update the arm64 and x86 implementations for the new
hook.
- https://lore.kernel.org/linux-mm/20260527035607.14919-1-xueyuan.chen21@gmail.com/
Xueyuan Chen (3):
mm/huge_memory: make persistent huge zero folio read-only
arm64/mm: make pages read-only in the linear map
x86/mm: make pages read-only in the direct map
arch/arm64/mm/pageattr.c | 13 +++++++++++++
arch/x86/mm/init.c | 9 +++++++++
include/linux/mm.h | 2 ++
mm/huge_memory.c | 13 ++++++++++++-
4 files changed, 36 insertions(+), 1 deletion(-)
--
2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v2 1/3] mm/huge_memory: make persistent huge zero folio read-only
2026-06-09 14:37 [RFC PATCH v2 0/3] make persistent huge zero folio read-only Xueyuan Chen
@ 2026-06-09 14:37 ` Xueyuan Chen
2026-06-09 19:33 ` Dave Hansen
2026-06-09 19:45 ` Andrew Morton
2026-06-09 14:38 ` [RFC PATCH v2 2/3] arm64/mm: make pages read-only in the linear map Xueyuan Chen
2026-06-09 14:38 ` [RFC PATCH v2 3/3] x86/mm: make pages read-only in the direct map Xueyuan Chen
2 siblings, 2 replies; 8+ messages in thread
From: Xueyuan Chen @ 2026-06-09 14:37 UTC (permalink / raw)
To: akpm, linux-mm
Cc: linux-kernel, linux-arm-kernel, x86, catalin.marinas, will, tglx,
mingo, bp, dave.hansen, luto, peterz, hpa, david, ljs, liam,
vbabka, rppt, surenb, mhocko, ziy, baolin.wang, npache,
ryan.roberts, dev.jain, baohua, lance.yang, yang, jannh,
Xueyuan Chen, Dave Hansen
The huge zero folio is shared globally, and its contents should never
change after initialization. As Jann Horn pointed out[1], the kernel has
had bugs, including security bugs, where read-only pages were later written
to. If the persistent huge zero folio is read-only in the direct map, such
writes fault instead of silently corrupting the shared zero contents.
Add arch_make_pages_readonly() so mm code can request read-only direct-map
protection for a page range. Direct-map protection is
architecture-specific, so the generic weak implementation does nothing.
This was inspired by Jann Horn's read-only zero page work[1] and follow-up
discussion[2] with Yang Shi.
[1] https://lore.kernel.org/linux-mm/20260508-ro-zeropage-v1-1-9808abc20b49@google.com/
[2] https://lore.kernel.org/linux-mm/CAHbLzkrXXe7r3n3jXgDKtwZhRqj=jDx9E6dLOULohnhBguvi9A@mail.gmail.com/
Suggested-by: Dave Hansen <dave.hansen@intel.com>
Suggested-by: David Hildenbrand <david@kernel.org>
Co-developed-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
---
include/linux/mm.h | 2 ++
mm/huge_memory.c | 13 ++++++++++++-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 0f2612a70fb1..02d33cf45b01 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2936,6 +2936,8 @@ static inline int arch_make_folio_accessible(struct folio *folio)
}
#endif
+bool arch_make_pages_readonly(struct page *page, int nr_pages);
+
/*
* Some inline functions in vmstat.h depend on page_zone()
*/
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index b81d1ecb434b..2e9d01dc1197 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -308,6 +308,11 @@ static unsigned long shrink_huge_zero_folio_scan(struct shrinker *shrink,
return 0;
}
+bool __weak arch_make_pages_readonly(struct page *page, int nr_pages)
+{
+ return false;
+}
+
static struct shrinker *huge_zero_folio_shrinker;
#ifdef CONFIG_SYSFS
@@ -982,8 +987,14 @@ static int __init thp_shrinker_init(void)
* that get_huge_zero_folio() will most likely not fail as
* thp_shrinker_init() is invoked early on during boot.
*/
- if (!get_huge_zero_folio())
+ if (!get_huge_zero_folio()) {
pr_warn("Allocating persistent huge zero folio failed\n");
+ return 0;
+ }
+
+ arch_make_pages_readonly(folio_page(huge_zero_folio, 0),
+ HPAGE_PMD_NR);
+
return 0;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v2 2/3] arm64/mm: make pages read-only in the linear map
2026-06-09 14:37 [RFC PATCH v2 0/3] make persistent huge zero folio read-only Xueyuan Chen
2026-06-09 14:37 ` [RFC PATCH v2 1/3] mm/huge_memory: " Xueyuan Chen
@ 2026-06-09 14:38 ` Xueyuan Chen
2026-06-09 14:38 ` [RFC PATCH v2 3/3] x86/mm: make pages read-only in the direct map Xueyuan Chen
2 siblings, 0 replies; 8+ messages in thread
From: Xueyuan Chen @ 2026-06-09 14:38 UTC (permalink / raw)
To: akpm, linux-mm
Cc: linux-kernel, linux-arm-kernel, x86, catalin.marinas, will, tglx,
mingo, bp, dave.hansen, luto, peterz, hpa, david, ljs, liam,
vbabka, rppt, surenb, mhocko, ziy, baolin.wang, npache,
ryan.roberts, dev.jain, baohua, lance.yang, yang, jannh,
Xueyuan Chen
Implement arch_make_pages_readonly() for arm64. Make the corresponding
linear-map range read-only so unexpected writes fault instead of
corrupting shared contents.
Respect can_set_direct_map() before touching the linear map. If arm64
cannot safely update the linear map at page granularity, leave the mapping
unchanged.
Co-developed-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
---
arch/arm64/mm/pageattr.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index ce035e1b4eaf..da97ec7d5195 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -3,6 +3,7 @@
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
+#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mem_encrypt.h>
@@ -147,6 +148,18 @@ static int __change_memory_common(unsigned long start, unsigned long size,
return ret;
}
+bool arch_make_pages_readonly(struct page *page, int nr_pages)
+{
+ unsigned long addr = (unsigned long)page_address(page);
+
+ if (!can_set_direct_map())
+ return false;
+
+ return !__change_memory_common(addr, PAGE_SIZE * nr_pages,
+ __pgprot(PTE_RDONLY),
+ __pgprot(PTE_WRITE));
+}
+
static int change_memory_common(unsigned long addr, int numpages,
pgprot_t set_mask, pgprot_t clear_mask)
{
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v2 3/3] x86/mm: make pages read-only in the direct map
2026-06-09 14:37 [RFC PATCH v2 0/3] make persistent huge zero folio read-only Xueyuan Chen
2026-06-09 14:37 ` [RFC PATCH v2 1/3] mm/huge_memory: " Xueyuan Chen
2026-06-09 14:38 ` [RFC PATCH v2 2/3] arm64/mm: make pages read-only in the linear map Xueyuan Chen
@ 2026-06-09 14:38 ` Xueyuan Chen
2 siblings, 0 replies; 8+ messages in thread
From: Xueyuan Chen @ 2026-06-09 14:38 UTC (permalink / raw)
To: akpm, linux-mm
Cc: linux-kernel, linux-arm-kernel, x86, catalin.marinas, will, tglx,
mingo, bp, dave.hansen, luto, peterz, hpa, david, ljs, liam,
vbabka, rppt, surenb, mhocko, ziy, baolin.wang, npache,
ryan.roberts, dev.jain, baohua, lance.yang, yang, jannh,
Xueyuan Chen
Implement arch_make_pages_readonly() for x86. Make the corresponding
direct-map range read-only so unexpected writes fault instead of
corrupting shared contents.
Reject highmem pages because they have no permanent direct-map address.
Treat the set_memory_ro() update as best effort. If it fails, leave the
mapping unchanged.
Co-developed-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
---
arch/x86/mm/init.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index fb67217fddcd..ff0a7003eaeb 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -3,6 +3,7 @@
#include <linux/ioport.h>
#include <linux/swap.h>
#include <linux/memblock.h>
+#include <linux/mm.h>
#include <linux/swapfile.h>
#include <linux/swapops.h>
#include <linux/kmemleak.h>
@@ -38,6 +39,14 @@
#include "mm_internal.h"
+bool arch_make_pages_readonly(struct page *page, int nr_pages)
+{
+ if (PageHighMem(page))
+ return false;
+
+ return !set_memory_ro((unsigned long)page_address(page), nr_pages);
+}
+
/*
* Tables translating between page_cache_type_t and pte encoding.
*
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v2 1/3] mm/huge_memory: make persistent huge zero folio read-only
2026-06-09 14:37 ` [RFC PATCH v2 1/3] mm/huge_memory: " Xueyuan Chen
@ 2026-06-09 19:33 ` Dave Hansen
2026-06-10 3:20 ` Lance Yang
2026-06-09 19:45 ` Andrew Morton
1 sibling, 1 reply; 8+ messages in thread
From: Dave Hansen @ 2026-06-09 19:33 UTC (permalink / raw)
To: Xueyuan Chen, akpm, linux-mm
Cc: linux-kernel, linux-arm-kernel, x86, catalin.marinas, will, tglx,
mingo, bp, dave.hansen, luto, peterz, hpa, david, ljs, liam,
vbabka, rppt, surenb, mhocko, ziy, baolin.wang, npache,
ryan.roberts, dev.jain, baohua, lance.yang, yang, jannh
On 6/9/26 07:37, Xueyuan Chen wrote:
> +bool __weak arch_make_pages_readonly(struct page *page, int nr_pages)
> +{
> + return false;
> +}
This is a rather wonky function. It's going to cause all kinds of fun if
it is used like this:
arch_make_pages_readonly(syscall_table, 1);
It's also kinda weird to have it return a bool, and not check that bool
at the single call site. Some things come to mind:
1. This function needs commenting. It needs to say what it does, when
architectures should override it and what their implementations
should look like. It needs to be clear that this can't be used for
anything really important. What should architectures do with alias
mappings? Are they allowed to touch non-direct map aliases? Are they
required to?
2. The return type needs to be reconsidered. Is 'bool' even acceptable?
Should it just be 'void' if callers can't do anything when it fails?
3. What should the naming be? "readonly" vs "ro". Should it have a
"maybe" since it's kinda optional?
4. Should this new API be folio or page-based in the first place?
5. Is mm/huge_memory.c the right place to define a generic mm function,
even a stub?
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v2 1/3] mm/huge_memory: make persistent huge zero folio read-only
2026-06-09 14:37 ` [RFC PATCH v2 1/3] mm/huge_memory: " Xueyuan Chen
2026-06-09 19:33 ` Dave Hansen
@ 2026-06-09 19:45 ` Andrew Morton
2026-06-10 2:15 ` Lance Yang
1 sibling, 1 reply; 8+ messages in thread
From: Andrew Morton @ 2026-06-09 19:45 UTC (permalink / raw)
To: Xueyuan Chen
Cc: linux-mm, linux-kernel, linux-arm-kernel, x86, catalin.marinas,
will, tglx, mingo, bp, dave.hansen, luto, peterz, hpa, david, ljs,
liam, vbabka, rppt, surenb, mhocko, ziy, baolin.wang, npache,
ryan.roberts, dev.jain, baohua, lance.yang, yang, jannh,
Dave Hansen
On Tue, 9 Jun 2026 22:37:59 +0800 Xueyuan Chen <xueyuan.chen21@gmail.com> wrote:
> The huge zero folio is shared globally, and its contents should never
> change after initialization. As Jann Horn pointed out[1], the kernel has
> had bugs, including security bugs, where read-only pages were later written
> to. If the persistent huge zero folio is read-only in the direct map, such
> writes fault instead of silently corrupting the shared zero contents.
>
> Add arch_make_pages_readonly() so mm code can request read-only direct-map
> protection for a page range. Direct-map protection is
> architecture-specific, so the generic weak implementation does nothing.
>
> This was inspired by Jann Horn's read-only zero page work[1] and follow-up
> discussion[2] with Yang Shi.
>
> [1] https://lore.kernel.org/linux-mm/20260508-ro-zeropage-v1-1-9808abc20b49@google.com/
> [2] https://lore.kernel.org/linux-mm/CAHbLzkrXXe7r3n3jXgDKtwZhRqj=jDx9E6dLOULohnhBguvi9A@mail.gmail.com/
>
> ...
>
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -308,6 +308,11 @@ static unsigned long shrink_huge_zero_folio_scan(struct shrinker *shrink,
> return 0;
> }
>
> +bool __weak arch_make_pages_readonly(struct page *page, int nr_pages)
> +{
> + return false;
> +}
> +
> static struct shrinker *huge_zero_folio_shrinker;
>
> #ifdef CONFIG_SYSFS
> @@ -982,8 +987,14 @@ static int __init thp_shrinker_init(void)
> * that get_huge_zero_folio() will most likely not fail as
> * thp_shrinker_init() is invoked early on during boot.
> */
> - if (!get_huge_zero_folio())
> + if (!get_huge_zero_folio()) {
> pr_warn("Allocating persistent huge zero folio failed\n");
> + return 0;
> + }
> +
> + arch_make_pages_readonly(folio_page(huge_zero_folio, 0),
> + HPAGE_PMD_NR);
Can it simply pass the folio?
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v2 1/3] mm/huge_memory: make persistent huge zero folio read-only
2026-06-09 19:45 ` Andrew Morton
@ 2026-06-10 2:15 ` Lance Yang
0 siblings, 0 replies; 8+ messages in thread
From: Lance Yang @ 2026-06-10 2:15 UTC (permalink / raw)
To: akpm
Cc: xueyuan.chen21, linux-mm, linux-kernel, linux-arm-kernel, x86,
catalin.marinas, will, tglx, mingo, bp, dave.hansen, luto, peterz,
hpa, david, ljs, liam, vbabka, rppt, surenb, mhocko, ziy,
baolin.wang, npache, ryan.roberts, dev.jain, baohua, lance.yang,
yang, jannh, dave.hansen
On Tue, Jun 09, 2026 at 12:45:49PM -0700, Andrew Morton wrote:
>On Tue, 9 Jun 2026 22:37:59 +0800 Xueyuan Chen <xueyuan.chen21@gmail.com> wrote:
>
>> The huge zero folio is shared globally, and its contents should never
>> change after initialization. As Jann Horn pointed out[1], the kernel has
>> had bugs, including security bugs, where read-only pages were later written
>> to. If the persistent huge zero folio is read-only in the direct map, such
>> writes fault instead of silently corrupting the shared zero contents.
>>
>> Add arch_make_pages_readonly() so mm code can request read-only direct-map
>> protection for a page range. Direct-map protection is
>> architecture-specific, so the generic weak implementation does nothing.
>>
>> This was inspired by Jann Horn's read-only zero page work[1] and follow-up
>> discussion[2] with Yang Shi.
>>
>> [1] https://lore.kernel.org/linux-mm/20260508-ro-zeropage-v1-1-9808abc20b49@google.com/
>> [2] https://lore.kernel.org/linux-mm/CAHbLzkrXXe7r3n3jXgDKtwZhRqj=jDx9E6dLOULohnhBguvi9A@mail.gmail.com/
>>
>> ...
>>
>> --- a/mm/huge_memory.c
>> +++ b/mm/huge_memory.c
>> @@ -308,6 +308,11 @@ static unsigned long shrink_huge_zero_folio_scan(struct shrinker *shrink,
>> return 0;
>> }
>>
>> +bool __weak arch_make_pages_readonly(struct page *page, int nr_pages)
>> +{
>> + return false;
>> +}
>> +
>> static struct shrinker *huge_zero_folio_shrinker;
>>
>> #ifdef CONFIG_SYSFS
>> @@ -982,8 +987,14 @@ static int __init thp_shrinker_init(void)
>> * that get_huge_zero_folio() will most likely not fail as
>> * thp_shrinker_init() is invoked early on during boot.
>> */
>> - if (!get_huge_zero_folio())
>> + if (!get_huge_zero_folio()) {
>> pr_warn("Allocating persistent huge zero folio failed\n");
>> + return 0;
>> + }
>> +
>> + arch_make_pages_readonly(folio_page(huge_zero_folio, 0),
>> + HPAGE_PMD_NR);
>
>Can it simply pass the folio?
Right, this came from the RFC v1 discussion[1]. David preferred a page-
range helper for possible future non-folio callers, not something folio-
only.
Of course, we could also add a folio wrapper on top of that if needed :)
[1] https://lore.kernel.org/linux-mm/929875a2-9e94-4dbc-9c98-b342ccc3f4e2@kernel.org/
Thanks, Lance
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v2 1/3] mm/huge_memory: make persistent huge zero folio read-only
2026-06-09 19:33 ` Dave Hansen
@ 2026-06-10 3:20 ` Lance Yang
0 siblings, 0 replies; 8+ messages in thread
From: Lance Yang @ 2026-06-10 3:20 UTC (permalink / raw)
To: dave.hansen
Cc: xueyuan.chen21, akpm, linux-mm, linux-kernel, linux-arm-kernel,
x86, catalin.marinas, will, tglx, mingo, bp, dave.hansen, luto,
peterz, hpa, david, ljs, liam, vbabka, rppt, surenb, mhocko, ziy,
baolin.wang, npache, ryan.roberts, dev.jain, baohua, lance.yang,
yang, jannh
Hi Dave,
Thanks for taking the time to review.
On Tue, Jun 09, 2026 at 12:33:36PM -0700, Dave Hansen wrote:
>On 6/9/26 07:37, Xueyuan Chen wrote:
>> +bool __weak arch_make_pages_readonly(struct page *page, int nr_pages)
>> +{
>> + return false;
>> +}
>
>This is a rather wonky function. It's going to cause all kinds of fun if
>it is used like this:
>
> arch_make_pages_readonly(syscall_table, 1);
Ouch, yeah, it is ...
>It's also kinda weird to have it return a bool, and not check that bool
>at the single call site. Some things come to mind:
>
>1. This function needs commenting. It needs to say what it does, when
> architectures should override it and what their implementations
> should look like. It needs to be clear that this can't be used for
> anything really important. What should architectures do with alias
> mappings? Are they allowed to touch non-direct map aliases? Are they
> required to?
Agreed. Needs a real comment ...
Just meant as a best-effort direct/linear-map permission chang, nothing
stronger than that. I should spell out what happens, or does not happen,
to non-direct-map aliases, if anything, and make clear callers cannot
treat this as a hard guarantee :D
>2. The return type needs to be reconsidered. Is 'bool' even acceptable?
> Should it just be 'void' if callers can't do anything when it fails?
Maybe ignoring it is OK now, but someone may need the return value later?
>3. What should the naming be? "readonly" vs "ro". Should it have a
> "maybe" since it's kinda optional?
Fair point. "make" may be overstating it a bit ...
With a return value, arch_try_make_pages_readonly() sounds about right
to me. If we end up with void and pure best-effort semantics, maybe
arch_maybe_make_pages_readonly() fits better :)
>4. Should this new API be folio or page-based in the first place?
For page vs folio, I was mostly following David's RFC v1 suggestion.
Current caller is a folio, sure, but the page-range helper leaves room
for non-folio users later. Happy to add a simple folio wrapper if that
reads better ;)
>5. Is mm/huge_memory.c the right place to define a generic mm function,
> even a stub?
Ah, you're right! My bad, wrong place for a generic stub. Will move it
out for RFC v3.
Thanks, Lance
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-10 3:20 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-09 14:37 [RFC PATCH v2 0/3] make persistent huge zero folio read-only Xueyuan Chen
2026-06-09 14:37 ` [RFC PATCH v2 1/3] mm/huge_memory: " Xueyuan Chen
2026-06-09 19:33 ` Dave Hansen
2026-06-10 3:20 ` Lance Yang
2026-06-09 19:45 ` Andrew Morton
2026-06-10 2:15 ` Lance Yang
2026-06-09 14:38 ` [RFC PATCH v2 2/3] arm64/mm: make pages read-only in the linear map Xueyuan Chen
2026-06-09 14:38 ` [RFC PATCH v2 3/3] x86/mm: make pages read-only in the direct map Xueyuan Chen
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.