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>
This commit is contained in:
Родитель
aa0421410f
Коммит
e27d90e8be
|
@ -12,7 +12,9 @@
|
||||||
#define GUEST_PL 1
|
#define GUEST_PL 1
|
||||||
|
|
||||||
/* Page for Switcher text itself, then two pages per cpu */
|
/* Page for Switcher text itself, then two pages per cpu */
|
||||||
#define TOTAL_SWITCHER_PAGES (1 + 2 * nr_cpu_ids)
|
#define SWITCHER_TEXT_PAGES (1)
|
||||||
|
#define SWITCHER_STACK_PAGES (2 * nr_cpu_ids)
|
||||||
|
#define TOTAL_SWITCHER_PAGES (SWITCHER_TEXT_PAGES + SWITCHER_STACK_PAGES)
|
||||||
|
|
||||||
/* Where we map the Switcher, in both Host and Guest. */
|
/* Where we map the Switcher, in both Host and Guest. */
|
||||||
extern unsigned long switcher_addr;
|
extern unsigned long switcher_addr;
|
||||||
|
|
|
@ -22,7 +22,8 @@
|
||||||
|
|
||||||
unsigned long switcher_addr;
|
unsigned long switcher_addr;
|
||||||
struct page **lg_switcher_pages;
|
struct page **lg_switcher_pages;
|
||||||
static struct vm_struct *switcher_vma;
|
static struct vm_struct *switcher_text_vma;
|
||||||
|
static struct vm_struct *switcher_stacks_vma;
|
||||||
|
|
||||||
/* This One Big lock protects all inter-guest data structures. */
|
/* This One Big lock protects all inter-guest data structures. */
|
||||||
DEFINE_MUTEX(lguest_lock);
|
DEFINE_MUTEX(lguest_lock);
|
||||||
|
@ -82,55 +83,81 @@ static __init int map_switcher(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy in the compiled-in Switcher code (from x86/switcher_32.S).
|
||||||
|
* It goes in the first page, which we map in momentarily.
|
||||||
|
*/
|
||||||
|
memcpy(kmap(lg_switcher_pages[0]), start_switcher_text,
|
||||||
|
end_switcher_text - start_switcher_text);
|
||||||
|
kunmap(lg_switcher_pages[0]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We place the Switcher underneath the fixmap area, which is the
|
* We place the Switcher underneath the fixmap area, which is the
|
||||||
* highest virtual address we can get. This is important, since we
|
* highest virtual address we can get. This is important, since we
|
||||||
* tell the Guest it can't access this memory, so we want its ceiling
|
* tell the Guest it can't access this memory, so we want its ceiling
|
||||||
* as high as possible.
|
* as high as possible.
|
||||||
*/
|
*/
|
||||||
switcher_addr = FIXADDR_START - (TOTAL_SWITCHER_PAGES+1)*PAGE_SIZE;
|
switcher_addr = FIXADDR_START - TOTAL_SWITCHER_PAGES*PAGE_SIZE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now we reserve the "virtual memory area" we want. We might
|
* Now we reserve the "virtual memory area"s we want. We might
|
||||||
* not get it in theory, but in practice it's worked so far.
|
* not get them in theory, but in practice it's worked so far.
|
||||||
* The end address needs +1 because __get_vm_area allocates an
|
*
|
||||||
* extra guard page, so we need space for that.
|
* We want the switcher text to be read-only and executable, and
|
||||||
|
* the stacks to be read-write and non-executable.
|
||||||
*/
|
*/
|
||||||
switcher_vma = __get_vm_area(TOTAL_SWITCHER_PAGES * PAGE_SIZE,
|
switcher_text_vma = __get_vm_area(PAGE_SIZE, VM_ALLOC|VM_NO_GUARD,
|
||||||
VM_ALLOC, switcher_addr, switcher_addr
|
switcher_addr,
|
||||||
+ (TOTAL_SWITCHER_PAGES+1) * PAGE_SIZE);
|
switcher_addr + PAGE_SIZE);
|
||||||
if (!switcher_vma) {
|
|
||||||
|
if (!switcher_text_vma) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
printk("lguest: could not map switcher pages high\n");
|
printk("lguest: could not map switcher pages high\n");
|
||||||
goto free_pages;
|
goto free_pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switcher_stacks_vma = __get_vm_area(SWITCHER_STACK_PAGES * PAGE_SIZE,
|
||||||
|
VM_ALLOC|VM_NO_GUARD,
|
||||||
|
switcher_addr + PAGE_SIZE,
|
||||||
|
switcher_addr + TOTAL_SWITCHER_PAGES * PAGE_SIZE);
|
||||||
|
if (!switcher_stacks_vma) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
printk("lguest: could not map switcher pages high\n");
|
||||||
|
goto free_text_vma;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This code actually sets up the pages we've allocated to appear at
|
* This code actually sets up the pages we've allocated to appear at
|
||||||
* switcher_addr. map_vm_area() takes the vma we allocated above, the
|
* switcher_addr. map_vm_area() takes the vma we allocated above, the
|
||||||
* kind of pages we're mapping (kernel pages), and a pointer to our
|
* kind of pages we're mapping (kernel text pages and kernel writable
|
||||||
* array of struct pages.
|
* pages respectively), and a pointer to our array of struct pages.
|
||||||
*/
|
*/
|
||||||
err = map_vm_area(switcher_vma, PAGE_KERNEL_EXEC, lg_switcher_pages);
|
err = map_vm_area(switcher_text_vma, PAGE_KERNEL_RX, lg_switcher_pages);
|
||||||
if (err) {
|
if (err) {
|
||||||
printk("lguest: map_vm_area failed: %i\n", err);
|
printk("lguest: text map_vm_area failed: %i\n", err);
|
||||||
goto free_vma;
|
goto free_vmas;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = map_vm_area(switcher_stacks_vma, PAGE_KERNEL,
|
||||||
|
lg_switcher_pages + SWITCHER_TEXT_PAGES);
|
||||||
|
if (err) {
|
||||||
|
printk("lguest: stacks map_vm_area failed: %i\n", err);
|
||||||
|
goto free_vmas;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now the Switcher is mapped at the right address, we can't fail!
|
* Now the Switcher is mapped at the right address, we can't fail!
|
||||||
* Copy in the compiled-in Switcher code (from x86/switcher_32.S).
|
|
||||||
*/
|
*/
|
||||||
memcpy(switcher_vma->addr, start_switcher_text,
|
|
||||||
end_switcher_text - start_switcher_text);
|
|
||||||
|
|
||||||
printk(KERN_INFO "lguest: mapped switcher at %p\n",
|
printk(KERN_INFO "lguest: mapped switcher at %p\n",
|
||||||
switcher_vma->addr);
|
switcher_text_vma->addr);
|
||||||
/* And we succeeded... */
|
/* And we succeeded... */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free_vma:
|
free_vmas:
|
||||||
vunmap(switcher_vma->addr);
|
/* Undoes map_vm_area and __get_vm_area */
|
||||||
|
vunmap(switcher_stacks_vma->addr);
|
||||||
|
free_text_vma:
|
||||||
|
vunmap(switcher_text_vma->addr);
|
||||||
free_pages:
|
free_pages:
|
||||||
i = TOTAL_SWITCHER_PAGES;
|
i = TOTAL_SWITCHER_PAGES;
|
||||||
free_some_pages:
|
free_some_pages:
|
||||||
|
@ -148,7 +175,8 @@ static void unmap_switcher(void)
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
/* vunmap() undoes *both* map_vm_area() and __get_vm_area(). */
|
/* vunmap() undoes *both* map_vm_area() and __get_vm_area(). */
|
||||||
vunmap(switcher_vma->addr);
|
vunmap(switcher_text_vma->addr);
|
||||||
|
vunmap(switcher_stacks_vma->addr);
|
||||||
/* Now we just need to free the pages we copied the switcher into */
|
/* Now we just need to free the pages we copied the switcher into */
|
||||||
for (i = 0; i < TOTAL_SWITCHER_PAGES; i++)
|
for (i = 0; i < TOTAL_SWITCHER_PAGES; i++)
|
||||||
__free_pages(lg_switcher_pages[i], 0);
|
__free_pages(lg_switcher_pages[i], 0);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче