Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bd5a452 authored by Matthieu CASTET's avatar Matthieu CASTET Committed by Ingo Molnar
Browse files

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: default avatarSiarhei Liakh <sliakh.lkml@gmail.com>
Signed-off-by: default avatarXuxian Jiang <jiang@cs.ncsu.edu>
Signed-off-by: default avatarMatthieu 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: default avatarIngo Molnar <mingo@elte.hu>
parent 64edc8ed
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -65,6 +65,7 @@ extern unsigned long pci_mem_start;


#define PCIBIOS_MIN_CARDBUS_IO	0x4000
#define PCIBIOS_MIN_CARDBUS_IO	0x4000


extern int pcibios_enabled;
void pcibios_config_init(void);
void pcibios_config_init(void);
struct pci_bus *pcibios_scan_root(int bus);
struct pci_bus *pcibios_scan_root(int bus);


+6 −2
Original line number Original line Diff line number Diff line
@@ -69,7 +69,7 @@ jiffies_64 = jiffies;


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


	EXCEPTION_TABLE(16) :text = 0x9090
	EXCEPTION_TABLE(16) :text = 0x9090


#if defined(CONFIG_DEBUG_RODATA)
	/* .text should occupy whole number of pages */
	. = ALIGN(PAGE_SIZE);
#endif
	X64_ALIGN_DEBUG_RODATA_BEGIN
	X64_ALIGN_DEBUG_RODATA_BEGIN
	RO_DATA(PAGE_SIZE)
	RO_DATA(PAGE_SIZE)
	X64_ALIGN_DEBUG_RODATA_END
	X64_ALIGN_DEBUG_RODATA_END
@@ -335,7 +339,7 @@ SECTIONS
		__bss_start = .;
		__bss_start = .;
		*(.bss..page_aligned)
		*(.bss..page_aligned)
		*(.bss)
		*(.bss)
		. = ALIGN(4);
		. = ALIGN(PAGE_SIZE);
		__bss_stop = .;
		__bss_stop = .;
	}
	}


+2 −1
Original line number Original line Diff line number Diff line
@@ -364,8 +364,9 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end)
	/*
	/*
	 * We just marked the kernel text read only above, now that
	 * We just marked the kernel text read only above, now that
	 * we are going to free part of that, we need to make that
	 * we are going to free part of that, we need to make that
	 * writeable first.
	 * writeable and non-executable first.
	 */
	 */
	set_memory_nx(begin, (end - begin) >> PAGE_SHIFT);
	set_memory_rw(begin, (end - begin) >> PAGE_SHIFT);
	set_memory_rw(begin, (end - begin) >> PAGE_SHIFT);


	printk(KERN_INFO "Freeing %s: %luk freed\n", what, (end - begin) >> 10);
	printk(KERN_INFO "Freeing %s: %luk freed\n", what, (end - begin) >> 10);
+19 −1
Original line number Original line Diff line number Diff line
@@ -226,7 +226,7 @@ page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base)


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


static void mark_nxdata_nx(void)
{
	/*
	 * When this called, init has already been executed and released,
	 * so everything past _etext sould be NX.
	 */
	unsigned long start = PFN_ALIGN(_etext);
	/*
	 * This comes from is_kernel_text upper limit. Also HPAGE where used:
	 */
	unsigned long size = (((unsigned long)__init_end + HPAGE_SIZE) & HPAGE_MASK) - start;

	if (__supported_pte_mask & _PAGE_NX)
		printk(KERN_INFO "NX-protecting the kernel data: %luk\n", size >> 10);
	set_pages_nx(virt_to_page(start), size >> PAGE_SHIFT);
}

void mark_rodata_ro(void)
void mark_rodata_ro(void)
{
{
	unsigned long start = PFN_ALIGN(_text);
	unsigned long start = PFN_ALIGN(_text);
@@ -946,6 +963,7 @@ void mark_rodata_ro(void)
	printk(KERN_INFO "Testing CPA: write protecting again\n");
	printk(KERN_INFO "Testing CPA: write protecting again\n");
	set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
	set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
#endif
#endif
	mark_nxdata_nx();
}
}
#endif
#endif
+2 −1
Original line number Original line Diff line number Diff line
@@ -788,6 +788,7 @@ void mark_rodata_ro(void)
	unsigned long rodata_start =
	unsigned long rodata_start =
		((unsigned long)__start_rodata + PAGE_SIZE - 1) & PAGE_MASK;
		((unsigned long)__start_rodata + PAGE_SIZE - 1) & PAGE_MASK;
	unsigned long end = (unsigned long) &__end_rodata_hpage_align;
	unsigned long end = (unsigned long) &__end_rodata_hpage_align;
	unsigned long kernel_end = (((unsigned long)&__init_end + HPAGE_SIZE) & HPAGE_MASK);
	unsigned long text_end = PAGE_ALIGN((unsigned long) &__stop___ex_table);
	unsigned long text_end = PAGE_ALIGN((unsigned long) &__stop___ex_table);
	unsigned long rodata_end = PAGE_ALIGN((unsigned long) &__end_rodata);
	unsigned long rodata_end = PAGE_ALIGN((unsigned long) &__end_rodata);
	unsigned long data_start = (unsigned long) &_sdata;
	unsigned long data_start = (unsigned long) &_sdata;
@@ -802,7 +803,7 @@ void mark_rodata_ro(void)
	 * The rodata section (but not the kernel text!) should also be
	 * The rodata section (but not the kernel text!) should also be
	 * not-executable.
	 * not-executable.
	 */
	 */
	set_memory_nx(rodata_start, (end - rodata_start) >> PAGE_SHIFT);
	set_memory_nx(rodata_start, (kernel_end - rodata_start) >> PAGE_SHIFT);


	rodata_test();
	rodata_test();


Loading