From: <li.hanting@zte.com.cn>
To: <palmer@dabbelt.com>, <pjw@kernel.org>, <aou@eecs.berkeley.edu>,
<alex@ghiti.fr>
Cc: <linux-riscv@lists.infradead.org>, <linux-kernel@vger.kernel.org>,
<linux-mm@kvack.org>, <akpm@linux-foundation.org>,
<ljs@kernel.org>, <rppt@kernel.org>
Subject: [RFC PATCH] riscv: mm: allocate split linear mapping page tables via the standard page table allocation APIs
Date: Mon, 9 Nov 2026 16:14:38 +0800 (CST) [thread overview]
Message-ID: <202611091101.6A9B1Nf3015337@mse-db.zte.com.cn> (raw)
Hi,
Commit 9e4a3ec3411b ("x86/mm/pat: Allocate split page tables as kernel
page tables") recently fixed a latent inconsistency in the x86 CPA code
(assigned CVE-2026-97525): __split_large_page() allocated the new PTE
page with a bare pagetable_alloc(), bypassing pte_alloc_one_kernel(), so
the page table constructor never ran and the resulting page was neither
flagged PG_table, nor accounted in NR_PAGETABLE, nor marked as a kernel
page table.
While looking at the fallout of that commit, I noticed that RISC-V
open-codes the same pattern when splitting the linear mapping on behalf
of set_memory_*(): __split_linear_mapping_{pmd,pud,p4d}() allocate the
new page table pages with bare alloc_page(GFP_KERNEL) - three call
sites in arch/riscv/mm/pageattr.c, introduced by 311cd2f6e253 ("riscv:
Fix set_memory_XX() and set_direct_map_XX() by splitting huge linear
mappings").
The consequences are the same as on pre-fix x86: page tables that live
in init_mm without the PG_table flag, without NR_PAGETABLE accounting
and without the kernel ptdesc marking.
To be clear about severity: this is a latent inconsistency, not an
exploitable bug today. RISC-V never collapses the split linear mapping
back, and these page tables are never freed, so there is no free-path
mismatch of the kind the x86 fix was concerned about. But the
accounting mismatch is real (/proc/vmstat under-counts kernel page
tables), and any future work that frees these tables - linear map
memory hotplug, collapse support or anything similar - would inherit
the exact bug class the x86 fix addressed.
This RFC aligns the three sites with the standard allocation paths:
- the PTE level switches to pte_alloc_one_kernel(), exactly like the
x86 fix;
- the PMD and PUD levels have no *_alloc_one_kernel() variant today,
so a small local helper wraps pagetable_alloc() +
__pagetable_ctor() + ptdesc_set_kernel(), mirroring what the
generic PTE path does. GFP_PGTABLE_KERNEL also brings in
__GFP_ZERO, which the open-coded allocation was missing.
No functional change is intended; the observable difference is that
split linear mapping page tables now show up in
/proc/vmstat:nr_page_table_pages.
Open questions:
1. Would it make sense to provide generic pmd_alloc_one_kernel() /
pud_alloc_one_kernel() variants in asm-generic/pgalloc.h so that
architectures do not have to open-code this dance? This is the
main reason this is an RFC rather than a regular patch.
2. Since these tables are never freed today, is it preferable to take
this alignment now (my preference: it removes a trap for any
future free-path work), or defer it until such work actually
lands?
(Note: the generic CPA rework series "[PATCH RFC 00/18] arch, mm:
create a generic set_memory/change_page_attr core" would eventually
replace this code entirely; this patch is intended as a small fix in
the meantime. The series author has said he is waiting for such
fixes to settle before posting a non-RFC version.)
Comments welcome.
Signed-off-by: Hanting Li <li.hanting@zte.com.cn>
---
arch/riscv/mm/pageattr.c | 65 ++++++++++++++++++++++++++++-------------
1 file changed, 44 insertions(+), 21 deletions(-)
diff --git a/arch/riscv/mm/pageattr.c b/arch/riscv/mm/pageattr.c
--- a/arch/riscv/mm/pageattr.c
+++ b/arch/riscv/mm/pageattr.c
@@ -3,9 +3,11 @@
* Copyright (C) 2019 SiFive
*/
+#include <linux/mm.h>
#include <linux/pagewalk.h>
#include <linux/pgtable.h>
#include <linux/vmalloc.h>
+#include <asm/pgalloc.h>
#include <asm/tlbflush.h>
#include <asm/bitops.h>
#include <asm/set_memory.h>
@@ -93,6 +95,26 @@
};
#ifdef CONFIG_64BIT
+/*
+ * Kernel page tables allocated for the split linear mapping are never
+ * freed today, but they must still go through the page table constructor
+ * so that they are flagged PG_table, accounted in NR_PAGETABLE and marked
+ * as kernel page tables, like any other kernel page table allocated via
+ * the standard allocation APIs.
+ */
+static struct ptdesc *alloc_kernel_pgtable(void)
+{
+ struct ptdesc *ptdesc = pagetable_alloc(GFP_PGTABLE_KERNEL, 0);
+
+ if (!ptdesc)
+ return NULL;
+
+ __pagetable_ctor(ptdesc);
+ ptdesc_set_kernel(ptdesc);
+
+ return ptdesc;
+}
+
static int __split_linear_mapping_pmd(pud_t *pudp,
unsigned long vaddr, unsigned long end)
{
@@ -109,23 +131,22 @@
continue;
if (pmd_leaf(pmdp_get(pmdp))) {
- struct page *pte_page;
unsigned long pfn = _pmd_pfn(pmdp_get(pmdp));
pgprot_t prot = __pgprot(pmd_val(pmdp_get(pmdp)) & ~_PAGE_PFN_MASK);
pte_t *ptep_new;
int i;
- pte_page = alloc_page(GFP_KERNEL);
- if (!pte_page)
+ ptep_new = pte_alloc_one_kernel(&init_mm);
+ if (!ptep_new)
return -ENOMEM;
- ptep_new = (pte_t *)page_address(pte_page);
- for (i = 0; i < PTRS_PER_PTE; ++i, ++ptep_new)
- set_pte(ptep_new, pfn_pte(pfn + i, prot));
+ for (i = 0; i < PTRS_PER_PTE; ++i)
+ set_pte(ptep_new + i, pfn_pte(pfn + i, prot));
smp_wmb();
- set_pmd(pmdp, pfn_pmd(page_to_pfn(pte_page), PAGE_TABLE));
+ set_pmd(pmdp, pfn_pmd(page_to_pfn(virt_to_page(ptep_new)),
+ PAGE_TABLE));
}
} while (pmdp++, vaddr = next, vaddr != end);
@@ -149,24 +170,25 @@
continue;
if (pud_leaf(pudp_get(pudp))) {
- struct page *pmd_page;
+ struct ptdesc *ptdesc;
unsigned long pfn = _pud_pfn(pudp_get(pudp));
pgprot_t prot = __pgprot(pud_val(pudp_get(pudp)) & ~_PAGE_PFN_MASK);
pmd_t *pmdp_new;
int i;
- pmd_page = alloc_page(GFP_KERNEL);
- if (!pmd_page)
+ ptdesc = alloc_kernel_pgtable();
+ if (!ptdesc)
return -ENOMEM;
- pmdp_new = (pmd_t *)page_address(pmd_page);
- for (i = 0; i < PTRS_PER_PMD; ++i, ++pmdp_new)
- set_pmd(pmdp_new,
+ pmdp_new = (pmd_t *)ptdesc_address(ptdesc);
+ for (i = 0; i < PTRS_PER_PMD; ++i)
+ set_pmd(pmdp_new + i,
pfn_pmd(pfn + ((i * PMD_SIZE) >> PAGE_SHIFT), prot));
smp_wmb();
- set_pud(pudp, pfn_pud(page_to_pfn(pmd_page), PAGE_TABLE));
+ set_pud(pudp, pfn_pud(page_to_pfn(ptdesc_page(ptdesc)),
+ PAGE_TABLE));
}
ret = __split_linear_mapping_pmd(pudp, vaddr, next);
@@ -198,23 +220,23 @@
continue;
if (p4d_leaf(p4dp_get(p4dp))) {
- struct page *pud_page;
+ struct ptdesc *ptdesc;
unsigned long pfn = _p4d_pfn(p4dp_get(p4dp));
pgprot_t prot = __pgprot(p4d_val(p4dp_get(p4dp)) & ~_PAGE_PFN_MASK);
pud_t *pudp_new;
int i;
- pud_page = alloc_page(GFP_KERNEL);
- if (!pud_page)
+ ptdesc = alloc_kernel_pgtable();
+ if (!ptdesc)
return -ENOMEM;
/*
* Fill the pud level with leaf puds that have the same
* protections as the leaf p4d.
*/
- pudp_new = (pud_t *)page_address(pud_page);
- for (i = 0; i < PTRS_PER_PUD; ++i, ++pudp_new)
- set_pud(pudp_new,
+ pudp_new = (pud_t *)ptdesc_address(ptdesc);
+ for (i = 0; i < PTRS_PER_PUD; ++i)
+ set_pud(pudp_new + i,
pfn_pud(pfn + ((i * PUD_SIZE) >> PAGE_SHIFT), prot));
/*
@@ -224,7 +246,8 @@
*/
smp_wmb();
- set_p4d(p4dp, pfn_p4d(page_to_pfn(pud_page), PAGE_TABLE));
+ set_p4d(p4dp, pfn_p4d(page_to_pfn(ptdesc_page(ptdesc)),
+ PAGE_TABLE));
}
ret = __split_linear_mapping_pud(p4dp, vaddr, next);
reply other threads:[~2026-11-09 11:01 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=202611091101.6A9B1Nf3015337@mse-db.zte.com.cn \
--to=li.hanting@zte.com.cn \
--cc=akpm@linux-foundation.org \
--cc=alex@ghiti.fr \
--cc=aou@eecs.berkeley.edu \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-riscv@lists.infradead.org \
--cc=ljs@kernel.org \
--cc=palmer@dabbelt.com \
--cc=pjw@kernel.org \
--cc=rppt@kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®