| From aa1acff356bbedfd03b544051f5b371746735d89 Mon Sep 17 00:00:00 2001 |
| From: Andy Lutomirski <luto@kernel.org> |
| Date: Thu, 30 Jul 2015 14:31:31 -0700 |
| Subject: x86/xen: Probe target addresses in set_aliased_prot() before the |
| hypercall |
| |
| commit aa1acff356bbedfd03b544051f5b371746735d89 upstream. |
| |
| The update_va_mapping hypercall can fail if the VA isn't present |
| in the guest's page tables. Under certain loads, this can |
| result in an OOPS when the target address is in unpopulated vmap |
| space. |
| |
| While we're at it, add comments to help explain what's going on. |
| |
| This isn't a great long-term fix. This code should probably be |
| changed to use something like set_memory_ro. |
| |
| Signed-off-by: Andy Lutomirski <luto@kernel.org> |
| Cc: Andrew Cooper <andrew.cooper3@citrix.com> |
| Cc: Andy Lutomirski <luto@amacapital.net> |
| Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com> |
| Cc: Borislav Petkov <bp@alien8.de> |
| Cc: Brian Gerst <brgerst@gmail.com> |
| Cc: David Vrabel <dvrabel@cantab.net> |
| Cc: Denys Vlasenko <dvlasenk@redhat.com> |
| Cc: H. Peter Anvin <hpa@zytor.com> |
| Cc: Jan Beulich <jbeulich@suse.com> |
| Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> |
| Cc: Linus Torvalds <torvalds@linux-foundation.org> |
| Cc: Peter Zijlstra <peterz@infradead.org> |
| Cc: Sasha Levin <sasha.levin@oracle.com> |
| Cc: Steven Rostedt <rostedt@goodmis.org> |
| Cc: Thomas Gleixner <tglx@linutronix.de> |
| Cc: security@kernel.org <security@kernel.org> |
| Cc: xen-devel <xen-devel@lists.xen.org> |
| Link: http://lkml.kernel.org/r/0b0e55b995cda11e7829f140b833ef932fcabe3a.1438291540.git.luto@kernel.org |
| Signed-off-by: Ingo Molnar <mingo@kernel.org> |
| Signed-off-by: Zefan Li <lizefan@huawei.com> |
| --- |
| arch/x86/xen/enlighten.c | 40 ++++++++++++++++++++++++++++++++++++++++ |
| 1 file changed, 40 insertions(+) |
| |
| --- a/arch/x86/xen/enlighten.c |
| +++ b/arch/x86/xen/enlighten.c |
| @@ -413,6 +413,7 @@ static void set_aliased_prot(void *v, pg |
| pte_t pte; |
| unsigned long pfn; |
| struct page *page; |
| + unsigned char dummy; |
| |
| ptep = lookup_address((unsigned long)v, &level); |
| BUG_ON(ptep == NULL); |
| @@ -422,6 +423,32 @@ static void set_aliased_prot(void *v, pg |
| |
| pte = pfn_pte(pfn, prot); |
| |
| + /* |
| + * Careful: update_va_mapping() will fail if the virtual address |
| + * we're poking isn't populated in the page tables. We don't |
| + * need to worry about the direct map (that's always in the page |
| + * tables), but we need to be careful about vmap space. In |
| + * particular, the top level page table can lazily propagate |
| + * entries between processes, so if we've switched mms since we |
| + * vmapped the target in the first place, we might not have the |
| + * top-level page table entry populated. |
| + * |
| + * We disable preemption because we want the same mm active when |
| + * we probe the target and when we issue the hypercall. We'll |
| + * have the same nominal mm, but if we're a kernel thread, lazy |
| + * mm dropping could change our pgd. |
| + * |
| + * Out of an abundance of caution, this uses __get_user() to fault |
| + * in the target address just in case there's some obscure case |
| + * in which the target address isn't readable. |
| + */ |
| + |
| + preempt_disable(); |
| + |
| + pagefault_disable(); /* Avoid warnings due to being atomic. */ |
| + __get_user(dummy, (unsigned char __user __force *)v); |
| + pagefault_enable(); |
| + |
| if (HYPERVISOR_update_va_mapping((unsigned long)v, pte, 0)) |
| BUG(); |
| |
| @@ -433,6 +460,8 @@ static void set_aliased_prot(void *v, pg |
| BUG(); |
| } else |
| kmap_flush_unused(); |
| + |
| + preempt_enable(); |
| } |
| |
| static void xen_alloc_ldt(struct desc_struct *ldt, unsigned entries) |
| @@ -440,6 +469,17 @@ static void xen_alloc_ldt(struct desc_st |
| const unsigned entries_per_page = PAGE_SIZE / LDT_ENTRY_SIZE; |
| int i; |
| |
| + /* |
| + * We need to mark the all aliases of the LDT pages RO. We |
| + * don't need to call vm_flush_aliases(), though, since that's |
| + * only responsible for flushing aliases out the TLBs, not the |
| + * page tables, and Xen will flush the TLB for us if needed. |
| + * |
| + * To avoid confusing future readers: none of this is necessary |
| + * to load the LDT. The hypervisor only checks this when the |
| + * LDT is faulted in due to subsequent descriptor access. |
| + */ |
| + |
| for(i = 0; i < entries; i += entries_per_page) |
| set_aliased_prot(ldt + i, PAGE_KERNEL_RO); |
| } |