aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2015-12-21 19:09:54 -0500
committerIngo Molnar <mingo@kernel.org>2016-01-12 06:17:28 -0500
commite27d90e8be08d96b5e047cd01daf7e62c4fcab78 (patch)
treee5c6839715f611e51d82a90716f38adee52389f7
parentaa0421410fc540832f0fd267ff0f2657c3276053 (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.h4
-rw-r--r--drivers/lguest/core.c74
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. */
18extern unsigned long switcher_addr; 20extern 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
23unsigned long switcher_addr; 23unsigned long switcher_addr;
24struct page **lg_switcher_pages; 24struct page **lg_switcher_pages;
25static struct vm_struct *switcher_vma; 25static struct vm_struct *switcher_text_vma;
26static 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. */
28DEFINE_MUTEX(lguest_lock); 29DEFINE_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
132free_vma: 156free_vmas:
133 vunmap(switcher_vma->addr); 157 /* Undoes map_vm_area and __get_vm_area */
158 vunmap(switcher_stacks_vma->addr);
159free_text_vma:
160 vunmap(switcher_text_vma->addr);
134free_pages: 161free_pages:
135 i = TOTAL_SWITCHER_PAGES; 162 i = TOTAL_SWITCHER_PAGES;
136free_some_pages: 163free_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);