Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Rusty Russell and committed by
Ingo Molnar
e27d90e8 aa042141

+54 -24
+3 -1
arch/x86/include/asm/lguest.h
··· 12 12 #define GUEST_PL 1 13 13 14 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 19 /* Where we map the Switcher, in both Host and Guest. */ 18 20 extern unsigned long switcher_addr;
+51 -23
drivers/lguest/core.c
··· 22 22 23 23 unsigned long switcher_addr; 24 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 28 /* This One Big lock protects all inter-guest data structures. */ 28 29 DEFINE_MUTEX(lguest_lock); ··· 84 83 } 85 84 86 85 /* 86 + * Copy in the compiled-in Switcher code (from x86/switcher_32.S). 87 + * It goes in the first page, which we map in momentarily. 88 + */ 89 + memcpy(kmap(lg_switcher_pages[0]), start_switcher_text, 90 + end_switcher_text - start_switcher_text); 91 + kunmap(lg_switcher_pages[0]); 92 + 93 + /* 87 94 * We place the Switcher underneath the fixmap area, which is the 88 95 * highest virtual address we can get. This is important, since we 89 96 * tell the Guest it can't access this memory, so we want its ceiling 90 97 * as high as possible. 91 98 */ 92 - switcher_addr = FIXADDR_START - (TOTAL_SWITCHER_PAGES+1)*PAGE_SIZE; 99 + switcher_addr = FIXADDR_START - TOTAL_SWITCHER_PAGES*PAGE_SIZE; 93 100 94 101 /* 95 - * Now we reserve the "virtual memory area" we want. We might 96 - * not get it in theory, but in practice it's worked so far. 97 - * The end address needs +1 because __get_vm_area allocates an 98 - * extra guard page, so we need space for that. 102 + * Now we reserve the "virtual memory area"s we want. We might 103 + * not get them in theory, but in practice it's worked so far. 104 + * 105 + * We want the switcher text to be read-only and executable, and 106 + * the stacks to be read-write and non-executable. 99 107 */ 100 - switcher_vma = __get_vm_area(TOTAL_SWITCHER_PAGES * PAGE_SIZE, 101 - VM_ALLOC, switcher_addr, switcher_addr 102 - + (TOTAL_SWITCHER_PAGES+1) * PAGE_SIZE); 103 - if (!switcher_vma) { 108 + switcher_text_vma = __get_vm_area(PAGE_SIZE, VM_ALLOC|VM_NO_GUARD, 109 + switcher_addr, 110 + switcher_addr + PAGE_SIZE); 111 + 112 + if (!switcher_text_vma) { 104 113 err = -ENOMEM; 105 114 printk("lguest: could not map switcher pages high\n"); 106 115 goto free_pages; 107 116 } 108 117 118 + switcher_stacks_vma = __get_vm_area(SWITCHER_STACK_PAGES * PAGE_SIZE, 119 + VM_ALLOC|VM_NO_GUARD, 120 + switcher_addr + PAGE_SIZE, 121 + switcher_addr + TOTAL_SWITCHER_PAGES * PAGE_SIZE); 122 + if (!switcher_stacks_vma) { 123 + err = -ENOMEM; 124 + printk("lguest: could not map switcher pages high\n"); 125 + goto free_text_vma; 126 + } 127 + 109 128 /* 110 129 * This code actually sets up the pages we've allocated to appear at 111 130 * switcher_addr. map_vm_area() takes the vma we allocated above, the 112 - * kind of pages we're mapping (kernel pages), and a pointer to our 113 - * array of struct pages. 131 + * kind of pages we're mapping (kernel text pages and kernel writable 132 + * pages respectively), and a pointer to our array of struct pages. 114 133 */ 115 - err = map_vm_area(switcher_vma, PAGE_KERNEL_EXEC, lg_switcher_pages); 134 + err = map_vm_area(switcher_text_vma, PAGE_KERNEL_RX, lg_switcher_pages); 116 135 if (err) { 117 - printk("lguest: map_vm_area failed: %i\n", err); 118 - goto free_vma; 136 + printk("lguest: text map_vm_area failed: %i\n", err); 137 + goto free_vmas; 138 + } 139 + 140 + err = map_vm_area(switcher_stacks_vma, PAGE_KERNEL, 141 + lg_switcher_pages + SWITCHER_TEXT_PAGES); 142 + if (err) { 143 + printk("lguest: stacks map_vm_area failed: %i\n", err); 144 + goto free_vmas; 119 145 } 120 146 121 147 /* 122 148 * Now the Switcher is mapped at the right address, we can't fail! 123 - * Copy in the compiled-in Switcher code (from x86/switcher_32.S). 124 149 */ 125 - memcpy(switcher_vma->addr, start_switcher_text, 126 - end_switcher_text - start_switcher_text); 127 - 128 150 printk(KERN_INFO "lguest: mapped switcher at %p\n", 129 - switcher_vma->addr); 151 + switcher_text_vma->addr); 130 152 /* And we succeeded... */ 131 153 return 0; 132 154 133 - free_vma: 134 - vunmap(switcher_vma->addr); 155 + free_vmas: 156 + /* Undoes map_vm_area and __get_vm_area */ 157 + vunmap(switcher_stacks_vma->addr); 158 + free_text_vma: 159 + vunmap(switcher_text_vma->addr); 135 160 free_pages: 136 161 i = TOTAL_SWITCHER_PAGES; 137 162 free_some_pages: ··· 175 148 unsigned int i; 176 149 177 150 /* vunmap() undoes *both* map_vm_area() and __get_vm_area(). */ 178 - vunmap(switcher_vma->addr); 151 + vunmap(switcher_text_vma->addr); 152 + vunmap(switcher_stacks_vma->addr); 179 153 /* Now we just need to free the pages we copied the switcher into */ 180 154 for (i = 0; i < TOTAL_SWITCHER_PAGES; i++) 181 155 __free_pages(lg_switcher_pages[i], 0);