From patchwork Tue Feb 14 19:42:58 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Garnier X-Patchwork-Id: 9572653 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E754D60578 for ; Tue, 14 Feb 2017 19:45:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E3F4228391 for ; Tue, 14 Feb 2017 19:45:41 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D7E5D283A6; Tue, 14 Feb 2017 19:45:41 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-3.6 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, RCVD_IN_DNSWL_MED, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id EF70A28394 for ; Tue, 14 Feb 2017 19:45:39 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cdj0L-00023Y-Fj; Tue, 14 Feb 2017 19:43:13 +0000 Received: from mail6.bemta3.messagelabs.com ([195.245.230.39]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cdj0J-00022V-Na for xen-devel@lists.xenproject.org; Tue, 14 Feb 2017 19:43:11 +0000 Received: from [85.158.137.68] by server-4.bemta-3.messagelabs.com id 9F/A0-01392-ECD53A85; Tue, 14 Feb 2017 19:43:10 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprPIsWRWlGSWpSXmKPExsXiVRu0Ufds7OI IgxlnLCy+b5nM5MDocfjDFZYAxijWzLyk/IoE1oxbU18xFSx3rrgwqZ25gbHHsouRi0NIYCaj RPvZlSwgDovAKxaJOe8/soI4EgL9rBIbZh9h6mLkBHJiJLZtOA1lV0n8ad0CZgsJKEls3bCUG WLUf0aJoweeMIMk2AS0JPY0zGcCSYgIzOGReLllDViCWeAPo8SMneYgtrCAu8Slrz1gcRYBVY n7/z4DNXBw8ApYSOzYGwGxzFjidv9pVpAwp4ClxJa3gRB7LST+v/vEOoFRYAEjwypG9eLUorL UIl0TvaSizPSMktzEzBxdQwNjvdzU4uLE9NScxKRiveT83E2MwMBiAIIdjI1fnA4xSnIwKYny VgQtjhDiS8pPqcxILM6ILyrNSS0+xCjDwaEkwcsUA5QTLEpNT61Iy8wBhjhMWoKDR0mEdwpIm re4IDG3ODMdInWKUZdj167LL5mEWPLy81KlxHltQYoEQIoySvPgRsDi7RKjrJQwLyPQUUI8Ba lFuZklqPKvGMU5GJWEefNBpvBk5pXAbXoFdAQT0BGscQtBjihJREhJNTD6T9vp7rvkd+LUbTN Ffs1MtNe0LJh/QTtkr9qJM1wzxTq4VMWFznvaizv/ebv2aGfQldmXZ3E9vhiqsJx7buePXZu+ 2szp3uuz+8W9ldrpWRWqj3QuBCzapnz5yq60KTtyr09nF7vb3Cj3mq1RRJtnLffaLdVfldctm nTI5qzy9OZzsWefOH7apcRSnJFoqMVcVJwIANZWCH6yAgAA X-Env-Sender: thgarnie@google.com X-Msg-Ref: server-14.tower-31.messagelabs.com!1487101388!85578607!1 X-Originating-IP: [74.125.82.177] X-SpamReason: No, hits=0.0 required=7.0 tests= X-StarScan-Received: X-StarScan-Version: 9.2.3; banners=-,-,- X-VirusChecked: Checked Received: (qmail 62611 invoked from network); 14 Feb 2017 19:43:09 -0000 Received: from mail-ot0-f177.google.com (HELO mail-ot0-f177.google.com) (74.125.82.177) by server-14.tower-31.messagelabs.com with AES128-GCM-SHA256 encrypted SMTP; 14 Feb 2017 19:43:09 -0000 Received: by mail-ot0-f177.google.com with SMTP id t47so17309549ota.1 for ; Tue, 14 Feb 2017 11:43:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=+gJzzJZrSmhV4Q0q5WqTgsgRJfcZZeSHtNl3qagcclU=; b=i+1WFnA5ZipfTTZe4YcwVEaTjyF0gBN7Y80P1i6Xb8lincVVC3qHCxbRMhW4NqFlRg 4CJ7l6fWY+XZM3DcjO27nIo/IqxFss492McY1v7I3jDI/mKNOb30pPibNNhSESRBndyj w9m/chS27oAZsTXVW72BAJQjIKQicTJxNi7BGIguc68p5iVhPn9VFRw1C7ZEVClIEYGw AkJzTotv5YTl5tIcE5H9NoIoM7PScyeBwKM8s+k080J0LzN1XuuK5tsBxzu5mgIbXFgI XJMyzdDVJII/mikXfbzubwKy3YnKz8uDcTjERqQCbvGjXzX62j5zR28S1Ne0KF9i0XYp P9cA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=+gJzzJZrSmhV4Q0q5WqTgsgRJfcZZeSHtNl3qagcclU=; b=fGJ+GsO/wtwf7bVT5lHGCXdLrsQnnDzEPwJyRW5OrZ0dB7Xs4ST4CZnJA9s0MUQAd5 NswYKXPEnaEz+pCwhxT1EhKkl1PSqXNN/RXQWUu82ZRumVISLNqy/N3gBAVe/lW3s8bR IqiBc04KqXvL/OlLcvNEBnXnsGQc6Xdcd7pcUmbcInVpy7e8kpY1VreUEwPFxUuwNM2z FdrkX8COFxEmGEryi1UNG+f2Ys/dtbl8LBAPa2LxQtw2+lfE5aVyFDUjM7BHFdSxKM41 PD5KvH7J+3kylN/9YiRa6NabwIfkEn80NRLC9/yap3OfEJqg/4c2EMNTcmKJwmpbc44V RR+Q== X-Gm-Message-State: AMke39mg/dUTlCkDZjjS3nSnY2LGku/5XvUEZUv85YLxNUy7f86mctTy7wa1qi4VAU4yxIg+ X-Received: by 10.98.82.216 with SMTP id g207mr33034235pfb.32.1487101387975; Tue, 14 Feb 2017 11:43:07 -0800 (PST) Received: from skynet.sea.corp.google.com ([100.100.206.185]) by smtp.gmail.com with ESMTPSA id r78sm2804161pfl.63.2017.02.14.11.43.06 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 14 Feb 2017 11:43:07 -0800 (PST) From: Thomas Garnier To: Thomas Gleixner , Ingo Molnar , "H . Peter Anvin" , Andrey Ryabinin , Alexander Potapenko , Dmitry Vyukov , Thomas Garnier , Kees Cook , Andy Lutomirski , Borislav Petkov , Paul Gortmaker , Andy Lutomirski , "Rafael J . Wysocki" , Len Brown , Pavel Machek , Jiri Kosina , Matt Fleming , Ard Biesheuvel , Boris Ostrovsky , Juergen Gross , Rusty Russell , Peter Zijlstra , Christian Borntraeger , "Luis R . Rodriguez" , He Chen , Brian Gerst , Stanislaw Gruszka , Arnd Bergmann , Adam Buchbinder , Dave Hansen , Vitaly Kuznetsov , Josh Poimboeuf , Tim Chen , Rik van Riel , Andi Kleen , Jiri Olsa , Michael Ellerman , Joerg Roedel , Paolo Bonzini , =?UTF-8?q?Radim=20Kr=C4=8Dm=C3=A1=C5=99?= Date: Tue, 14 Feb 2017 11:42:58 -0800 Message-Id: <20170214194259.75960-3-thgarnie@google.com> X-Mailer: git-send-email 2.11.0.483.g087da7b7c-goog In-Reply-To: <20170214194259.75960-1-thgarnie@google.com> References: <20170214194259.75960-1-thgarnie@google.com> Cc: linux-efi@vger.kernel.org, kvm@vger.kernel.org, linux-pm@vger.kernel.org, x86@kernel.org, linux-kernel@vger.kernel.org, kasan-dev@googlegroups.com, lguest@lists.ozlabs.org, kernel-hardening@lists.openwall.com, xen-devel@lists.xenproject.org Subject: [Xen-devel] [PATCH v3 3/4] x86: Make the GDT remapping read-only on 64-bit X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP This patch makes the GDT remapped pages read-only to prevent corruption. This change is done only on 64-bit. The native_load_tr_desc function was adapted to correctly handle a read-only GDT. The LTR instruction always writes to the GDT TSS entry. This generates a page fault if the GDT is read-only. This change checks if the current GDT is a remap and swap GDTs as needed. This function was tested by booting multiple machines and checking hibernation works properly. KVM SVM and VMX were adapted to use the writeable GDT. On VMX, the per-cpu variable was removed for functions to fetch the original GDT. Instead of reloading the previous GDT, VMX will reload the fixmap GDT as expected. For testing, VMs were started and restored on multiple configurations. Signed-off-by: Thomas Garnier --- Based on next-20170213 --- arch/x86/include/asm/desc.h | 51 ++++++++++++++++++++++++++++++++++++---- arch/x86/include/asm/processor.h | 1 + arch/x86/kernel/cpu/common.c | 28 +++++++++++++++++----- arch/x86/kvm/svm.c | 4 +--- arch/x86/kvm/vmx.c | 15 ++++-------- 5 files changed, 75 insertions(+), 24 deletions(-) diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h index 5d4ba1311737..15b2a86c9267 100644 --- a/arch/x86/include/asm/desc.h +++ b/arch/x86/include/asm/desc.h @@ -57,6 +57,17 @@ static inline unsigned long get_cpu_gdt_rw_vaddr(unsigned int cpu) return (unsigned long)get_cpu_gdt_rw(cpu); } +/* Provide the current original GDT */ +static inline struct desc_struct *get_current_gdt_rw(void) +{ + return this_cpu_ptr(&gdt_page)->gdt; +} + +static inline unsigned long get_current_gdt_rw_vaddr(void) +{ + return (unsigned long)get_current_gdt_rw(); +} + /* Get the fixmap index for a specific processor */ static inline unsigned int get_cpu_gdt_ro_index(int cpu) { @@ -233,11 +244,6 @@ static inline void native_set_ldt(const void *addr, unsigned int entries) } } -static inline void native_load_tr_desc(void) -{ - asm volatile("ltr %w0"::"q" (GDT_ENTRY_TSS*8)); -} - static inline void native_load_gdt(const struct desc_ptr *dtr) { asm volatile("lgdt %0"::"m" (*dtr)); @@ -258,6 +264,41 @@ static inline void native_store_idt(struct desc_ptr *dtr) asm volatile("sidt %0":"=m" (*dtr)); } +/* + * The LTR instruction marks the TSS GDT entry as busy. On 64-bit, the GDT is + * a read-only remapping. To prevent a page fault, the GDT is switched to the + * original writeable version when needed. + */ +#ifdef CONFIG_X86_64 +static inline void native_load_tr_desc(void) +{ + struct desc_ptr gdt; + int cpu = raw_smp_processor_id(); + bool restore = 0; + struct desc_struct *fixmap_gdt; + + native_store_gdt(&gdt); + fixmap_gdt = get_cpu_gdt_ro(cpu); + + /* + * If the current GDT is the read-only fixmap, swap to the original + * writeable version. Swap back at the end. + */ + if (gdt.address == (unsigned long)fixmap_gdt) { + load_direct_gdt(cpu); + restore = 1; + } + asm volatile("ltr %w0"::"q" (GDT_ENTRY_TSS*8)); + if (restore) + load_fixmap_gdt(cpu); +} +#else +static inline void native_load_tr_desc(void) +{ + asm volatile("ltr %w0"::"q" (GDT_ENTRY_TSS*8)); +} +#endif + static inline unsigned long native_store_tr(void) { unsigned long tr; diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index c441d1f7e275..6ea9e419a856 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -706,6 +706,7 @@ extern struct desc_ptr early_gdt_descr; extern void cpu_set_gdt(int); extern void switch_to_new_gdt(int); +extern void load_direct_gdt(int); extern void load_fixmap_gdt(int); extern void load_percpu_segment(int); extern void cpu_init(void); diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 2853a42ded2d..bdf521383900 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -444,13 +444,31 @@ void load_percpu_segment(int cpu) load_stack_canary_segment(); } +/* On 64-bit the GDT remapping is read-only */ +#ifdef CONFIG_X86_64 +#define PAGE_FIXMAP_GDT PAGE_KERNEL_RO +#else +#define PAGE_FIXMAP_GDT PAGE_KERNEL +#endif + /* Setup the fixmap mapping only once per-processor */ static inline void setup_fixmap_gdt(int cpu) { __set_fixmap(get_cpu_gdt_ro_index(cpu), - __pa(get_cpu_gdt_rw(cpu)), PAGE_KERNEL); + __pa(get_cpu_gdt_rw(cpu)), PAGE_FIXMAP_GDT); } +/* Load the original GDT from the per-cpu structure */ +void load_direct_gdt(int cpu) +{ + struct desc_ptr gdt_descr; + + gdt_descr.address = (long)get_cpu_gdt_rw(cpu); + gdt_descr.size = GDT_SIZE - 1; + load_gdt(&gdt_descr); +} +EXPORT_SYMBOL_GPL(load_direct_gdt); + /* Load a fixmap remapping of the per-cpu GDT */ void load_fixmap_gdt(int cpu) { @@ -460,6 +478,7 @@ void load_fixmap_gdt(int cpu) gdt_descr.size = GDT_SIZE - 1; load_gdt(&gdt_descr); } +EXPORT_SYMBOL_GPL(load_fixmap_gdt); /* * Current gdt points %fs at the "master" per-cpu area: after this, @@ -467,11 +486,8 @@ void load_fixmap_gdt(int cpu) */ void switch_to_new_gdt(int cpu) { - struct desc_ptr gdt_descr; - - gdt_descr.address = (long)get_cpu_gdt_rw(cpu); - gdt_descr.size = GDT_SIZE - 1; - load_gdt(&gdt_descr); + /* Load the original GDT */ + load_direct_gdt(cpu); /* Reload the per-cpu base */ load_percpu_segment(cpu); } diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index d0414f054bdf..7b9a71e465b1 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -741,7 +741,6 @@ static int svm_hardware_enable(void) struct svm_cpu_data *sd; uint64_t efer; - struct desc_ptr gdt_descr; struct desc_struct *gdt; int me = raw_smp_processor_id(); @@ -763,8 +762,7 @@ static int svm_hardware_enable(void) sd->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1; sd->next_asid = sd->max_asid + 1; - native_store_gdt(&gdt_descr); - gdt = (struct desc_struct *)gdt_descr.address; + gdt = get_current_gdt_rw(); sd->tss_desc = (struct kvm_ldttss_desc *)(gdt + GDT_ENTRY_TSS); wrmsrl(MSR_EFER, efer | EFER_SVME); diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 7c3e42623090..99167f20bc34 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -935,7 +935,6 @@ static DEFINE_PER_CPU(struct vmcs *, current_vmcs); * when a CPU is brought down, and we need to VMCLEAR all VMCSs loaded on it. */ static DEFINE_PER_CPU(struct list_head, loaded_vmcss_on_cpu); -static DEFINE_PER_CPU(struct desc_ptr, host_gdt); /* * We maintian a per-CPU linked-list of vCPU, so in wakeup_handler() we @@ -1997,10 +1996,9 @@ static void reload_tss(void) /* * VT restores TR but not its size. Useless. */ - struct desc_ptr *gdt = this_cpu_ptr(&host_gdt); struct desc_struct *descs; - descs = (void *)gdt->address; + descs = get_current_gdt_rw(); descs[GDT_ENTRY_TSS].type = 9; /* available TSS */ load_TR_desc(); } @@ -2061,7 +2059,6 @@ static bool update_transition_efer(struct vcpu_vmx *vmx, int efer_offset) static unsigned long segment_base(u16 selector) { - struct desc_ptr *gdt = this_cpu_ptr(&host_gdt); struct desc_struct *d; unsigned long table_base; unsigned long v; @@ -2069,7 +2066,7 @@ static unsigned long segment_base(u16 selector) if (!(selector & ~3)) return 0; - table_base = gdt->address; + table_base = get_current_gdt_rw_vaddr(); if (selector & 4) { /* from ldt */ u16 ldt_selector = kvm_read_ldt(); @@ -2185,7 +2182,7 @@ static void __vmx_load_host_state(struct vcpu_vmx *vmx) #endif if (vmx->host_state.msr_host_bndcfgs) wrmsrl(MSR_IA32_BNDCFGS, vmx->host_state.msr_host_bndcfgs); - load_gdt(this_cpu_ptr(&host_gdt)); + load_fixmap_gdt(raw_smp_processor_id()); } static void vmx_load_host_state(struct vcpu_vmx *vmx) @@ -2287,7 +2284,7 @@ static void vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) } if (!already_loaded) { - struct desc_ptr *gdt = this_cpu_ptr(&host_gdt); + unsigned long gdt = get_current_gdt_rw_vaddr(); unsigned long sysenter_esp; kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu); @@ -2297,7 +2294,7 @@ static void vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) * processors. */ vmcs_writel(HOST_TR_BASE, kvm_read_tr_base()); /* 22.2.4 */ - vmcs_writel(HOST_GDTR_BASE, gdt->address); /* 22.2.4 */ + vmcs_writel(HOST_GDTR_BASE, gdt); /* 22.2.4 */ rdmsrl(MSR_IA32_SYSENTER_ESP, sysenter_esp); vmcs_writel(HOST_IA32_SYSENTER_ESP, sysenter_esp); /* 22.2.3 */ @@ -3523,8 +3520,6 @@ static int hardware_enable(void) ept_sync_global(); } - native_store_gdt(this_cpu_ptr(&host_gdt)); - return 0; }