[PATCH 4/5 RESEND] arm64: dynamic enforcement of PXNTable

From: Maxwell Bland
Date: Tue Apr 16 2024 - 15:20:28 EST


PXNTable is enforced during the init process to ensure that regions of
user memory and kernel data cannot be executed from, preventing attacks
which write to writable kernel pages and then modify the kernel's page
tables to make this code executable. This patch ensures this protection
is also preserved for dynamically allocated pages/pagetables, making it
so that all PMDs populated outside of the module code region are
PXNTable by default.

Signed-off-by: Maxwell Bland <mbland@xxxxxxxxxxxx>
---
arch/arm64/include/asm/pgalloc.h | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h
index 5785272144e8..2376b4e7915c 100644
--- a/arch/arm64/include/asm/pgalloc.h
+++ b/arch/arm64/include/asm/pgalloc.h
@@ -12,6 +12,7 @@
#include <asm/processor.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
+#include <asm/module.h>

#define __HAVE_ARCH_PGD_FREE
#define __HAVE_ARCH_PUD_FREE
@@ -119,6 +120,12 @@ static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t ptep,
set_pmd(pmdp, __pmd(__phys_to_pmd_val(ptep) | prot));
}

+static inline bool vaddr_is_data(unsigned long vaddr)
+{
+ return ((vaddr + PMD_SIZE < MODULES_ASLR_START || vaddr >= MODULES_ASLR_END) &&
+ (vaddr + PMD_SIZE < (unsigned long) _text || vaddr >= (unsigned long) _etext));
+}
+
/*
* Populate the pmdp entry with a pointer to the pte. This pmd is part
* of the mm address space.
@@ -127,8 +134,11 @@ static inline void
pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep,
unsigned long vaddr)
{
+ pmdval_t pmd = PMD_TYPE_TABLE | PMD_TABLE_UXN;
VM_BUG_ON(mm && mm != &init_mm);
- __pmd_populate(pmdp, __pa(ptep), PMD_TYPE_TABLE | PMD_TABLE_UXN);
+ if (vaddr_is_data(vaddr))
+ pmd |= PMD_TABLE_PXN;
+ __pmd_populate(pmdp, __pa(ptep), pmd);
}

static inline void
--
2.39.2