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
next prev 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®