From: "David Hildenbrand (Arm)" <david@kernel.org>
To: Xueyuan Chen <xueyuan.chen21@gmail.com>, akpm@linux-foundation.org
Cc: ljs@kernel.org, usama.arif@linux.dev, catalin.marinas@arm.com,
will@kernel.org, linux-arm-kernel@lists.infradead.org,
tglx@kernel.org, mingo@redhat.com, bp@alien8.de,
dave.hansen@linux.intel.com, x86@kernel.org, hpa@zytor.com,
rppt@kernel.org, ryan.roberts@arm.com, ziy@nvidia.com,
baohua@kernel.org, linux-mm@kvack.org,
linux-kernel@vger.kernel.org, Lance Yang <lance.yang@linux.dev>
Subject: Re: [PATCH v6 1/3] mm: make persistent huge zero folio read-only
Date: Tue, 25 Aug 2026 17:51:38 +0200 [thread overview]
Message-ID: <fd78d494-7f47-4cf8-b66e-c95c2e3ed889@kernel.org> (raw)
In-Reply-To: <20260730090647.2401252-2-xueyuan.chen21@gmail.com>
On 7/30/26 11:06, Xueyuan Chen wrote:
> The persistent huge zero folio is shared globally and should stay zero
> after initialization. As Jann Horn pointed out[1], kernel bugs have ended
> up writing to pages that were meant to be read-only, including in
> security-sensitive cases. Making the persistent huge zero folio read-only
> in the direct map turns such writes into faults instead of silent zero-page
> corruption.
>
> Add set_direct_map_ro_noflush() so mm code can make a direct-map range
> read-only. Use an address-based signature to match ongoing direct-map
> helper work[2], where existing page-based helpers may move the same way.
> The helper is direct-map specific and leaves TLB invalidation to its
> caller. Architectures without direct-map permission support keep existing
> behavior through the generic stub.
>
> The folio is allocated and zeroed through the writable direct map before
> thp_shrinker_init() changes its permissions. thp_shrinker_init() is called
> from hugepage_init(), which is registered as a subsys_initcall and runs
> after SMP initialization. Stale writable kernel TLB entries may therefore
> exist. Flush the direct-map range immediately after the page-table update
> so they cannot bypass the read-only mapping.
>
> GFP_TRANSHUGE includes __GFP_HIGHMEM. On 32-bit systems, the persistent
> folio may therefore reside in high memory, where folio_address() returns
> NULL. Such a folio has no permanent direct-map mapping to protect, so skip
> the permission change and TLB flush.
>
> Treat the direct-map permission change as best-effort. Architectures that
> do not implement the helper keep the existing behavior via the generic
> stub.
>
> Inspired by Jann Horn's read-only zero page work[1] and follow-up
> discussion[3] with Yang Shi.
>
> Link: https://lore.kernel.org/r/20260508-ro-zeropage-v1-1-9808abc20b49@google.com [1]
> Link: https://lore.kernel.org/r/0e5b23a6-4895-454a-9dfa-6dc21adc2991@kernel.org [2]
> Link: https://lore.kernel.org/r/CAHbLzkrXXe7r3n3jXgDKtwZhRqj=jDx9E6dLOULohnhBguvi9A@mail.gmail.com [3]
> Link: https://lore.kernel.org/r/20260727113530.5cd347581a66b3279490a604@linux-foundation.org
> Suggested-by: David Hildenbrand <david@kernel.org>
> Suggested-by: Usama Arif <usama.arif@linux.dev>
> 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/set_memory.h | 29 +++++++++++++++++++++++++++++
> mm/huge_memory.c | 20 +++++++++++++++++++-
> 2 files changed, 48 insertions(+), 1 deletion(-)
>
> diff --git a/include/linux/set_memory.h b/include/linux/set_memory.h
> index 3030d9245f5a..e83ced6a3827 100644
> --- a/include/linux/set_memory.h
> +++ b/include/linux/set_memory.h
> @@ -40,6 +40,24 @@ static inline int set_direct_map_valid_noflush(struct page *page,
> return 0;
> }
>
> +/**
> + * set_direct_map_ro_noflush - make a direct-map range read-only
> + * @addr: start address in the direct map
> + * @nr_pages: number of pages starting at @addr
> + *
> + * Make the direct-map range starting at @addr read-only without invalidating
> + * TLBs. Callers must either ensure that no stale writable translations can
> + * be used, or treat the permission change as a best-effort hardening step.
> + *
> + * Return: 0 on success or when direct-map permission changes are unsupported,
> + * or a negative errno on failure.
> + */
> +static inline int set_direct_map_ro_noflush(const void *addr,
> + unsigned long nr_pages)
Two tabs here in MM land.
> +{
> + return 0;
> +}
Nobody checks the return value, so how helpful is it to return it? Do we expect
other users to check for the return code?
I guess we just try to keep consistency with the other functions?
[...]
> @@ -932,6 +934,8 @@ static int __init thp_shrinker_init(void)
> shrinker_register(deferred_split_shrinker);
>
> if (IS_ENABLED(CONFIG_PERSISTENT_HUGE_ZERO_FOLIO)) {
> + unsigned long addr;
> +
> /*
> * Bump the reference of the huge_zero_folio and do not
> * initialize the shrinker.
> @@ -940,8 +944,22 @@ 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;
> + }
> +
> + /* Highmem folios have no permanent direct-map mapping to protect. */
> + if (folio_test_highmem(huge_zero_folio))
> + return 0;
> +
> + addr = (unsigned long)folio_address(huge_zero_folio);
> + /*
> + * The folio was zeroed through the writable direct map. Flush
> + * after the page-table update to invalidate stale translations.
> + */
> + set_direct_map_ro_noflush((void *)addr, HPAGE_PMD_NR);
> + flush_tlb_kernel_range(addr, addr + HPAGE_PMD_SIZE);
> return 0;
> }
>
The code changed upstream in the meantime. There is now the huge_zero_init() we
can hook into instead.
--
Cheers,
David
next prev parent reply other threads:[~2026-08-25 15:51 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-30 9:06 [PATCH v6 0/3] mm: make persistent huge zero folio read-only Xueyuan Chen
2026-07-30 9:06 ` [PATCH v6 1/3] " Xueyuan Chen
2026-08-25 15:51 ` David Hildenbrand (Arm) [this message]
2026-08-25 16:29 ` Dave Hansen
2026-07-30 9:06 ` [PATCH v6 2/3] arm64/mm: add set_direct_map_ro_noflush() Xueyuan Chen
2026-08-25 15:52 ` David Hildenbrand (Arm)
2026-08-25 16:44 ` Will Deacon
2026-08-25 16:46 ` David Hildenbrand (Arm)
2026-08-26 7:53 ` Xueyuan Chen
2026-07-30 9:06 ` [PATCH v6 3/3] x86/mm: " Xueyuan Chen
2026-08-25 15:52 ` David Hildenbrand (Arm)
2026-08-25 16:18 ` Dave Hansen
2026-08-25 16:43 ` David Hildenbrand (Arm)
2026-08-25 16:57 ` Dave Hansen
2026-08-25 17:30 ` David Hildenbrand (Arm)
2026-08-26 12:19 ` Xueyuan Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=fd78d494-7f47-4cf8-b66e-c95c2e3ed889@kernel.org \
--to=david@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=bp@alien8.de \
--cc=catalin.marinas@arm.com \
--cc=dave.hansen@linux.intel.com \
--cc=hpa@zytor.com \
--cc=lance.yang@linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mingo@redhat.com \
--cc=rppt@kernel.org \
--cc=ryan.roberts@arm.com \
--cc=tglx@kernel.org \
--cc=usama.arif@linux.dev \
--cc=will@kernel.org \
--cc=x86@kernel.org \
--cc=xueyuan.chen21@gmail.com \
--cc=ziy@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.