Skip to content

Commit 5bd5a45

Browse files
mat-cIngo Molnar
authored andcommitted
x86: Add NX protection for kernel data
This patch expands functionality of CONFIG_DEBUG_RODATA to set main (static) kernel data area as NX. The following steps are taken to achieve this: 1. Linker script is adjusted so .text always starts and ends on a page bound 2. Linker script is adjusted so .rodata always start and end on a page boundary 3. NX is set for all pages from _etext through _end in mark_rodata_ro. 4. free_init_pages() sets released memory NX in arch/x86/mm/init.c 5. bios rom is set to x when pcibios is used. The results of patch application may be observed in the diff of kernel page table dumps: pcibios: -- data_nx_pt_before.txt 2009-10-13 07:48:59.000000000 -0400 ++ data_nx_pt_after.txt 2009-10-13 07:26:46.000000000 -0400 0x00000000-0xc0000000 3G pmd ---[ Kernel Mapping ]--- -0xc0000000-0xc0100000 1M RW GLB x pte +0xc0000000-0xc00a0000 640K RW GLB NX pte +0xc00a0000-0xc0100000 384K RW GLB x pte -0xc0100000-0xc03d7000 2908K ro GLB x pte +0xc0100000-0xc0318000 2144K ro GLB x pte +0xc0318000-0xc03d7000 764K ro GLB NX pte -0xc03d7000-0xc0600000 2212K RW GLB x pte +0xc03d7000-0xc0600000 2212K RW GLB NX pte 0xc0600000-0xf7a00000 884M RW PSE GLB NX pmd 0xf7a00000-0xf7bfe000 2040K RW GLB NX pte 0xf7bfe000-0xf7c00000 8K pte No pcibios: -- data_nx_pt_before.txt 2009-10-13 07:48:59.000000000 -0400 ++ data_nx_pt_after.txt 2009-10-13 07:26:46.000000000 -0400 0x00000000-0xc0000000 3G pmd ---[ Kernel Mapping ]--- -0xc0000000-0xc0100000 1M RW GLB x pte +0xc0000000-0xc0100000 1M RW GLB NX pte -0xc0100000-0xc03d7000 2908K ro GLB x pte +0xc0100000-0xc0318000 2144K ro GLB x pte +0xc0318000-0xc03d7000 764K ro GLB NX pte -0xc03d7000-0xc0600000 2212K RW GLB x pte +0xc03d7000-0xc0600000 2212K RW GLB NX pte 0xc0600000-0xf7a00000 884M RW PSE GLB NX pmd 0xf7a00000-0xf7bfe000 2040K RW GLB NX pte 0xf7bfe000-0xf7c00000 8K pte The patch has been originally developed for Linux 2.6.34-rc2 x86 by Siarhei Liakh <sliakh.lkml@gmail.com> and Xuxian Jiang <jiang@cs.ncsu.edu>. -v1: initial patch for 2.6.30 -v2: patch for 2.6.31-rc7 -v3: moved all code into arch/x86, adjusted credits -v4: fixed ifdef, removed credits from CREDITS -v5: fixed an address calculation bug in mark_nxdata_nx() -v6: added acked-by and PT dump diff to commit log -v7: minor adjustments for -tip -v8: rework with the merge of "Set first MB as RW+NX" Signed-off-by: Siarhei Liakh <sliakh.lkml@gmail.com> Signed-off-by: Xuxian Jiang <jiang@cs.ncsu.edu> Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr> Cc: Arjan van de Ven <arjan@infradead.org> Cc: James Morris <jmorris@namei.org> Cc: Andi Kleen <ak@muc.de> Cc: Rusty Russell <rusty@rustcorp.com.au> Cc: Stephen Rothwell <sfr@canb.auug.org.au> Cc: Dave Jones <davej@redhat.com> Cc: Kees Cook <kees.cook@canonical.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> LKML-Reference: <4CE2F82E.60601@free.fr> [ minor cleanliness edits ] Signed-off-by: Ingo Molnar <mingo@elte.hu>
1 parent 64edc8e commit 5bd5a45

File tree

7 files changed

+57
-6
lines changed

7 files changed

+57
-6
lines changed

arch/x86/include/asm/pci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ extern unsigned long pci_mem_start;
6565

6666
#define PCIBIOS_MIN_CARDBUS_IO 0x4000
6767

68+
extern int pcibios_enabled;
6869
void pcibios_config_init(void);
6970
struct pci_bus *pcibios_scan_root(int bus);
7071

arch/x86/kernel/vmlinux.lds.S

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jiffies_64 = jiffies;
6969

7070
PHDRS {
7171
text PT_LOAD FLAGS(5); /* R_E */
72-
data PT_LOAD FLAGS(7); /* RWE */
72+
data PT_LOAD FLAGS(6); /* RW_ */
7373
#ifdef CONFIG_X86_64
7474
user PT_LOAD FLAGS(5); /* R_E */
7575
#ifdef CONFIG_SMP
@@ -116,6 +116,10 @@ SECTIONS
116116

117117
EXCEPTION_TABLE(16) :text = 0x9090
118118

119+
#if defined(CONFIG_DEBUG_RODATA)
120+
/* .text should occupy whole number of pages */
121+
. = ALIGN(PAGE_SIZE);
122+
#endif
119123
X64_ALIGN_DEBUG_RODATA_BEGIN
120124
RO_DATA(PAGE_SIZE)
121125
X64_ALIGN_DEBUG_RODATA_END
@@ -335,7 +339,7 @@ SECTIONS
335339
__bss_start = .;
336340
*(.bss..page_aligned)
337341
*(.bss)
338-
. = ALIGN(4);
342+
. = ALIGN(PAGE_SIZE);
339343
__bss_stop = .;
340344
}
341345

arch/x86/mm/init.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,9 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end)
364364
/*
365365
* We just marked the kernel text read only above, now that
366366
* we are going to free part of that, we need to make that
367-
* writeable first.
367+
* writeable and non-executable first.
368368
*/
369+
set_memory_nx(begin, (end - begin) >> PAGE_SHIFT);
369370
set_memory_rw(begin, (end - begin) >> PAGE_SHIFT);
370371

371372
printk(KERN_INFO "Freeing %s: %luk freed\n", what, (end - begin) >> 10);

arch/x86/mm/init_32.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base)
226226

227227
static inline int is_kernel_text(unsigned long addr)
228228
{
229-
if (addr >= PAGE_OFFSET && addr <= (unsigned long)__init_end)
229+
if (addr >= (unsigned long)_text && addr <= (unsigned long)__init_end)
230230
return 1;
231231
return 0;
232232
}
@@ -912,6 +912,23 @@ void set_kernel_text_ro(void)
912912
set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
913913
}
914914

915+
static void mark_nxdata_nx(void)
916+
{
917+
/*
918+
* When this called, init has already been executed and released,
919+
* so everything past _etext sould be NX.
920+
*/
921+
unsigned long start = PFN_ALIGN(_etext);
922+
/*
923+
* This comes from is_kernel_text upper limit. Also HPAGE where used:
924+
*/
925+
unsigned long size = (((unsigned long)__init_end + HPAGE_SIZE) & HPAGE_MASK) - start;
926+
927+
if (__supported_pte_mask & _PAGE_NX)
928+
printk(KERN_INFO "NX-protecting the kernel data: %luk\n", size >> 10);
929+
set_pages_nx(virt_to_page(start), size >> PAGE_SHIFT);
930+
}
931+
915932
void mark_rodata_ro(void)
916933
{
917934
unsigned long start = PFN_ALIGN(_text);
@@ -946,6 +963,7 @@ void mark_rodata_ro(void)
946963
printk(KERN_INFO "Testing CPA: write protecting again\n");
947964
set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
948965
#endif
966+
mark_nxdata_nx();
949967
}
950968
#endif
951969

arch/x86/mm/init_64.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,7 @@ void mark_rodata_ro(void)
788788
unsigned long rodata_start =
789789
((unsigned long)__start_rodata + PAGE_SIZE - 1) & PAGE_MASK;
790790
unsigned long end = (unsigned long) &__end_rodata_hpage_align;
791+
unsigned long kernel_end = (((unsigned long)&__init_end + HPAGE_SIZE) & HPAGE_MASK);
791792
unsigned long text_end = PAGE_ALIGN((unsigned long) &__stop___ex_table);
792793
unsigned long rodata_end = PAGE_ALIGN((unsigned long) &__end_rodata);
793794
unsigned long data_start = (unsigned long) &_sdata;
@@ -802,7 +803,7 @@ void mark_rodata_ro(void)
802803
* The rodata section (but not the kernel text!) should also be
803804
* not-executable.
804805
*/
805-
set_memory_nx(rodata_start, (end - rodata_start) >> PAGE_SHIFT);
806+
set_memory_nx(rodata_start, (kernel_end - rodata_start) >> PAGE_SHIFT);
806807

807808
rodata_test();
808809

arch/x86/mm/pageattr.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/pfn.h>
1414
#include <linux/percpu.h>
1515
#include <linux/gfp.h>
16+
#include <linux/pci.h>
1617

1718
#include <asm/e820.h>
1819
#include <asm/processor.h>
@@ -261,8 +262,10 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address,
261262
* The BIOS area between 640k and 1Mb needs to be executable for
262263
* PCI BIOS based config access (CONFIG_PCI_GOBIOS) support.
263264
*/
264-
if (within(pfn, BIOS_BEGIN >> PAGE_SHIFT, BIOS_END >> PAGE_SHIFT))
265+
#ifdef CONFIG_PCI_BIOS
266+
if (pcibios_enabled && within(pfn, BIOS_BEGIN >> PAGE_SHIFT, BIOS_END >> PAGE_SHIFT))
265267
pgprot_val(forbidden) |= _PAGE_NX;
268+
#endif
266269

267270
/*
268271
* The kernel text needs to be executable for obvious reasons

arch/x86/pci/pcbios.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/uaccess.h>
1010
#include <asm/pci_x86.h>
1111
#include <asm/pci-functions.h>
12+
#include <asm/cacheflush.h>
1213

1314
/* BIOS32 signature: "_32_" */
1415
#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
@@ -25,6 +26,27 @@
2526
#define PCIBIOS_HW_TYPE1_SPEC 0x10
2627
#define PCIBIOS_HW_TYPE2_SPEC 0x20
2728

29+
int pcibios_enabled;
30+
31+
/* According to the BIOS specification at:
32+
* http://members.datafast.net.au/dft0802/specs/bios21.pdf, we could
33+
* restrict the x zone to some pages and make it ro. But this may be
34+
* broken on some bios, complex to handle with static_protections.
35+
* We could make the 0xe0000-0x100000 range rox, but this can break
36+
* some ISA mapping.
37+
*
38+
* So we let's an rw and x hole when pcibios is used. This shouldn't
39+
* happen for modern system with mmconfig, and if you don't want it
40+
* you could disable pcibios...
41+
*/
42+
static inline void set_bios_x(void)
43+
{
44+
pcibios_enabled = 1;
45+
set_memory_x(PAGE_OFFSET + BIOS_BEGIN, (BIOS_END - BIOS_BEGIN) >> PAGE_SHIFT);
46+
if (__supported_pte_mask & _PAGE_NX)
47+
printk(KERN_INFO "PCI : PCI BIOS aera is rw and x. Use pci=nobios if you want it NX.\n");
48+
}
49+
2850
/*
2951
* This is the standard structure used to identify the entry point
3052
* to the BIOS32 Service Directory, as documented in
@@ -332,6 +354,7 @@ static struct pci_raw_ops * __devinit pci_find_bios(void)
332354
DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n",
333355
bios32_entry);
334356
bios32_indirect.address = bios32_entry + PAGE_OFFSET;
357+
set_bios_x();
335358
if (check_pcibios())
336359
return &pci_bios_access;
337360
}

0 commit comments

Comments
 (0)