aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Smalley <sds@tycho.nsa.gov>2015-10-05 12:55:20 -0400
committerIngo Molnar <mingo@kernel.org>2015-10-06 05:11:48 -0400
commite1a58320a38dfa72be48a0f1a3a92273663ba6db (patch)
tree1ff867a83c601546e0910c009c96f4e1c1cd43b6
parent38a413cbc2b2834683b21823d964bc2d2f0abb82 (diff)
x86/mm: Warn on W^X mappings
Warn on any residual W+X mappings after setting NX if DEBUG_WX is enabled. Introduce a separate X86_PTDUMP_CORE config that enables the code for dumping the page tables without enabling the debugfs interface, so that DEBUG_WX can be enabled without exposing the debugfs interface. Switch EFI_PGT_DUMP to using X86_PTDUMP_CORE so that it also does not require enabling the debugfs interface. On success it prints this to the kernel log: x86/mm: Checked W+X mappings: passed, no W+X pages found. On failure it prints a warning and a count of the failed pages: ------------[ cut here ]------------ WARNING: CPU: 1 PID: 1 at arch/x86/mm/dump_pagetables.c:226 note_page+0x610/0x7b0() x86/mm: Found insecure W+X mapping at address ffffffff81755000/__stop___ex_table+0xfa8/0xabfa8 [...] Call Trace: [<ffffffff81380a5f>] dump_stack+0x44/0x55 [<ffffffff8109d3f2>] warn_slowpath_common+0x82/0xc0 [<ffffffff8109d48c>] warn_slowpath_fmt+0x5c/0x80 [<ffffffff8106cfc9>] ? note_page+0x5c9/0x7b0 [<ffffffff8106d010>] note_page+0x610/0x7b0 [<ffffffff8106d409>] ptdump_walk_pgd_level_core+0x259/0x3c0 [<ffffffff8106d5a7>] ptdump_walk_pgd_level_checkwx+0x17/0x20 [<ffffffff81063905>] mark_rodata_ro+0xf5/0x100 [<ffffffff817415a0>] ? rest_init+0x80/0x80 [<ffffffff817415bd>] kernel_init+0x1d/0xe0 [<ffffffff8174cd1f>] ret_from_fork+0x3f/0x70 [<ffffffff817415a0>] ? rest_init+0x80/0x80 ---[ end trace a1f23a1e42a2ac76 ]--- x86/mm: Checked W+X mappings: FAILED, 171 W+X pages found. Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov> Acked-by: Kees Cook <keescook@chromium.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Arjan van de Ven <arjan@linux.intel.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-kernel@vger.kernel.org Link: http://lkml.kernel.org/r/1444064120-11450-1-git-send-email-sds@tycho.nsa.gov [ Improved the Kconfig help text and made the new option default-y if CONFIG_DEBUG_RODATA=y, because it already found buggy mappings, so we really want people to have this on by default. ] Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/Kconfig.debug36
-rw-r--r--arch/x86/include/asm/pgtable.h7
-rw-r--r--arch/x86/mm/Makefile2
-rw-r--r--arch/x86/mm/dump_pagetables.c42
-rw-r--r--arch/x86/mm/init_32.c2
-rw-r--r--arch/x86/mm/init_64.c2
6 files changed, 88 insertions, 3 deletions
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index d8c0d3266173..3e0baf726eef 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -65,10 +65,14 @@ config EARLY_PRINTK_EFI
65 This is useful for kernel debugging when your machine crashes very 65 This is useful for kernel debugging when your machine crashes very
66 early before the console code is initialized. 66 early before the console code is initialized.
67 67
68config X86_PTDUMP_CORE
69 def_bool n
70
68config X86_PTDUMP 71config X86_PTDUMP
69 bool "Export kernel pagetable layout to userspace via debugfs" 72 bool "Export kernel pagetable layout to userspace via debugfs"
70 depends on DEBUG_KERNEL 73 depends on DEBUG_KERNEL
71 select DEBUG_FS 74 select DEBUG_FS
75 select X86_PTDUMP_CORE
72 ---help--- 76 ---help---
73 Say Y here if you want to show the kernel pagetable layout in a 77 Say Y here if you want to show the kernel pagetable layout in a
74 debugfs file. This information is only useful for kernel developers 78 debugfs file. This information is only useful for kernel developers
@@ -79,7 +83,8 @@ config X86_PTDUMP
79 83
80config EFI_PGT_DUMP 84config EFI_PGT_DUMP
81 bool "Dump the EFI pagetable" 85 bool "Dump the EFI pagetable"
82 depends on EFI && X86_PTDUMP 86 depends on EFI
87 select X86_PTDUMP_CORE
83 ---help--- 88 ---help---
84 Enable this if you want to dump the EFI page table before 89 Enable this if you want to dump the EFI page table before
85 enabling virtual mode. This can be used to debug miscellaneous 90 enabling virtual mode. This can be used to debug miscellaneous
@@ -105,6 +110,35 @@ config DEBUG_RODATA_TEST
105 feature as well as for the change_page_attr() infrastructure. 110 feature as well as for the change_page_attr() infrastructure.
106 If in doubt, say "N" 111 If in doubt, say "N"
107 112
113config DEBUG_WX
114 bool "Warn on W+X mappings at boot"
115 depends on DEBUG_RODATA
116 default y
117 select X86_PTDUMP_CORE
118 ---help---
119 Generate a warning if any W+X mappings are found at boot.
120
121 This is useful for discovering cases where the kernel is leaving
122 W+X mappings after applying NX, as such mappings are a security risk.
123
124 Look for a message in dmesg output like this:
125
126 x86/mm: Checked W+X mappings: passed, no W+X pages found.
127
128 or like this, if the check failed:
129
130 x86/mm: Checked W+X mappings: FAILED, <N> W+X pages found.
131
132 Note that even if the check fails, your kernel is possibly
133 still fine, as W+X mappings are not a security hole in
134 themselves, what they do is that they make the exploitation
135 of other unfixed kernel bugs easier.
136
137 There is no runtime or memory usage effect of this option
138 once the kernel has booted up - it's a one time check.
139
140 If in doubt, say "Y".
141
108config DEBUG_SET_MODULE_RONX 142config DEBUG_SET_MODULE_RONX
109 bool "Set loadable kernel module data as NX and text as RO" 143 bool "Set loadable kernel module data as NX and text as RO"
110 depends on MODULES 144 depends on MODULES
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 59fc3414c68b..c0b41f111a9a 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -19,6 +19,13 @@
19#include <asm/x86_init.h> 19#include <asm/x86_init.h>
20 20
21void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd); 21void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd);
22void ptdump_walk_pgd_level_checkwx(void);
23
24#ifdef CONFIG_DEBUG_WX
25#define debug_checkwx() ptdump_walk_pgd_level_checkwx()
26#else
27#define debug_checkwx() do { } while (0)
28#endif
22 29
23/* 30/*
24 * ZERO_PAGE is a global shared page that is always zero: used 31 * ZERO_PAGE is a global shared page that is always zero: used
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile
index a482d105172b..65c47fda26fc 100644
--- a/arch/x86/mm/Makefile
+++ b/arch/x86/mm/Makefile
@@ -14,7 +14,7 @@ obj-$(CONFIG_SMP) += tlb.o
14obj-$(CONFIG_X86_32) += pgtable_32.o iomap_32.o 14obj-$(CONFIG_X86_32) += pgtable_32.o iomap_32.o
15 15
16obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o 16obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
17obj-$(CONFIG_X86_PTDUMP) += dump_pagetables.o 17obj-$(CONFIG_X86_PTDUMP_CORE) += dump_pagetables.o
18 18
19obj-$(CONFIG_HIGHMEM) += highmem_32.o 19obj-$(CONFIG_HIGHMEM) += highmem_32.o
20 20
diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
index 71ab2d741024..1bf417e9cc13 100644
--- a/arch/x86/mm/dump_pagetables.c
+++ b/arch/x86/mm/dump_pagetables.c
@@ -32,6 +32,8 @@ struct pg_state {
32 const struct addr_marker *marker; 32 const struct addr_marker *marker;
33 unsigned long lines; 33 unsigned long lines;
34 bool to_dmesg; 34 bool to_dmesg;
35 bool check_wx;
36 unsigned long wx_pages;
35}; 37};
36 38
37struct addr_marker { 39struct addr_marker {
@@ -214,6 +216,16 @@ static void note_page(struct seq_file *m, struct pg_state *st,
214 const char *unit = units; 216 const char *unit = units;
215 unsigned long delta; 217 unsigned long delta;
216 int width = sizeof(unsigned long) * 2; 218 int width = sizeof(unsigned long) * 2;
219 pgprotval_t pr = pgprot_val(st->current_prot);
220
221 if (st->check_wx && (pr & _PAGE_RW) && !(pr & _PAGE_NX)) {
222 WARN_ONCE(1,
223 "x86/mm: Found insecure W+X mapping at address %p/%pS\n",
224 (void *)st->start_address,
225 (void *)st->start_address);
226 st->wx_pages += (st->current_address -
227 st->start_address) / PAGE_SIZE;
228 }
217 229
218 /* 230 /*
219 * Now print the actual finished series 231 * Now print the actual finished series
@@ -346,7 +358,8 @@ static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
346#define pgd_none(a) pud_none(__pud(pgd_val(a))) 358#define pgd_none(a) pud_none(__pud(pgd_val(a)))
347#endif 359#endif
348 360
349void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd) 361static void ptdump_walk_pgd_level_core(struct seq_file *m, pgd_t *pgd,
362 bool checkwx)
350{ 363{
351#ifdef CONFIG_X86_64 364#ifdef CONFIG_X86_64
352 pgd_t *start = (pgd_t *) &init_level4_pgt; 365 pgd_t *start = (pgd_t *) &init_level4_pgt;
@@ -362,6 +375,10 @@ void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
362 st.to_dmesg = true; 375 st.to_dmesg = true;
363 } 376 }
364 377
378 st.check_wx = checkwx;
379 if (checkwx)
380 st.wx_pages = 0;
381
365 for (i = 0; i < PTRS_PER_PGD; i++) { 382 for (i = 0; i < PTRS_PER_PGD; i++) {
366 st.current_address = normalize_addr(i * PGD_LEVEL_MULT); 383 st.current_address = normalize_addr(i * PGD_LEVEL_MULT);
367 if (!pgd_none(*start)) { 384 if (!pgd_none(*start)) {
@@ -381,8 +398,26 @@ void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
381 /* Flush out the last page */ 398 /* Flush out the last page */
382 st.current_address = normalize_addr(PTRS_PER_PGD*PGD_LEVEL_MULT); 399 st.current_address = normalize_addr(PTRS_PER_PGD*PGD_LEVEL_MULT);
383 note_page(m, &st, __pgprot(0), 0); 400 note_page(m, &st, __pgprot(0), 0);
401 if (!checkwx)
402 return;
403 if (st.wx_pages)
404 pr_info("x86/mm: Checked W+X mappings: FAILED, %lu W+X pages found.\n",
405 st.wx_pages);
406 else
407 pr_info("x86/mm: Checked W+X mappings: passed, no W+X pages found.\n");
408}
409
410void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
411{
412 ptdump_walk_pgd_level_core(m, pgd, false);
384} 413}
385 414
415void ptdump_walk_pgd_level_checkwx(void)
416{
417 ptdump_walk_pgd_level_core(NULL, NULL, true);
418}
419
420#ifdef CONFIG_X86_PTDUMP
386static int ptdump_show(struct seq_file *m, void *v) 421static int ptdump_show(struct seq_file *m, void *v)
387{ 422{
388 ptdump_walk_pgd_level(m, NULL); 423 ptdump_walk_pgd_level(m, NULL);
@@ -400,10 +435,13 @@ static const struct file_operations ptdump_fops = {
400 .llseek = seq_lseek, 435 .llseek = seq_lseek,
401 .release = single_release, 436 .release = single_release,
402}; 437};
438#endif
403 439
404static int pt_dump_init(void) 440static int pt_dump_init(void)
405{ 441{
442#ifdef CONFIG_X86_PTDUMP
406 struct dentry *pe; 443 struct dentry *pe;
444#endif
407 445
408#ifdef CONFIG_X86_32 446#ifdef CONFIG_X86_32
409 /* Not a compile-time constant on x86-32 */ 447 /* Not a compile-time constant on x86-32 */
@@ -415,10 +453,12 @@ static int pt_dump_init(void)
415 address_markers[FIXADDR_START_NR].start_address = FIXADDR_START; 453 address_markers[FIXADDR_START_NR].start_address = FIXADDR_START;
416#endif 454#endif
417 455
456#ifdef CONFIG_X86_PTDUMP
418 pe = debugfs_create_file("kernel_page_tables", 0600, NULL, NULL, 457 pe = debugfs_create_file("kernel_page_tables", 0600, NULL, NULL,
419 &ptdump_fops); 458 &ptdump_fops);
420 if (!pe) 459 if (!pe)
421 return -ENOMEM; 460 return -ENOMEM;
461#endif
422 462
423 return 0; 463 return 0;
424} 464}
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index 7562f42914b4..cb4ef3de61f9 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -957,6 +957,8 @@ void mark_rodata_ro(void)
957 set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); 957 set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
958#endif 958#endif
959 mark_nxdata_nx(); 959 mark_nxdata_nx();
960 if (__supported_pte_mask & _PAGE_NX)
961 debug_checkwx();
960} 962}
961#endif 963#endif
962 964
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 30564e2752d3..f8b157366700 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -1150,6 +1150,8 @@ void mark_rodata_ro(void)
1150 free_init_pages("unused kernel", 1150 free_init_pages("unused kernel",
1151 (unsigned long) __va(__pa_symbol(rodata_end)), 1151 (unsigned long) __va(__pa_symbol(rodata_end)),
1152 (unsigned long) __va(__pa_symbol(_sdata))); 1152 (unsigned long) __va(__pa_symbol(_sdata)));
1153
1154 debug_checkwx();
1153} 1155}
1154 1156
1155#endif 1157#endif