Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1763318AbZJOX0U (ORCPT ); Thu, 15 Oct 2009 19:26:20 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1763303AbZJOX0T (ORCPT ); Thu, 15 Oct 2009 19:26:19 -0400 Received: from mail-fx0-f218.google.com ([209.85.220.218]:46633 "EHLO mail-fx0-f218.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754048AbZJOX0S (ORCPT ); Thu, 15 Oct 2009 19:26:18 -0400 DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:date:message-id:subject:from:to:cc:content-type; b=ZU0WtFMDZt1TD+wppaRYNtQtHoomYRYkD10Ssdk6tUhlmtBBe6Nql2jHaiEx2MZDid yfPveD0BsxP4mo9o3tqgE4KPgM9TCP9Vv/FmQBjADUBZ6inNjy8AqhvqBFHBoBmpJBkR TWWfXxFQmuLaiOLrYoBotOsDc4bxxHGizf5II= MIME-Version: 1.0 Date: Thu, 15 Oct 2009 19:25:39 -0400 Message-ID: <817ecb6f0910151625o7eb645e8pff574a2c12bdb763@mail.gmail.com> Subject: [PATCH V6] x86: NX protection for kernel data From: Siarhei Liakh To: linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: Arjan van de Ven , James Morris , Andrew Morton , Andi Kleen , Rusty Russell , Thomas Gleixner , "H. Peter Anvin" , Ingo Molnar , David Howells , Aristeu Rozanski Content-Type: text/plain; charset=ISO-8859-1 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 5400 Lines: 139 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 boundary 2. Linker script is adjusted so .rodata and .data always start and end on a page boundary 3. void mark_nxdata_nx(void) added to arch/x86/mm/init.c with actual functionality: NX is set for all pages from _etext through _end. 4. mark_nxdata_nx() called from free_initmem() (after init has been released) 5. free_init_pages() sets released memory NX in arch/x86/mm/init.c The results of patch application may be observed in the diff of kernel page table dumps: --- 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 @@ -2,8 +2,9 @@ 0x00000000-0xc0000000 3G pmd ---[ Kernel Mapping ]--- 0xc0000000-0xc0100000 1M RW GLB x pte -0xc0100000-0xc048d000 3636K ro GLB x pte -0xc048d000-0xc0600000 1484K RW GLB x pte +0xc0100000-0xc0381000 2564K ro GLB x pte +0xc0381000-0xc048d000 1072K ro GLB NX pte +0xc048d000-0xc0600000 1484K RW GLB NX pte 0xc0600000-0xf7800000 882M RW PSE GLB NX pmd 0xf7800000-0xf79fe000 2040K RW GLB NX pte 0xf79fe000-0xf7a00000 8K pte The patch have been developed for Linux 2.6.31-rc7 x86 by Siarhei Liakh and Xuxian Jiang . 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 --- Signed-off-by: Siarhei Liakh Signed-off-by: Xuxian Jiang Acked-by: Arjan van de Ven diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index 78d185d..83ae734 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -43,14 +43,14 @@ jiffies_64 = jiffies; PHDRS { text PT_LOAD FLAGS(5); /* R_E */ - data PT_LOAD FLAGS(7); /* RWE */ + data PT_LOAD FLAGS(6); /* RW_ */ #ifdef CONFIG_X86_64 - user PT_LOAD FLAGS(7); /* RWE */ - data.init PT_LOAD FLAGS(7); /* RWE */ + user PT_LOAD FLAGS(6); /* RW_ */ + data.init PT_LOAD FLAGS(6); /* RW_ */ #ifdef CONFIG_SMP - percpu PT_LOAD FLAGS(7); /* RWE */ + percpu PT_LOAD FLAGS(6); /* RW_ */ #endif - data.init2 PT_LOAD FLAGS(7); /* RWE */ + data.init2 PT_LOAD FLAGS(6); /* RW_ */ #endif note PT_NOTE FLAGS(0); /* ___ */ } @@ -89,6 +89,8 @@ SECTIONS IRQENTRY_TEXT *(.fixup) *(.gnu.warning) + /* .text should occupy whole number of pages */ + . = ALIGN(PAGE_SIZE); /* End of text section */ _etext = .; } :text = 0x9090 @@ -151,6 +153,8 @@ SECTIONS .data.read_mostly : AT(ADDR(.data.read_mostly) - LOAD_OFFSET) { *(.data.read_mostly) + /* .data should occupy whole number of pages */ + . = ALIGN(PAGE_SIZE); /* End of data section */ _edata = .; } diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 0607119..7bfd411 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -423,9 +423,10 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end) /* * We just marked the kernel text read only above, now that * we are going to free part of that, we need to make that - * writeable first. + * writeable and non-executable first. */ set_memory_rw(begin, (end - begin) >> PAGE_SHIFT); + set_memory_nx(begin, (end - begin) >> PAGE_SHIFT); printk(KERN_INFO "Freeing %s: %luk freed\n", what, (end - begin) >> 10); @@ -440,11 +441,29 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end) #endif } +void mark_nxdata_nx(void) +{ +#ifdef CONFIG_DEBUG_RODATA + /* + * When this called, init has already been executed and released, + * so everything past _etext sould be NX. + */ + unsigned long start = PAGE_ALIGN((unsigned long)(&_etext)); + unsigned long size = PAGE_ALIGN((unsigned long)(&_end)) - start; + + printk(KERN_INFO "NX-protecting the kernel data: %lx, %lu pages\n", + start, size >> PAGE_SHIFT); + set_memory_nx(start, size >> PAGE_SHIFT); +#endif +} + void free_initmem(void) { free_init_pages("unused kernel memory", (unsigned long)(&__init_begin), (unsigned long)(&__init_end)); + /* Set kernel's data as NX */ + mark_nxdata_nx(); } #ifdef CONFIG_BLK_DEV_INITRD -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/