aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mm/ptdump
diff options
context:
space:
mode:
authorRussell Currey <ruscur@russell.cc>2019-05-02 03:39:47 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2019-05-02 12:54:45 -0400
commit453d87f6a8aed827f5ebb1708a4cea458fd68d23 (patch)
treebc0424487d4f512ca6313ac84b03568784b19965 /arch/powerpc/mm/ptdump
parent5f18cbdbdd42b050c51eb9859f8ce43db3f51846 (diff)
powerpc/mm: Warn if W+X pages found on boot
Implement code to walk all pages and warn if any are found to be both writable and executable. Depends on STRICT_KERNEL_RWX enabled, and is behind the DEBUG_WX config option. This only runs on boot and has no runtime performance implications. Very heavily influenced (and in some cases copied verbatim) from the ARM64 code written by Laura Abbott (thanks!), since our ptdump infrastructure is similar. Signed-off-by: Russell Currey <ruscur@russell.cc> [mpe: Fixup build error when disabled] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'arch/powerpc/mm/ptdump')
-rw-r--r--arch/powerpc/mm/ptdump/ptdump.c43
1 files changed, 42 insertions, 1 deletions
diff --git a/arch/powerpc/mm/ptdump/ptdump.c b/arch/powerpc/mm/ptdump/ptdump.c
index e249a56c07bf..646876d9da64 100644
--- a/arch/powerpc/mm/ptdump/ptdump.c
+++ b/arch/powerpc/mm/ptdump/ptdump.c
@@ -31,7 +31,7 @@
31#include "ptdump.h" 31#include "ptdump.h"
32 32
33#ifdef CONFIG_PPC32 33#ifdef CONFIG_PPC32
34#define KERN_VIRT_START 0 34#define KERN_VIRT_START PAGE_OFFSET
35#endif 35#endif
36 36
37/* 37/*
@@ -68,6 +68,8 @@ struct pg_state {
68 unsigned long last_pa; 68 unsigned long last_pa;
69 unsigned int level; 69 unsigned int level;
70 u64 current_flags; 70 u64 current_flags;
71 bool check_wx;
72 unsigned long wx_pages;
71}; 73};
72 74
73struct addr_marker { 75struct addr_marker {
@@ -181,6 +183,20 @@ static void dump_addr(struct pg_state *st, unsigned long addr)
181 183
182} 184}
183 185
186static void note_prot_wx(struct pg_state *st, unsigned long addr)
187{
188 if (!st->check_wx)
189 return;
190
191 if (!((st->current_flags & pgprot_val(PAGE_KERNEL_X)) == pgprot_val(PAGE_KERNEL_X)))
192 return;
193
194 WARN_ONCE(1, "powerpc/mm: Found insecure W+X mapping at address %p/%pS\n",
195 (void *)st->start_address, (void *)st->start_address);
196
197 st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
198}
199
184static void note_page(struct pg_state *st, unsigned long addr, 200static void note_page(struct pg_state *st, unsigned long addr,
185 unsigned int level, u64 val) 201 unsigned int level, u64 val)
186{ 202{
@@ -210,6 +226,7 @@ static void note_page(struct pg_state *st, unsigned long addr,
210 226
211 /* Check the PTE flags */ 227 /* Check the PTE flags */
212 if (st->current_flags) { 228 if (st->current_flags) {
229 note_prot_wx(st, addr);
213 dump_addr(st, addr); 230 dump_addr(st, addr);
214 231
215 /* Dump all the flags */ 232 /* Dump all the flags */
@@ -387,6 +404,30 @@ static void build_pgtable_complete_mask(void)
387 pg_level[i].mask |= pg_level[i].flag[j].mask; 404 pg_level[i].mask |= pg_level[i].flag[j].mask;
388} 405}
389 406
407#ifdef CONFIG_PPC_DEBUG_WX
408void ptdump_check_wx(void)
409{
410 struct pg_state st = {
411 .seq = NULL,
412 .marker = address_markers,
413 .check_wx = true,
414 };
415
416 if (radix_enabled())
417 st.start_address = PAGE_OFFSET;
418 else
419 st.start_address = KERN_VIRT_START;
420
421 walk_pagetables(&st);
422
423 if (st.wx_pages)
424 pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found\n",
425 st.wx_pages);
426 else
427 pr_info("Checked W+X mappings: passed, no W+X pages found\n");
428}
429#endif
430
390static int ptdump_init(void) 431static int ptdump_init(void)
391{ 432{
392 struct dentry *debugfs_file; 433 struct dentry *debugfs_file;