mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Andi Kleen <ak@suse.de>
To: patches@x86-64.org, linux-kernel@vger.kernel.org
Subject: [PATCH] [35/48] x86: tighten kernel image page access rights
Date: Sun, 29 Apr 2007 12:53:25 +0200 (CEST)	[thread overview]
Message-ID: <20070429105325.0B1A0151C5@wotan.suse.de> (raw)
In-Reply-To: <200704291252.514680000@suse.de>


On x86-64, kernel memory freed after init can be entirely unmapped instead
of just getting 'poisoned' by overwriting with a debug pattern.

On i386 and x86-64 (under CONFIG_DEBUG_RODATA), kernel text and bug table
can also be write-protected.

Compared to the first version, this one prevents re-creating deleted
mappings in the kernel image range on x86-64, if those got removed
previously. This, together with the original changes, prevents temporarily
having inconsistent mappings when cacheability attributes are being
changed on such pages (e.g. from AGP code). While on i386 such duplicate
mappings don't exist, the same change is done there, too, both for
consistency and because checking pte_present() before using various other
pte_XXX functions is a requirement anyway. At once, i386 code gets
adjusted to use pte_huge() instead of open coding this.

Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Andi Kleen <ak@suse.de>

---
 arch/i386/kernel/vmlinux.lds.S   |    4 ++--
 arch/i386/mm/init.c              |   27 ++++++++++++++++++++-------
 arch/i386/mm/pageattr.c          |    6 ++++--
 arch/x86_64/kernel/head.S        |    1 -
 arch/x86_64/kernel/vmlinux.lds.S |    5 +++--
 arch/x86_64/mm/init.c            |   25 ++++++++++++++++---------
 arch/x86_64/mm/pageattr.c        |   18 +++++++++++++-----
 include/asm-i386/pgtable.h       |    2 ++
 include/linux/poison.h           |    3 ---
 9 files changed, 60 insertions(+), 31 deletions(-)

Index: linux/arch/i386/kernel/vmlinux.lds.S
===================================================================
--- linux.orig/arch/i386/kernel/vmlinux.lds.S
+++ linux/arch/i386/kernel/vmlinux.lds.S
@@ -61,8 +61,6 @@ SECTIONS
   	__stop___ex_table = .;
   }
 
-  RODATA
-
   BUG_TABLE
 
   . = ALIGN(4);
@@ -72,6 +70,8 @@ SECTIONS
   	__tracedata_end = .;
   }
 
+  RODATA
+
   /* writeable */
   . = ALIGN(4096);
   .data : AT(ADDR(.data) - LOAD_OFFSET) {	/* Data */
Index: linux/arch/i386/mm/init.c
===================================================================
--- linux.orig/arch/i386/mm/init.c
+++ linux/arch/i386/mm/init.c
@@ -22,6 +22,7 @@
 #include <linux/init.h>
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
+#include <linux/pfn.h>
 #include <linux/poison.h>
 #include <linux/bootmem.h>
 #include <linux/slab.h>
@@ -751,13 +752,25 @@ static int noinline do_test_wp_bit(void)
 
 void mark_rodata_ro(void)
 {
-	unsigned long addr = (unsigned long)__start_rodata;
+	unsigned long start = PFN_ALIGN(_text);
+	unsigned long size = PFN_ALIGN(_etext) - start;
 
-	for (; addr < (unsigned long)__end_rodata; addr += PAGE_SIZE)
-		change_page_attr(virt_to_page(addr), 1, PAGE_KERNEL_RO);
-
-	printk("Write protecting the kernel read-only data: %uk\n",
-			(__end_rodata - __start_rodata) >> 10);
+#ifdef CONFIG_HOTPLUG_CPU
+	/* It must still be possible to apply SMP alternatives. */
+	if (num_possible_cpus() <= 1)
+#endif
+	{
+		change_page_attr(virt_to_page(start),
+		                 size >> PAGE_SHIFT, PAGE_KERNEL_RX);
+		printk("Write protecting the kernel text: %luk\n", size >> 10);
+	}
+
+	start += size;
+	size = (unsigned long)__end_rodata - start;
+	change_page_attr(virt_to_page(start),
+	                 size >> PAGE_SHIFT, PAGE_KERNEL_RO);
+	printk("Write protecting the kernel read-only data: %luk\n",
+	       size >> 10);
 
 	/*
 	 * change_page_attr() requires a global_flush_tlb() call after it.
@@ -781,7 +794,7 @@ void free_init_pages(char *what, unsigne
 		__free_page(page);
 		totalram_pages++;
 	}
-	printk(KERN_INFO "Freeing %s: %ldk freed\n", what, (end - begin) >> 10);
+	printk(KERN_INFO "Freeing %s: %luk freed\n", what, (end - begin) >> 10);
 }
 
 void free_initmem(void)
Index: linux/arch/i386/mm/pageattr.c
===================================================================
--- linux.orig/arch/i386/mm/pageattr.c
+++ linux/arch/i386/mm/pageattr.c
@@ -140,9 +140,11 @@ __change_page_attr(struct page *page, pg
 	kpte = lookup_address(address);
 	if (!kpte)
 		return -EINVAL;
+	if (!pte_present(*kpte))
+		return 0;
 	kpte_page = virt_to_page(kpte);
 	if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) { 
-		if ((pte_val(*kpte) & _PAGE_PSE) == 0) { 
+		if (!pte_huge(*kpte)) {
 			set_pte_atomic(kpte, mk_pte(page, prot)); 
 		} else {
 			pgprot_t ref_prot;
@@ -158,7 +160,7 @@ __change_page_attr(struct page *page, pg
 			kpte_page = split;
 		}
 		page_private(kpte_page)++;
-	} else if ((pte_val(*kpte) & _PAGE_PSE) == 0) { 
+	} else if (!pte_huge(*kpte)) {
 		set_pte_atomic(kpte, mk_pte(page, PAGE_KERNEL));
 		BUG_ON(page_private(kpte_page) == 0);
 		page_private(kpte_page)--;
Index: linux/arch/x86_64/kernel/head.S
===================================================================
--- linux.orig/arch/x86_64/kernel/head.S
+++ linux/arch/x86_64/kernel/head.S
@@ -280,7 +280,6 @@ early_idt_ripmsg:
 
 .balign PAGE_SIZE
 ENTRY(stext)
-ENTRY(_stext)
 
 #define NEXT_PAGE(name) \
 	.balign	PAGE_SIZE; \
Index: linux/arch/x86_64/kernel/vmlinux.lds.S
===================================================================
--- linux.orig/arch/x86_64/kernel/vmlinux.lds.S
+++ linux/arch/x86_64/kernel/vmlinux.lds.S
@@ -29,6 +29,7 @@ SECTIONS
   .text :  AT(ADDR(.text) - LOAD_OFFSET) {
 	/* First the code that has to be first for bootstrapping */
 	*(.bootstrap.text)
+	_stext = .;
 	/* Then all the functions that are "hot" in profiles, to group them
            onto the same hugetlb entry */
 	#include "functionlist"
@@ -50,10 +51,10 @@ SECTIONS
   __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { *(__ex_table) }
   __stop___ex_table = .;
 
-  RODATA
-
   BUG_TABLE
 
+  RODATA
+
   . = ALIGN(PAGE_SIZE);        /* Align data segment to page size boundary */
 				/* Data */
   .data : AT(ADDR(.data) - LOAD_OFFSET) {
Index: linux/arch/x86_64/mm/init.c
===================================================================
--- linux.orig/arch/x86_64/mm/init.c
+++ linux/arch/x86_64/mm/init.c
@@ -22,6 +22,7 @@
 #include <linux/bootmem.h>
 #include <linux/proc_fs.h>
 #include <linux/pci.h>
+#include <linux/pfn.h>
 #include <linux/poison.h>
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
@@ -563,21 +564,23 @@ void free_init_pages(char *what, unsigne
 	if (begin >= end)
 		return;
 
-	printk(KERN_INFO "Freeing %s: %ldk freed\n", what, (end - begin) >> 10);
+	printk(KERN_INFO "Freeing %s: %luk freed\n", what, (end - begin) >> 10);
 	for (addr = begin; addr < end; addr += PAGE_SIZE) {
 		struct page *page = pfn_to_page(addr >> PAGE_SHIFT);
 		ClearPageReserved(page);
 		init_page_count(page);
 		memset(page_address(page), POISON_FREE_INITMEM, PAGE_SIZE);
+		if (addr >= __START_KERNEL_map)
+			change_page_attr_addr(addr, 1, __pgprot(0));
 		__free_page(page);
 		totalram_pages++;
 	}
+	if (addr > __START_KERNEL_map)
+		global_flush_tlb();
 }
 
 void free_initmem(void)
 {
-	memset(__initdata_begin, POISON_FREE_INITDATA,
-		__initdata_end - __initdata_begin);
 	free_init_pages("unused kernel memory",
 			__pa_symbol(&__init_begin),
 			__pa_symbol(&__init_end));
@@ -587,14 +590,18 @@ void free_initmem(void)
 
 void mark_rodata_ro(void)
 {
-	unsigned long addr = (unsigned long)__va(__pa_symbol(&__start_rodata));
-	unsigned long end  = (unsigned long)__va(__pa_symbol(&__end_rodata));
+	unsigned long start = PFN_ALIGN(__va(__pa_symbol(&_stext))), size;
 
-	for (; addr < end; addr += PAGE_SIZE)
-		change_page_attr_addr(addr, 1, PAGE_KERNEL_RO);
+#ifdef CONFIG_HOTPLUG_CPU
+	/* It must still be possible to apply SMP alternatives. */
+	if (num_possible_cpus() > 1)
+		start = PFN_ALIGN(__va(__pa_symbol(&_etext)));
+#endif
+	size = (unsigned long)__va(__pa_symbol(&__end_rodata)) - start;
+	change_page_attr_addr(start, size >> PAGE_SHIFT, PAGE_KERNEL_RO);
 
-	printk ("Write protecting the kernel read-only data: %luk\n",
-			(__end_rodata - __start_rodata) >> 10);
+	printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n",
+	       size >> 10);
 
 	/*
 	 * change_page_attr_addr() requires a global_flush_tlb() call after it.
Index: linux/arch/x86_64/mm/pageattr.c
===================================================================
--- linux.orig/arch/x86_64/mm/pageattr.c
+++ linux/arch/x86_64/mm/pageattr.c
@@ -126,7 +126,7 @@ __change_page_attr(unsigned long address
 	struct page *kpte_page;
 	pgprot_t ref_prot2;
 	kpte = lookup_address(address);
-	if (!kpte) return 0;
+	if (!kpte || !pte_present(*kpte)) return 0;
 	kpte_page = virt_to_page(((unsigned long)kpte) & PAGE_MASK);
 	if (pgprot_val(prot) != pgprot_val(ref_prot)) { 
 		if (!pte_huge(*kpte)) {
@@ -179,16 +179,24 @@ __change_page_attr(unsigned long address
 int change_page_attr_addr(unsigned long address, int numpages, pgprot_t prot)
 {
 	unsigned long phys_base_pfn = __pa_symbol(__START_KERNEL_map) >> PAGE_SHIFT;
-	int err = 0; 
+	int err = 0, kernel_map = 0;
 	int i; 
 
+	if (address >= __START_KERNEL_map
+	    && address < __START_KERNEL_map + KERNEL_TEXT_SIZE) {
+		address = (unsigned long)__va(__pa(address));
+		kernel_map = 1;
+	}
+
 	down_write(&init_mm.mmap_sem);
 	for (i = 0; i < numpages; i++, address += PAGE_SIZE) {
 		unsigned long pfn = __pa(address) >> PAGE_SHIFT;
 
-		err = __change_page_attr(address, pfn, prot, PAGE_KERNEL);
-		if (err) 
-			break; 
+		if (!kernel_map || pte_present(pfn_pte(0, prot))) {
+			err = __change_page_attr(address, pfn, prot, PAGE_KERNEL);
+			if (err)
+				break;
+		}
 		/* Handle kernel mapping too which aliases part of the
 		 * lowmem */
 		if ((pfn >= phys_base_pfn) &&
Index: linux/include/asm-i386/pgtable.h
===================================================================
--- linux.orig/include/asm-i386/pgtable.h
+++ linux/include/asm-i386/pgtable.h
@@ -159,6 +159,7 @@ void paging_init(void);
 
 extern unsigned long long __PAGE_KERNEL, __PAGE_KERNEL_EXEC;
 #define __PAGE_KERNEL_RO		(__PAGE_KERNEL & ~_PAGE_RW)
+#define __PAGE_KERNEL_RX		(__PAGE_KERNEL_EXEC & ~_PAGE_RW)
 #define __PAGE_KERNEL_NOCACHE		(__PAGE_KERNEL | _PAGE_PCD)
 #define __PAGE_KERNEL_LARGE		(__PAGE_KERNEL | _PAGE_PSE)
 #define __PAGE_KERNEL_LARGE_EXEC	(__PAGE_KERNEL_EXEC | _PAGE_PSE)
@@ -166,6 +167,7 @@ extern unsigned long long __PAGE_KERNEL,
 #define PAGE_KERNEL		__pgprot(__PAGE_KERNEL)
 #define PAGE_KERNEL_RO		__pgprot(__PAGE_KERNEL_RO)
 #define PAGE_KERNEL_EXEC	__pgprot(__PAGE_KERNEL_EXEC)
+#define PAGE_KERNEL_RX		__pgprot(__PAGE_KERNEL_RX)
 #define PAGE_KERNEL_NOCACHE	__pgprot(__PAGE_KERNEL_NOCACHE)
 #define PAGE_KERNEL_LARGE	__pgprot(__PAGE_KERNEL_LARGE)
 #define PAGE_KERNEL_LARGE_EXEC	__pgprot(__PAGE_KERNEL_LARGE_EXEC)
Index: linux/include/linux/poison.h
===================================================================
--- linux.orig/include/linux/poison.h
+++ linux/include/linux/poison.h
@@ -26,9 +26,6 @@
 /********** arch/$ARCH/mm/init.c **********/
 #define POISON_FREE_INITMEM	0xcc
 
-/********** arch/x86_64/mm/init.c **********/
-#define	POISON_FREE_INITDATA	0xba
-
 /********** arch/ia64/hp/common/sba_iommu.c **********/
 /*
  * arch/ia64/hp/common/sba_iommu.c uses a 16-byte poison string with a

  parent reply	other threads:[~2007-04-29 11:05 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-04-29 10:52 [PATCH] [0/48] x86 candidate patches for review III: various stuff Andi Kleen
2007-04-29 10:52 ` [PATCH] [1/48] x86_64: fix x86_64-mm-sched-clock-share Andi Kleen
2007-04-29 10:52 ` [PATCH] [2/48] i386: Rewrite sched_clock Andi Kleen
2007-04-29 10:52 ` [PATCH] [3/48] x86_64: Use new shared sched_clock in x86-64 too Andi Kleen
2007-04-29 10:52 ` [PATCH] [4/48] x86_64: Don't disable basic block reordering Andi Kleen
2007-04-29 10:52 ` [PATCH] [5/48] x86_64: Allow sys_uselib unconditionally Andi Kleen
2007-04-29 10:52 ` [PATCH] [6/48] x86_64: Minor white space cleanup in traps.c Andi Kleen
2007-04-29 10:52 ` [PATCH] [7/48] x86_64: Set HASHDIST_DEFAULT to 1 for x86_64 NUMA Andi Kleen
2007-04-29 10:52 ` [PATCH] [8/48] i386: modpost apic related warning fixes Andi Kleen
2007-04-29 10:52 ` [PATCH] [9/48] i386: make struct vmi_ops static Andi Kleen
2007-04-29 10:52 ` [PATCH] [10/48] i386: type cast clean up for find_next_zero_bit Andi Kleen
2007-04-29 10:52 ` [PATCH] [11/48] i386: workaround for a -Wmissing-prototypes warning Andi Kleen
2007-04-29 10:53 ` [PATCH] [12/48] x86: Log reason why TSC was marked unstable Andi Kleen
2007-04-29 10:53 ` [PATCH] [13/48] x86_64: fix ia32_binfmt.c build error Andi Kleen
2007-04-29 10:53 ` [PATCH] [14/48] x86_64: remove extra smp_processor_id calling Andi Kleen
2007-04-29 10:53 ` [PATCH] [15/48] x86_64: make simnow_init() static Andi Kleen
2007-04-29 10:53 ` [PATCH] [16/48] i386: vmi_pmd_clear() static Andi Kleen
2007-04-29 10:53 ` [PATCH] [18/48] x86_64: configurable fake numa node sizes Andi Kleen
2007-04-29 10:53 ` [PATCH] [19/48] x86_64: split remaining fake nodes equally Andi Kleen
2007-04-29 10:53 ` [PATCH] [20/48] x86_64: fixed size remaining fake nodes Andi Kleen
2007-04-29 10:53 ` [PATCH] [21/48] x86: remove constant_tsc reporting from /proc/cpuinfo' power flags Andi Kleen
2007-04-29 10:53 ` [PATCH] [22/48] x86_64: fake numa for cpusets document Andi Kleen
2007-04-29 10:53 ` [PATCH] [23/48] i386: VDSO_PRELINK warning fix Andi Kleen
2007-04-29 10:53 ` [PATCH] [24/48] i386: Initialize esp0 properly all the time Andi Kleen
2007-04-29 10:53 ` [PATCH] [25/48] x86_64: Introduce load_TLS to the "for" loop Andi Kleen
2007-04-29 10:53 ` [PATCH] [26/48] x86_64: Clarify CONFIG_REORDER explanation Andi Kleen
2007-04-29 10:53 ` [PATCH] [27/48] i386: Allow i386 crash kernels to handle x86_64 dumps Andi Kleen
2007-04-29 10:53 ` [PATCH] [28/48] i386: prevent ACPI quirk warning mass spamming in logs Andi Kleen
2007-04-29 10:53 ` [PATCH] [29/48] x86: add command line length to boot protocol Andi Kleen
2007-04-29 10:53 ` [PATCH] [30/48] i386: Use per-cpu variables for GDT, PDA Andi Kleen
2007-04-29 10:53 ` [PATCH] [31/48] i386: Use per-cpu GDT immediately upon boot Andi Kleen
2007-04-29 10:53 ` [PATCH] [32/48] i386: clean up cpu_init() Andi Kleen
2007-04-29 10:53 ` [PATCH] [33/48] i386: Rename boot_gdt_table to boot_gdt Andi Kleen
2007-04-29 10:53 ` [PATCH] [34/48] i386: rationalize paravirt wrappers Andi Kleen
2007-04-29 10:53 ` Andi Kleen [this message]
2007-04-29 10:53 ` [PATCH] [36/48] i386: get rid of unused variables Andi Kleen
2007-04-29 10:53 ` [PATCH] [37/48] i386: ignore vgacon if hardware not present Andi Kleen
2007-04-29 12:17   ` Antonino A. Daplas
2007-04-29 13:24     ` Andi Kleen
2007-04-29 14:10       ` Antonino A. Daplas
2007-04-29 14:16         ` Andi Kleen
2007-04-29 17:16           ` Jeremy Fitzhardinge
2007-04-29 17:39             ` Andi Kleen
2007-04-29 10:53 ` [PATCH] [38/48] x86_64: Remove unused stext symbol Andi Kleen
2007-04-29 10:53 ` [PATCH] [39/48] i386: remove the APM_RTC_IS_GMT config option Andi Kleen
2007-04-29 10:53 ` [PATCH] [40/48] x86_64: use lru instead of page->index and page->private for pgd lists management Andi Kleen
2007-04-29 10:53 ` [PATCH] [41/48] x86: sys_ioperm() prototype cleanup Andi Kleen
2007-04-29 10:53 ` [PATCH] [42/48] x86: remove UNEXPECTED_IO_APIC() Andi Kleen
2007-04-29 10:53 ` [PATCH] [43/48] x86_64: fix vtime() vsyscall Andi Kleen
2007-04-29 10:53 ` [PATCH] [44/48] x86_64: vsyscall_gtod_data diet and vgettimeofday() fix Andi Kleen
2007-04-29 10:53 ` [PATCH] [45/48] x86_64: Inhibit machine from asserting an NMI when doing Alt-SysRq-M operation Andi Kleen
2007-04-29 10:53 ` [PATCH] [46/48] x86_64: adjust EDID retrieval Andi Kleen
2007-04-29 12:18   ` Antonino A. Daplas
2007-04-29 10:53 ` [PATCH] [47/48] x86_64: Fix "Section mismatch" compile warning Andi Kleen
2007-04-29 10:53 ` [PATCH] [48/48] i386: cleanup GDT Access Andi Kleen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070429105325.0B1A0151C5@wotan.suse.de \
    --to=ak@suse.de \
    --cc=linux-kernel@vger.kernel.org \
    --cc=patches@x86-64.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®