From patchwork Thu Jan 26 16:59:40 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Garnier X-Patchwork-Id: 9539797 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 19ADD604A0 for ; Thu, 26 Jan 2017 17:06:33 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0A7042094F for ; Thu, 26 Jan 2017 17:06:33 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id F2015269DA; Thu, 26 Jan 2017 17:06:32 +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=-4.1 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, RCVD_IN_DNSWL_MED, 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 4CA542094F for ; Thu, 26 Jan 2017 17:06:32 +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 1cWnSv-0002Gm-G3; Thu, 26 Jan 2017 17:04:05 +0000 Received: from mail6.bemta5.messagelabs.com ([195.245.231.135]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cWnOq-0001h1-Qu for xen-devel@lists.xenproject.org; Thu, 26 Jan 2017 16:59:53 +0000 Received: from [85.158.139.211] by server-13.bemta-5.messagelabs.com id 73/DD-18129-70B2A885; Thu, 26 Jan 2017 16:59:51 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprHIsWRWlGSWpSXmKPExsXiVRusp8um3RV hMGm+isX3LZOZHBg9Dn+4whLAGMWamZeUX5HAmnH3e17Bb6eKkwskGhhfWHQxcnEICUxjlDjc s5ati5GTg0XgFYvEzducIAkJgX5WiVsbvrCDJCQEYiTevz3CCmFXSqw4/5URxBYSUJLYumEpM 8Skf4wSfy//B5vEJqAlsadhPhNIQkRgM6/Eojk3mUESzAJ/GCVm7DQHsYUF3CUu3nvBArFaVW LCj/tgNbwCFhJtmz4xQWwzlrjdfxpsM6eApUTLpUYWiM0WEl+2/GSdwCiwgJFhFaN6cWpRWWq RrqVeUlFmekZJbmJmjq6hgalebmpxcWJ6ak5iUrFecn7uJkZgWDEAwQ7Gta3OhxglOZiURHkb VboihPiS8lMqMxKLM+KLSnNSiw8xynBwKEnwLtcEygkWpaanVqRl5gADHCYtwcGjJML7QgMoz VtckJhbnJkOkTrFqMuxa9fll0xCLHn5ealS4ryeIDMEQIoySvPgRsCi7RKjrJQwLyPQUUI8Ba lFuZklqPKvGMU5GJWEeTNApvBk5pXAbXoFdAQT0BEXmNtBjihJREhJNTC6sv1PT4tV4qzJ3TC HJb6kM97B2n6R4a2NWntafrgWqOy1W8XGxJvT9Jip994770Lv5AMZHw/5KYZeLls5e1ugxIYw 9tMBZ59cftIZoLO6udi/iytf8eXehcflJ1dVuga/m9n5m2PL8h0pKqkz9NZ2RG5ob+r8Z37vX /MN/nerpzzeK6nqraDEUpyRaKjFXFScCACg+v9BsQIAAA== X-Env-Sender: thgarnie@google.com X-Msg-Ref: server-11.tower-206.messagelabs.com!1485449989!64144460!1 X-Originating-IP: [74.125.83.46] X-SpamReason: No, hits=0.0 required=7.0 tests= X-StarScan-Received: X-StarScan-Version: 9.1.1; banners=-,-,- X-VirusChecked: Checked Received: (qmail 5724 invoked from network); 26 Jan 2017 16:59:50 -0000 Received: from mail-pg0-f46.google.com (HELO mail-pg0-f46.google.com) (74.125.83.46) by server-11.tower-206.messagelabs.com with AES128-GCM-SHA256 encrypted SMTP; 26 Jan 2017 16:59:50 -0000 Received: by mail-pg0-f46.google.com with SMTP id 3so15165112pgj.3 for ; Thu, 26 Jan 2017 08:59:50 -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=PgLQSf2pfRc8DraWurY9jaj/GYZaOcHYQ4sTBSJaurM=; b=lbfa0wZRfTV+UsN+5tPRavZfG3hdKuGxsa73hLVBPhpEnnqq2Wx/uCl8FwIfUoGRph SjW+2ZmXwnzHes08T5IpInskn4D843RLu/uEiRm1YehACmuEDpOy39y2rW2InwMAoFCi qBhejVGQEeBKglqaL+I0jERxqJ1HcBDPbRyTeQiwOoxltvP8nuwVlAC7I+foVkD2Jbv2 c82uBo1zZaGIA7itQG3x9ZJzZ2If8ONti5BkXCV6aySWhYkjZ6TvFJCMb7Hr0XK4khbv vnWzzx2FzwGdAxPWVjp10RQcXAtLbIUQ1vG154kL5Q1RO8hvzruDvyNP4NPmJY6ZLz+s DEUA== 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=PgLQSf2pfRc8DraWurY9jaj/GYZaOcHYQ4sTBSJaurM=; b=TqIdFDwlyQfXtAFCngk3RJzc4welPYUHOdCnSrB37VcJPIGR1NmmwmkdDTUhuZWLCI qO3ZeS81i9HZW6iZrWc3D0qRLyJHmdG4e4Bvo1cUf7j1WejRqpryhrRpO+zVpkWNJPIc ynh7R/SivxpP4dAuNASt5DHlYonjZ77tKuicCExXloDwHLOGntZXVQn/L+tmAbyjrowZ P//b5A3tvWWYrXmOTSl7XFjnNvQTfs/exWfeK2HgiFWwT4699LvEDFyNldKlCawIabpZ i6HSjQfOfOQwbT6idvxQJg6VTFBqtuUYhoZRKpMV2XlGkGvDJ2O2qxoGYScWgawGc5FE l3wA== X-Gm-Message-State: AIkVDXKqHjy2PotwqmfbGHdfd7ivYy4Yp6GFr/9gHh4dhbGb/xrhr52pI9kB7bF8+o6cp0v1 X-Received: by 10.84.199.194 with SMTP id d2mr5565042plh.134.1485449988329; Thu, 26 Jan 2017 08:59:48 -0800 (PST) Received: from skynet.sea.corp.google.com ([100.100.206.185]) by smtp.gmail.com with ESMTPSA id y6sm4723469pge.16.2017.01.26.08.59.47 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 26 Jan 2017 08:59:47 -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 , Arjan van de Ven , Paul Gortmaker , Borislav Petkov , Andy Lutomirski , "Rafael J . Wysocki" , Len Brown , Pavel Machek , Jiri Kosina , Matt Fleming , Ard Biesheuvel , Boris Ostrovsky , Juergen Gross , Rusty Russell , Christian Borntraeger , Fenghua Yu , He Chen , Brian Gerst , "Luis R . Rodriguez" , Adam Buchbinder , Stanislaw Gruszka , Arnd Bergmann , Dave Hansen , Chen Yucong , Vitaly Kuznetsov , David Vrabel , Josh Poimboeuf , Tim Chen , Rik van Riel , Andi Kleen , Jiri Olsa , Prarit Bhargava , Michael Ellerman , Joerg Roedel , Paolo Bonzini , =?UTF-8?q?Radim=20Kr=C4=8Dm=C3=A1=C5=99?= Date: Thu, 26 Jan 2017 08:59:40 -0800 Message-Id: <20170126165940.30799-3-thgarnie@google.com> X-Mailer: git-send-email 2.11.0.483.g087da7b7c-goog In-Reply-To: <20170126165940.30799-1-thgarnie@google.com> References: <20170126165940.30799-1-thgarnie@google.com> X-Mailman-Approved-At: Thu, 26 Jan 2017 17:04:04 +0000 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 v2 3/3] 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-20170125 --- arch/x86/include/asm/desc.h | 46 +++++++++++++++++++++++++++++++++++----- 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, 70 insertions(+), 24 deletions(-) diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h index 4cc176f57b78..ca7b2224fcb4 100644 --- a/arch/x86/include/asm/desc.h +++ b/arch/x86/include/asm/desc.h @@ -52,6 +52,12 @@ static inline struct desc_struct *get_cpu_direct_gdt(unsigned int cpu) return per_cpu(gdt_page, cpu).gdt; } +/* Provide the current original GDT */ +static inline struct desc_struct *get_current_direct_gdt(void) +{ + return this_cpu_ptr(&gdt_page)->gdt; +} + /* Get the fixmap index for a specific processor */ static inline unsigned int get_cpu_fixmap_gdt_index(int cpu) { @@ -223,11 +229,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)); @@ -248,6 +249,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. In 64bit, 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 = false; + struct desc_struct *fixmap_gdt; + + native_store_gdt(&gdt); + fixmap_gdt = get_cpu_fixmap_gdt(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 = true; + } + 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 22801fd345dc..e8e68b00a2ec 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -705,6 +705,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 15f06cf3e3d4..a7a54f57b68a 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 64bit 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_fixmap_gdt_index(cpu), - __pa(get_cpu_direct_gdt(cpu)), PAGE_KERNEL); + __pa(get_cpu_direct_gdt(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_direct_gdt(cpu); + gdt_descr.size = GDT_SIZE - 1; + load_gdt(&gdt_descr); +} +EXPORT_SYMBOL(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(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_direct_gdt(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..7864f4c813a1 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_direct_gdt(); 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 4e691035a32d..c64fa6e0417c 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 = (void *)get_current_direct_gdt(); 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 = (unsigned long)get_current_direct_gdt(); 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 = (unsigned long)get_current_direct_gdt(); 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; }