diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2015-12-21 19:09:54 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-01-12 06:17:28 -0500 |
commit | e27d90e8be08d96b5e047cd01daf7e62c4fcab78 (patch) | |
tree | e5c6839715f611e51d82a90716f38adee52389f7 | |
parent | aa0421410fc540832f0fd267ff0f2657c3276053 (diff) |
lguest: Map switcher text R/O
Pavel noted that lguest maps the switcher code executable and
read-write. This is a bad idea for any kernel text, but
particularly for text mapped at a fixed address.
Create two vmas, one for the text (PAGE_KERNEL_RX) and another
for the stacks (PAGE_KERNEL). Use VM_NO_GUARD to map them
adjacent (as expected by the rest of the code).
Reported-by: Pavel Machek <pavel@ucw.cz>
Tested-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | arch/x86/include/asm/lguest.h | 4 | ||||
-rw-r--r-- | drivers/lguest/core.c | 74 |
2 files changed, 54 insertions, 24 deletions
diff --git a/arch/x86/include/asm/lguest.h b/arch/x86/include/asm/lguest.h index 3bbc07a57a31..73d0c9b92087 100644 --- a/arch/x86/include/asm/lguest.h +++ b/arch/x86/include/asm/lguest.h | |||
@@ -12,7 +12,9 @@ | |||
12 | #define GUEST_PL 1 | 12 | #define GUEST_PL 1 |
13 | 13 | ||
14 | /* Page for Switcher text itself, then two pages per cpu */ | 14 | /* Page for Switcher text itself, then two pages per cpu */ |
15 | #define TOTAL_SWITCHER_PAGES (1 + 2 * nr_cpu_ids) | 15 | #define SWITCHER_TEXT_PAGES (1) |
16 | #define SWITCHER_STACK_PAGES (2 * nr_cpu_ids) | ||
17 | #define TOTAL_SWITCHER_PAGES (SWITCHER_TEXT_PAGES + SWITCHER_STACK_PAGES) | ||
16 | 18 | ||
17 | /* Where we map the Switcher, in both Host and Guest. */ | 19 | /* Where we map the Switcher, in both Host and Guest. */ |
18 | extern unsigned long switcher_addr; | 20 | extern unsigned long switcher_addr; |
diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 312ffd3d0017..9e385b38debf 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c | |||
@@ -22,7 +22,8 @@ | |||
22 | 22 | ||
23 | unsigned long switcher_addr; | 23 | unsigned long switcher_addr; |
24 | struct page **lg_switcher_pages; | 24 | struct page **lg_switcher_pages; |
25 | static struct vm_struct *switcher_vma; | 25 | static struct vm_struct *switcher_text_vma; |
26 | static struct vm_struct *switcher_stacks_vma; | ||
26 | 27 | ||
27 | /* This One Big lock protects all inter-guest data structures. */ | 28 | /* This One Big lock protects all inter-guest data structures. */ |
28 | DEFINE_MUTEX(lguest_lock); | 29 | DEFINE_MUTEX(lguest_lock); |
@@ -83,54 +84,80 @@ static __init int map_switcher(void) | |||
83 | } | 84 | } |
84 | 85 | ||
85 | /* | 86 | /* |
87 | * Copy in the compiled-in Switcher code (from x86/switcher_32.S). | ||
88 | * It goes in the first page, which we map in momentarily. | ||
89 | */ | ||
90 | memcpy(kmap(lg_switcher_pages[0]), start_switcher_text, | ||
91 | end_switcher_text - start_switcher_text); | ||
92 | kunmap(lg_switcher_pages[0]); | ||
93 | |||
94 | /* | ||
86 | * We place the Switcher underneath the fixmap area, which is the | 95 | * We place the Switcher underneath the fixmap area, which is the |
87 | * highest virtual address we can get. This is important, since we | 96 | * highest virtual address we can get. This is important, since we |
88 | * tell the Guest it can't access this memory, so we want its ceiling | 97 | * tell the Guest it can't access this memory, so we want its ceiling |
89 | * as high as possible. | 98 | * as high as possible. |
90 | */ | 99 | */ |
91 | switcher_addr = FIXADDR_START - (TOTAL_SWITCHER_PAGES+1)*PAGE_SIZE; | 100 | switcher_addr = FIXADDR_START - TOTAL_SWITCHER_PAGES*PAGE_SIZE; |
92 | 101 | ||
93 | /* | 102 | /* |
94 | * Now we reserve the "virtual memory area" we want. We might | 103 | * Now we reserve the "virtual memory area"s we want. We might |
95 | * not get it in theory, but in practice it's worked so far. | 104 | * not get them in theory, but in practice it's worked so far. |
96 | * The end address needs +1 because __get_vm_area allocates an | 105 | * |
97 | * extra guard page, so we need space for that. | 106 | * We want the switcher text to be read-only and executable, and |
107 | * the stacks to be read-write and non-executable. | ||
98 | */ | 108 | */ |
99 | switcher_vma = __get_vm_area(TOTAL_SWITCHER_PAGES * PAGE_SIZE, | 109 | switcher_text_vma = __get_vm_area(PAGE_SIZE, VM_ALLOC|VM_NO_GUARD, |
100 | VM_ALLOC, switcher_addr, switcher_addr | 110 | switcher_addr, |
101 | + (TOTAL_SWITCHER_PAGES+1) * PAGE_SIZE); | 111 | switcher_addr + PAGE_SIZE); |
102 | if (!switcher_vma) { | 112 | |
113 | if (!switcher_text_vma) { | ||
103 | err = -ENOMEM; | 114 | err = -ENOMEM; |
104 | printk("lguest: could not map switcher pages high\n"); | 115 | printk("lguest: could not map switcher pages high\n"); |
105 | goto free_pages; | 116 | goto free_pages; |
106 | } | 117 | } |
107 | 118 | ||
119 | switcher_stacks_vma = __get_vm_area(SWITCHER_STACK_PAGES * PAGE_SIZE, | ||
120 | VM_ALLOC|VM_NO_GUARD, | ||
121 | switcher_addr + PAGE_SIZE, | ||
122 | switcher_addr + TOTAL_SWITCHER_PAGES * PAGE_SIZE); | ||
123 | if (!switcher_stacks_vma) { | ||
124 | err = -ENOMEM; | ||
125 | printk("lguest: could not map switcher pages high\n"); | ||
126 | goto free_text_vma; | ||
127 | } | ||
128 | |||
108 | /* | 129 | /* |
109 | * This code actually sets up the pages we've allocated to appear at | 130 | * This code actually sets up the pages we've allocated to appear at |
110 | * switcher_addr. map_vm_area() takes the vma we allocated above, the | 131 | * switcher_addr. map_vm_area() takes the vma we allocated above, the |
111 | * kind of pages we're mapping (kernel pages), and a pointer to our | 132 | * kind of pages we're mapping (kernel text pages and kernel writable |
112 | * array of struct pages. | 133 | * pages respectively), and a pointer to our array of struct pages. |
113 | */ | 134 | */ |
114 | err = map_vm_area(switcher_vma, PAGE_KERNEL_EXEC, lg_switcher_pages); | 135 | err = map_vm_area(switcher_text_vma, PAGE_KERNEL_RX, lg_switcher_pages); |
136 | if (err) { | ||
137 | printk("lguest: text map_vm_area failed: %i\n", err); | ||
138 | goto free_vmas; | ||
139 | } | ||
140 | |||
141 | err = map_vm_area(switcher_stacks_vma, PAGE_KERNEL, | ||
142 | lg_switcher_pages + SWITCHER_TEXT_PAGES); | ||
115 | if (err) { | 143 | if (err) { |
116 | printk("lguest: map_vm_area failed: %i\n", err); | 144 | printk("lguest: stacks map_vm_area failed: %i\n", err); |
117 | goto free_vma; | 145 | goto free_vmas; |
118 | } | 146 | } |
119 | 147 | ||
120 | /* | 148 | /* |
121 | * Now the Switcher is mapped at the right address, we can't fail! | 149 | * Now the Switcher is mapped at the right address, we can't fail! |
122 | * Copy in the compiled-in Switcher code (from x86/switcher_32.S). | ||
123 | */ | 150 | */ |
124 | memcpy(switcher_vma->addr, start_switcher_text, | ||
125 | end_switcher_text - start_switcher_text); | ||
126 | |||
127 | printk(KERN_INFO "lguest: mapped switcher at %p\n", | 151 | printk(KERN_INFO "lguest: mapped switcher at %p\n", |
128 | switcher_vma->addr); | 152 | switcher_text_vma->addr); |
129 | /* And we succeeded... */ | 153 | /* And we succeeded... */ |
130 | return 0; | 154 | return 0; |
131 | 155 | ||
132 | free_vma: | 156 | free_vmas: |
133 | vunmap(switcher_vma->addr); | 157 | /* Undoes map_vm_area and __get_vm_area */ |
158 | vunmap(switcher_stacks_vma->addr); | ||
159 | free_text_vma: | ||
160 | vunmap(switcher_text_vma->addr); | ||
134 | free_pages: | 161 | free_pages: |
135 | i = TOTAL_SWITCHER_PAGES; | 162 | i = TOTAL_SWITCHER_PAGES; |
136 | free_some_pages: | 163 | free_some_pages: |
@@ -148,7 +175,8 @@ static void unmap_switcher(void) | |||
148 | unsigned int i; | 175 | unsigned int i; |
149 | 176 | ||
150 | /* vunmap() undoes *both* map_vm_area() and __get_vm_area(). */ | 177 | /* vunmap() undoes *both* map_vm_area() and __get_vm_area(). */ |
151 | vunmap(switcher_vma->addr); | 178 | vunmap(switcher_text_vma->addr); |
179 | vunmap(switcher_stacks_vma->addr); | ||
152 | /* Now we just need to free the pages we copied the switcher into */ | 180 | /* Now we just need to free the pages we copied the switcher into */ |
153 | for (i = 0; i < TOTAL_SWITCHER_PAGES; i++) | 181 | for (i = 0; i < TOTAL_SWITCHER_PAGES; i++) |
154 | __free_pages(lg_switcher_pages[i], 0); | 182 | __free_pages(lg_switcher_pages[i], 0); |