diff options
-rw-r--r-- | drivers/lguest/core.c | 5 | ||||
-rw-r--r-- | drivers/lguest/interrupts_and_traps.c | 9 | ||||
-rw-r--r-- | drivers/lguest/lguest.c | 5 | ||||
-rw-r--r-- | drivers/lguest/segments.c | 62 | ||||
-rw-r--r-- | drivers/lguest/switcher.S | 15 |
5 files changed, 29 insertions, 67 deletions
diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 0a46e8837d9a..4a315f08a567 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -453,6 +453,11 @@ static void run_guest_once(struct lguest *lg, struct lguest_pages *pages) * lguest_pages". */ copy_in_guest_info(lg, pages); + /* Set the trap number to 256 (impossible value). If we fault while + * switching to the Guest (bad segment registers or bug), this will + * cause us to abort the Guest. */ + lg->regs->trapnum = 256; + /* Now: we push the "eflags" register on the stack, then do an "lcall". * This is how we change from using the kernel code segment to using * the dedicated lguest code segment, as well as jumping into the diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 49787e964a0d..49aa55577d0d 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c @@ -195,13 +195,16 @@ static int has_err(unsigned int trap) /* deliver_trap() returns true if it could deliver the trap. */ int deliver_trap(struct lguest *lg, unsigned int num) { - u32 lo = lg->idt[num].a, hi = lg->idt[num].b; + /* Trap numbers are always 8 bit, but we set an impossible trap number + * for traps inside the Switcher, so check that here. */ + if (num >= ARRAY_SIZE(lg->idt)) + return 0; /* Early on the Guest hasn't set the IDT entries (or maybe it put a * bogus one in): if we fail here, the Guest will be killed. */ - if (!idt_present(lo, hi)) + if (!idt_present(lg->idt[num].a, lg->idt[num].b)) return 0; - set_guest_interrupt(lg, lo, hi, has_err(num)); + set_guest_interrupt(lg, lg->idt[num].a, lg->idt[num].b, has_err(num)); return 1; } diff --git a/drivers/lguest/lguest.c b/drivers/lguest/lguest.c index fb17d2757be9..524beea7fb19 100644 --- a/drivers/lguest/lguest.c +++ b/drivers/lguest/lguest.c @@ -323,9 +323,12 @@ static void lguest_write_gdt_entry(struct desc_struct *dt, * __thread variables). So we have a hypercall specifically for this case. */ static void lguest_load_tls(struct thread_struct *t, unsigned int cpu) { + /* There's one problem which normal hardware doesn't have: the Host + * can't handle us removing entries we're currently using. So we clear + * the GS register here: if it's needed it'll be reloaded anyway. */ + loadsegment(gs, 0); lazy_hcall(LHCALL_LOAD_TLS, __pa(&t->tls_array), cpu, 0); } -/*:*/ /*G:038 That's enough excitement for now, back to ploughing through each of * the paravirt_ops (we're about 1/3 of the way through). diff --git a/drivers/lguest/segments.c b/drivers/lguest/segments.c index f675a41a80da..9b81119f46e9 100644 --- a/drivers/lguest/segments.c +++ b/drivers/lguest/segments.c @@ -43,22 +43,6 @@ * begin. */ -/* Is the descriptor the Guest wants us to put in OK? - * - * The flag which Intel says must be zero: must be zero. The descriptor must - * be present, (this is actually checked earlier but is here for thorougness), - * and the descriptor type must be 1 (a memory segment). */ -static int desc_ok(const struct desc_struct *gdt) -{ - return ((gdt->b & 0x00209000) == 0x00009000); -} - -/* Is the segment present? (Otherwise it can't be used by the Guest). */ -static int segment_present(const struct desc_struct *gdt) -{ - return gdt->b & 0x8000; -} - /* There are several entries we don't let the Guest set. The TSS entry is the * "Task State Segment" which controls all kinds of delicate things. The * LGUEST_CS and LGUEST_DS entries are reserved for the Switcher, and the @@ -71,37 +55,11 @@ static int ignored_gdt(unsigned int num) || num == GDT_ENTRY_DOUBLEFAULT_TSS); } -/* If the Guest asks us to remove an entry from the GDT, we have to be careful. - * If one of the segment registers is pointing at that entry the Switcher will - * crash when it tries to reload the segment registers for the Guest. - * - * It doesn't make much sense for the Guest to try to remove its own code, data - * or stack segments while they're in use: assume that's a Guest bug. If it's - * one of the lesser segment registers using the removed entry, we simply set - * that register to 0 (unusable). */ -static void check_segment_use(struct lguest *lg, unsigned int desc) -{ - /* GDT entries are 8 bytes long, so we divide to get the index and - * ignore the bottom bits. */ - if (lg->regs->gs / 8 == desc) - lg->regs->gs = 0; - if (lg->regs->fs / 8 == desc) - lg->regs->fs = 0; - if (lg->regs->es / 8 == desc) - lg->regs->es = 0; - if (lg->regs->ds / 8 == desc - || lg->regs->cs / 8 == desc - || lg->regs->ss / 8 == desc) - kill_guest(lg, "Removed live GDT entry %u", desc); -} -/*:*/ -/*M:009 We wouldn't need to check for removal of in-use segments if we handled - * faults in the Switcher. However, it's probably not a worthwhile - * optimization. :*/ - -/*H:610 Once the GDT has been changed, we look through the changed entries and - * see if they're OK. If not, we'll call kill_guest() and the Guest will never - * get to use the invalid entries. */ +/*H:610 Once the GDT has been changed, we fix the new entries up a little. We + * don't care if they're invalid: the worst that can happen is a General + * Protection Fault in the Switcher when it restores a Guest segment register + * which tries to use that entry. Then we kill the Guest for causing such a + * mess: the message will be "unhandled trap 256". */ static void fixup_gdt_table(struct lguest *lg, unsigned start, unsigned end) { unsigned int i; @@ -112,16 +70,6 @@ static void fixup_gdt_table(struct lguest *lg, unsigned start, unsigned end) if (ignored_gdt(i)) continue; - /* We could fault in switch_to_guest if they are using - * a removed segment. */ - if (!segment_present(&lg->gdt[i])) { - check_segment_use(lg, i); - continue; - } - - if (!desc_ok(&lg->gdt[i])) - kill_guest(lg, "Bad GDT descriptor %i", i); - /* Segment descriptors contain a privilege level: the Guest is * sometimes careless and leaves this as 0, even though it's * running at privilege level 1. If so, we fix it here. */ diff --git a/drivers/lguest/switcher.S b/drivers/lguest/switcher.S index d418179ea6b5..7c9c230cc845 100644 --- a/drivers/lguest/switcher.S +++ b/drivers/lguest/switcher.S @@ -47,6 +47,7 @@ // Down here in the depths of assembler code. #include <linux/linkage.h> #include <asm/asm-offsets.h> +#include <asm/page.h> #include "lg.h" // We mark the start of the code to copy @@ -182,13 +183,15 @@ ENTRY(switch_to_guest) movl $(LGUEST_DS), %eax; \ movl %eax, %ds; \ /* So where are we? Which CPU, which struct? \ - * The stack is our clue: our TSS sets \ - * It at the end of "struct lguest_pages" \ - * And we then pushed and pushed and pushed Guest regs: \ - * Now stack points atop the "struct lguest_regs". \ - * Subtract that offset, and we find our struct. */ \ + * The stack is our clue: our TSS starts \ + * It at the end of "struct lguest_pages". \ + * Or we may have stumbled while restoring \ + * Our Guest segment regs while in switch_to_guest, \ + * The fault pushed atop that part-unwound stack. \ + * If we round the stack down to the page start \ + * We're at the start of "struct lguest_pages". */ \ movl %esp, %eax; \ - subl $LGUEST_PAGES_regs, %eax; \ + andl $(~(1 << PAGE_SHIFT - 1)), %eax; \ /* Save our trap number: the switch will obscure it \ * (The Guest regs are not mapped here in the Host) \ * %ebx holds it safe for deliver_to_host */ \ |