Skip to content

Commit e1a5832

Browse files
stephensmalleyIngo Molnar
authored andcommitted
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>
1 parent 38a413c commit e1a5832

File tree

6 files changed

+88
-3
lines changed

6 files changed

+88
-3
lines changed

arch/x86/Kconfig.debug

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,14 @@ config EARLY_PRINTK_EFI
6565
This is useful for kernel debugging when your machine crashes very
6666
early before the console code is initialized.
6767

68+
config X86_PTDUMP_CORE
69+
def_bool n
70+
6871
config X86_PTDUMP
6972
bool "Export kernel pagetable layout to userspace via debugfs"
7073
depends on DEBUG_KERNEL
7174
select DEBUG_FS
75+
select X86_PTDUMP_CORE
7276
---help---
7377
Say Y here if you want to show the kernel pagetable layout in a
7478
debugfs file. This information is only useful for kernel developers
@@ -79,7 +83,8 @@ config X86_PTDUMP
7983

8084
config EFI_PGT_DUMP
8185
bool "Dump the EFI pagetable"
82-
depends on EFI && X86_PTDUMP
86+
depends on EFI
87+
select X86_PTDUMP_CORE
8388
---help---
8489
Enable this if you want to dump the EFI page table before
8590
enabling virtual mode. This can be used to debug miscellaneous
@@ -105,6 +110,35 @@ config DEBUG_RODATA_TEST
105110
feature as well as for the change_page_attr() infrastructure.
106111
If in doubt, say "N"
107112

113+
config 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+
108142
config DEBUG_SET_MODULE_RONX
109143
bool "Set loadable kernel module data as NX and text as RO"
110144
depends on MODULES

arch/x86/include/asm/pgtable.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919
#include <asm/x86_init.h>
2020

2121
void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd);
22+
void 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
2229

2330
/*
2431
* ZERO_PAGE is a global shared page that is always zero: used

arch/x86/mm/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ obj-$(CONFIG_SMP) += tlb.o
1414
obj-$(CONFIG_X86_32) += pgtable_32.o iomap_32.o
1515

1616
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
17-
obj-$(CONFIG_X86_PTDUMP) += dump_pagetables.o
17+
obj-$(CONFIG_X86_PTDUMP_CORE) += dump_pagetables.o
1818

1919
obj-$(CONFIG_HIGHMEM) += highmem_32.o
2020

arch/x86/mm/dump_pagetables.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ struct pg_state {
3232
const struct addr_marker *marker;
3333
unsigned long lines;
3434
bool to_dmesg;
35+
bool check_wx;
36+
unsigned long wx_pages;
3537
};
3638

3739
struct addr_marker {
@@ -214,6 +216,16 @@ static void note_page(struct seq_file *m, struct pg_state *st,
214216
const char *unit = units;
215217
unsigned long delta;
216218
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+
}
217229

218230
/*
219231
* 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,
346358
#define pgd_none(a) pud_none(__pud(pgd_val(a)))
347359
#endif
348360

349-
void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
361+
static void ptdump_walk_pgd_level_core(struct seq_file *m, pgd_t *pgd,
362+
bool checkwx)
350363
{
351364
#ifdef CONFIG_X86_64
352365
pgd_t *start = (pgd_t *) &init_level4_pgt;
@@ -362,6 +375,10 @@ void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
362375
st.to_dmesg = true;
363376
}
364377

378+
st.check_wx = checkwx;
379+
if (checkwx)
380+
st.wx_pages = 0;
381+
365382
for (i = 0; i < PTRS_PER_PGD; i++) {
366383
st.current_address = normalize_addr(i * PGD_LEVEL_MULT);
367384
if (!pgd_none(*start)) {
@@ -381,8 +398,26 @@ void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
381398
/* Flush out the last page */
382399
st.current_address = normalize_addr(PTRS_PER_PGD*PGD_LEVEL_MULT);
383400
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+
410+
void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
411+
{
412+
ptdump_walk_pgd_level_core(m, pgd, false);
384413
}
385414

415+
void ptdump_walk_pgd_level_checkwx(void)
416+
{
417+
ptdump_walk_pgd_level_core(NULL, NULL, true);
418+
}
419+
420+
#ifdef CONFIG_X86_PTDUMP
386421
static int ptdump_show(struct seq_file *m, void *v)
387422
{
388423
ptdump_walk_pgd_level(m, NULL);
@@ -400,10 +435,13 @@ static const struct file_operations ptdump_fops = {
400435
.llseek = seq_lseek,
401436
.release = single_release,
402437
};
438+
#endif
403439

404440
static int pt_dump_init(void)
405441
{
442+
#ifdef CONFIG_X86_PTDUMP
406443
struct dentry *pe;
444+
#endif
407445

408446
#ifdef CONFIG_X86_32
409447
/* Not a compile-time constant on x86-32 */
@@ -415,10 +453,12 @@ static int pt_dump_init(void)
415453
address_markers[FIXADDR_START_NR].start_address = FIXADDR_START;
416454
#endif
417455

456+
#ifdef CONFIG_X86_PTDUMP
418457
pe = debugfs_create_file("kernel_page_tables", 0600, NULL, NULL,
419458
&ptdump_fops);
420459
if (!pe)
421460
return -ENOMEM;
461+
#endif
422462

423463
return 0;
424464
}

arch/x86/mm/init_32.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,8 @@ void mark_rodata_ro(void)
957957
set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
958958
#endif
959959
mark_nxdata_nx();
960+
if (__supported_pte_mask & _PAGE_NX)
961+
debug_checkwx();
960962
}
961963
#endif
962964

arch/x86/mm/init_64.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,8 @@ void mark_rodata_ro(void)
11501150
free_init_pages("unused kernel",
11511151
(unsigned long) __va(__pa_symbol(rodata_end)),
11521152
(unsigned long) __va(__pa_symbol(_sdata)));
1153+
1154+
debug_checkwx();
11531155
}
11541156

11551157
#endif

0 commit comments

Comments
 (0)