[PATCH RFC v3 19/21] mm/pgtable: disallow calling (pgd|p4d|pud)_page, pgd_page_vaddr() and (p4d|pud)_pgtable with dummy

From: Yeoreum Yun

Date: Wed Sep 02 2026 - 08:29:35 EST


From: "David Hildenbrand (Arm)" <david@xxxxxxxxxx>

These helpers silently fallback to the folded helpers. With e.g., pdgp_get()
returning dummy values, pgd_page_vaddr(pgdp_get(pgdp)) will now behave
differently than pgd_page_vaddr(*pgdp).

Disallow calling (pgd|p4d|pud)_page() entirely, and catch when passing a
dummy value to pgd_page_vaddr() and (p4d|pud)_pgtable.
This will catch most abuse of dummy pXdp value at compile time --
so far there were none, and it better stay that way.

Signed-off-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
Co-developed-by: Yeoreum Yun <yeoreum.yun@xxxxxxx>
Signed-off-by: Yeoreum Yun <yeoreum.yun@xxxxxxx>
---
include/asm-generic/pgtable-nop4d.h | 8 ++++++--
include/asm-generic/pgtable-nopmd.h | 8 ++++++--
include/asm-generic/pgtable-nopud.h | 8 ++++++--
3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/include/asm-generic/pgtable-nop4d.h b/include/asm-generic/pgtable-nop4d.h
index 2d86325235da..d2dccf7542da 100644
--- a/include/asm-generic/pgtable-nop4d.h
+++ b/include/asm-generic/pgtable-nop4d.h
@@ -70,8 +70,12 @@ static __always_inline p4d_t *__p4d_offset_lockless(pgd_t *pgdp, pgd_t pgd,
#define p4d_val(x) (pgd_val((x).pgd))
#define __p4d(x) ((p4d_t) { __pgd(x) })

-#define pgd_page(pgd) (p4d_page((p4d_t){ pgd }))
-#define pgd_page_vaddr(pgd) ((unsigned long)(p4d_pgtable((p4d_t){ pgd })))
+#define pgd_page(pgd) ({ BUILD_BUG(); (struct page *)NULL; })
+#define pgd_page_vaddr(pgd) \
+({ \
+ pgd_check_dummy(pgd); \
+ (unsigned long)p4d_pgtable((p4d_t) { pgd }); \
+})

/*
* allocating and freeing a p4d is trivial: the 1-entry p4d is
diff --git a/include/asm-generic/pgtable-nopmd.h b/include/asm-generic/pgtable-nopmd.h
index bd40d3a5743f..2afbf2d8659d 100644
--- a/include/asm-generic/pgtable-nopmd.h
+++ b/include/asm-generic/pgtable-nopmd.h
@@ -79,8 +79,12 @@ static __always_inline pmd_t *__pmd_offset_lockless(pud_t *pudp, pud_t pud,
#define pmd_val(x) (pud_val((x).pud))
#define __pmd(x) ((pmd_t) { __pud(x) } )

-#define pud_page(pud) (pmd_page((pmd_t){ pud }))
-#define pud_pgtable(pud) ((pmd_t *)(pmd_page_vaddr((pmd_t){ pud })))
+#define pud_page(pud) ({ BUILD_BUG(); (struct page *)NULL; })
+#define pud_pgtable(pud) \
+({ \
+ pud_check_dummy(pud); \
+ ((pmd_t *)(pmd_page_vaddr((pmd_t) { pud }))); \
+})

/*
* allocating and freeing a pmd is trivial: the 1-entry pmd is
diff --git a/include/asm-generic/pgtable-nopud.h b/include/asm-generic/pgtable-nopud.h
index 7475d0bd0f2e..3264673c0c38 100644
--- a/include/asm-generic/pgtable-nopud.h
+++ b/include/asm-generic/pgtable-nopud.h
@@ -77,8 +77,12 @@ static __always_inline pud_t *__pud_offset_lockless(p4d_t *p4dp, p4d_t p4d,
#define pud_val(x) (p4d_val((x).p4d))
#define __pud(x) ((pud_t) { __p4d(x) })

-#define p4d_page(p4d) (pud_page((pud_t){ p4d }))
-#define p4d_pgtable(p4d) ((pud_t *)(pud_pgtable((pud_t){ p4d })))
+#define p4d_page(p4d) ({ BUILD_BUG(); (struct page *)NULL; })
+#define p4d_pgtable(p4d) \
+({ \
+ p4d_check_dummy(p4d); \
+ ((pud_t *)(pud_pgtable((pud_t) { p4d }))); \
+})

/*
* allocating and freeing a pud is trivial: the 1-entry pud is

--
2.43.0