From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pf1-f172.google.com (mail-pf1-f172.google.com [209.85.210.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4C6973EF678 for ; Thu, 30 Jul 2026 09:07:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.172 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785402427; cv=none; b=rx9jlsWekUJrbdOReChP324v2GPPlxusJn2cHgBSwlXTmb3MYDUuxK4C2NoffSNl1WnraLWq/11boN3uWLhFgPtyTAhhQq97M7kXSVQp7Q9O9RrKK3/cw//xqMGFg/AIIMBO2QPr/JgJ8mgdfMQw2D46Kyx5eBckIcmRk5ZwmOY= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785402427; c=relaxed/simple; bh=0h6HC0KAzOmkK235lQC12eChc//TPk+wqTUx+k1DVzk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fWnG9eSpx2N044G3KQdTIo7ADO5wHklEtxXi6sXhU/5Z5+oCtGoKOfuFqmTsn5l0kZhqtMo9mzupshvcg1HgUY19S4UeU413JfE91rbzRf+o7GAOiq4/rdKebVLZ6ZYvYD1MrcS9yFQAj7CtRjHXfD/2cKDl45kVEL1iecQ65/g= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=j2yFyD50; arc=none smtp.client-ip=209.85.210.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="j2yFyD50" Received: by mail-pf1-f172.google.com with SMTP id d2e1a72fcca58-84e4e98d2cfso195735b3a.0 for ; Thu, 30 Jul 2026 02:07:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785402426; x=1786007226; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=u00RxS2OEU/Jkrk6J/93Lztn6eTwvbQr7TbXJRpbv68=; b=j2yFyD501boL1G9q141I5M2fo4zOwzxju2WDb8W+YSAbemA9DasoDAgqvtTEWZmcqJ s2K8DofIh1COfvXXLWbfqckNw2yDxiCyfcEm1Ui8uDEsV8kVNPgQU6LDnogKoj2rgLZZ 1nQDktm7hQ2G33Bx4AO0VpzaI/PPlLHKd51mjYG4yRw1FUGb6EG0Kyy40mTnBjdZ7PcL 9eYlUBr3rHXzMVhZiF/z4g5HLffC2PsuTRLlKzBh+3tzXt5uuR52r9uXyerSkcI4mUYC KSiJNEWfAyxeSH+/D5zACe4Cv71WZMagSUtVUObRLWDkcJEQY1XJD1nLlE7H7Z9wDO/7 4FnQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785402426; x=1786007226; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=u00RxS2OEU/Jkrk6J/93Lztn6eTwvbQr7TbXJRpbv68=; b=KmcnT3tOz1mOpmyDxqgzq4wn10WvSv6Q1IdxLves2S5fcmUefhN28EYJ2DInl4ChRt XaKn0so2kd8lZX5ogsTX5DDB5o+/xhHq2wIWBSnL2B4tu+ywqAji8x9zMUn/hGjLwDwa w4MDhRswGxANMjuy8jTXN/IjA+EmggOF1+l9Rxim67OYoC4Od2S32kOFRM18381NV1uz m/5Wcmg1gXuidhyZiBu47gO3W3Zvv9A4ZHN9rRXBc/svtzpe2NylpTU32PLfDRYrDYsy 6+22nmO/Br5RfnyXa6qWx1NOFA5oJwJC4tguUtYNTdkdNkwLoXC3mMqgkpMwMxItPOrI G8Mg== X-Forwarded-Encrypted: i=1; AHgh+RoziHoXcwwaUfkx+vultBVlUInNRvl3mblmGASbwkwL6wE/XFk8UmwjNKYXke7jkW+kcuMmtS2EddEUHo8=@vger.kernel.org X-Gm-Message-State: AOJu0YwroKNtbaGUo2qxu4SweRVfUu4sFTdnF0p3ki5b6f1UwbVa4Ycc q3/aP6PUdQ1vVXnGFsyRLqd4OwycqxFjzvH64wfAmCnmo+nbxYg4Vn1m X-Gm-Gg: AR+sD11frt5aA7FMDCvXUF5ja6giqEwgbkn9ah628NoOElV4YSQEjpAmMcGJRAOkY3B BxN1PFgWio5XxOdHgdhhtM7u9MjAzaHtqk7ZWNdtmefH/P/jhIfN5WLfrT7FEaJd/s38ZYF9Kq1 tJ+7ItH2R+8AxP0bKxXws9fcWyqQclz6nNupC00KrOwF2SLpKsiplNJHvF9oUqCly62jaxVJXFS xepiVWZ3x8gIfjKNYIKAjHo/ux3l5HYC+qCyv5PpxN3T/BQaJyEqt3L6xfgjHyn0rO/f/zClpE0 1CdAWa/ZS73cO8SukoSPgd1aarnliZyxS6SWLZU3C/iBJ++ohSqRe+DnZwJvjuJkPvWcTdKHr+N +dZ4j0058YR6TGffLjGvPhXau2/dCgiDQx8iNdA89nxlVlbGG7+MPE1nr+MWGRYPclWHw5iKkz4 I2mcgHu038IWmBwiP34hmrH6C9M6zeYRw0Gfl7u87OtDXT9hHHG+UkLgRwAY1u59hM1ifjdPAFL lPh1jFxcMzM4hptaW9DJIjBkamsVMtFvi6aMzKbAqTHDjmG2qvqiTR7f0L6RISN X-Received: by 2002:a05:6a20:7f81:b0:3c3:2dc0:7cbd with SMTP id adf61e73a8af0-3c900541df0mr2314097637.1.1785402425540; Thu, 30 Jul 2026 02:07:05 -0700 (PDT) Received: from debian.lan ([155.117.85.23]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-cbdba447107sm1902510a12.21.2026.07.30.02.06.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 02:07:04 -0700 (PDT) From: Xueyuan Chen To: akpm@linux-foundation.org Cc: david@kernel.org, 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, Xueyuan Chen , Lance Yang Subject: [PATCH v6 1/3] mm: make persistent huge zero folio read-only Date: Thu, 30 Jul 2026 17:06:45 +0800 Message-ID: <20260730090647.2401252-2-xueyuan.chen21@gmail.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260730090647.2401252-1-xueyuan.chen21@gmail.com> References: <20260730090647.2401252-1-xueyuan.chen21@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 Suggested-by: Usama Arif Co-developed-by: Lance Yang Signed-off-by: Lance Yang Signed-off-by: Xueyuan Chen --- 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) +{ + return 0; +} + static inline bool kernel_page_present(struct page *page) { return true; @@ -56,6 +74,17 @@ static inline bool can_set_direct_map(void) } #define can_set_direct_map can_set_direct_map #endif + +#ifndef set_direct_map_ro_noflush +/* See the comment above the generic fallback for the _noflush contract. */ +static inline int set_direct_map_ro_noflush(const void *addr, + unsigned long nr_pages) +{ + return 0; +} + +#define set_direct_map_ro_noflush set_direct_map_ro_noflush +#endif #endif /* CONFIG_ARCH_HAS_SET_DIRECT_MAP */ #ifdef CONFIG_X86_64 diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 970e077019b7..2dfc7a4218c3 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -40,8 +40,10 @@ #include #include #include +#include #include +#include #include "internal.h" #include "swap.h" @@ -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; } -- 2.47.3