401 lines
13 KiB
Diff
401 lines
13 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Thomas Gleixner <tglx@linutronix.de>
|
|
Date: Wed, 20 Dec 2017 18:28:54 +0100
|
|
Subject: [PATCH] x86/cpu_entry_area: Move it to a separate unit
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
CVE-2017-5754
|
|
|
|
Separate the cpu_entry_area code out of cpu/common.c and the fixmap.
|
|
|
|
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
|
Cc: Andy Lutomirski <luto@kernel.org>
|
|
Cc: Borislav Petkov <bp@alien8.de>
|
|
Cc: Dave Hansen <dave.hansen@linux.intel.com>
|
|
Cc: H. Peter Anvin <hpa@zytor.com>
|
|
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
Cc: Juergen Gross <jgross@suse.com>
|
|
Cc: Linus Torvalds <torvalds@linux-foundation.org>
|
|
Cc: Peter Zijlstra <peterz@infradead.org>
|
|
Signed-off-by: Ingo Molnar <mingo@kernel.org>
|
|
(cherry picked from commit ed1bbc40a0d10e0c5c74fe7bdc6298295cf40255)
|
|
Signed-off-by: Andy Whitcroft <apw@canonical.com>
|
|
Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
|
|
(cherry picked from commit 0fa11d2cd3d67af676aa2762ade282ba6d09cbe5)
|
|
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
|
|
---
|
|
arch/x86/mm/Makefile | 2 +-
|
|
arch/x86/include/asm/cpu_entry_area.h | 52 +++++++++++++++++
|
|
arch/x86/include/asm/fixmap.h | 41 +-------------
|
|
arch/x86/kernel/cpu/common.c | 94 ------------------------------
|
|
arch/x86/kernel/traps.c | 1 +
|
|
arch/x86/mm/cpu_entry_area.c | 104 ++++++++++++++++++++++++++++++++++
|
|
6 files changed, 159 insertions(+), 135 deletions(-)
|
|
create mode 100644 arch/x86/include/asm/cpu_entry_area.h
|
|
create mode 100644 arch/x86/mm/cpu_entry_area.c
|
|
|
|
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile
|
|
index 0fbdcb64f9f8..76f5399a8356 100644
|
|
--- a/arch/x86/mm/Makefile
|
|
+++ b/arch/x86/mm/Makefile
|
|
@@ -2,7 +2,7 @@
|
|
KCOV_INSTRUMENT_tlb.o := n
|
|
|
|
obj-y := init.o init_$(BITS).o fault.o ioremap.o extable.o pageattr.o mmap.o \
|
|
- pat.o pgtable.o physaddr.o setup_nx.o tlb.o
|
|
+ pat.o pgtable.o physaddr.o setup_nx.o tlb.o cpu_entry_area.o
|
|
|
|
# Make sure __phys_addr has no stackprotector
|
|
nostackp := $(call cc-option, -fno-stack-protector)
|
|
diff --git a/arch/x86/include/asm/cpu_entry_area.h b/arch/x86/include/asm/cpu_entry_area.h
|
|
new file mode 100644
|
|
index 000000000000..5471826803af
|
|
--- /dev/null
|
|
+++ b/arch/x86/include/asm/cpu_entry_area.h
|
|
@@ -0,0 +1,52 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+
|
|
+#ifndef _ASM_X86_CPU_ENTRY_AREA_H
|
|
+#define _ASM_X86_CPU_ENTRY_AREA_H
|
|
+
|
|
+#include <linux/percpu-defs.h>
|
|
+#include <asm/processor.h>
|
|
+
|
|
+/*
|
|
+ * cpu_entry_area is a percpu region that contains things needed by the CPU
|
|
+ * and early entry/exit code. Real types aren't used for all fields here
|
|
+ * to avoid circular header dependencies.
|
|
+ *
|
|
+ * Every field is a virtual alias of some other allocated backing store.
|
|
+ * There is no direct allocation of a struct cpu_entry_area.
|
|
+ */
|
|
+struct cpu_entry_area {
|
|
+ char gdt[PAGE_SIZE];
|
|
+
|
|
+ /*
|
|
+ * The GDT is just below entry_stack and thus serves (on x86_64) as
|
|
+ * a a read-only guard page.
|
|
+ */
|
|
+ struct entry_stack_page entry_stack_page;
|
|
+
|
|
+ /*
|
|
+ * On x86_64, the TSS is mapped RO. On x86_32, it's mapped RW because
|
|
+ * we need task switches to work, and task switches write to the TSS.
|
|
+ */
|
|
+ struct tss_struct tss;
|
|
+
|
|
+ char entry_trampoline[PAGE_SIZE];
|
|
+
|
|
+#ifdef CONFIG_X86_64
|
|
+ /*
|
|
+ * Exception stacks used for IST entries.
|
|
+ *
|
|
+ * In the future, this should have a separate slot for each stack
|
|
+ * with guard pages between them.
|
|
+ */
|
|
+ char exception_stacks[(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ + DEBUG_STKSZ];
|
|
+#endif
|
|
+};
|
|
+
|
|
+#define CPU_ENTRY_AREA_SIZE (sizeof(struct cpu_entry_area))
|
|
+#define CPU_ENTRY_AREA_PAGES (CPU_ENTRY_AREA_SIZE / PAGE_SIZE)
|
|
+
|
|
+DECLARE_PER_CPU(struct cpu_entry_area *, cpu_entry_area);
|
|
+
|
|
+extern void setup_cpu_entry_areas(void);
|
|
+
|
|
+#endif
|
|
diff --git a/arch/x86/include/asm/fixmap.h b/arch/x86/include/asm/fixmap.h
|
|
index a7fb137ad964..1b2521473480 100644
|
|
--- a/arch/x86/include/asm/fixmap.h
|
|
+++ b/arch/x86/include/asm/fixmap.h
|
|
@@ -25,6 +25,7 @@
|
|
#else
|
|
#include <uapi/asm/vsyscall.h>
|
|
#endif
|
|
+#include <asm/cpu_entry_area.h>
|
|
|
|
/*
|
|
* We can't declare FIXADDR_TOP as variable for x86_64 because vsyscall
|
|
@@ -44,46 +45,6 @@ extern unsigned long __FIXADDR_TOP;
|
|
PAGE_SIZE)
|
|
#endif
|
|
|
|
-/*
|
|
- * cpu_entry_area is a percpu region in the fixmap that contains things
|
|
- * needed by the CPU and early entry/exit code. Real types aren't used
|
|
- * for all fields here to avoid circular header dependencies.
|
|
- *
|
|
- * Every field is a virtual alias of some other allocated backing store.
|
|
- * There is no direct allocation of a struct cpu_entry_area.
|
|
- */
|
|
-struct cpu_entry_area {
|
|
- char gdt[PAGE_SIZE];
|
|
-
|
|
- /*
|
|
- * The GDT is just below entry_stack and thus serves (on x86_64) as
|
|
- * a a read-only guard page.
|
|
- */
|
|
- struct entry_stack_page entry_stack_page;
|
|
-
|
|
- /*
|
|
- * On x86_64, the TSS is mapped RO. On x86_32, it's mapped RW because
|
|
- * we need task switches to work, and task switches write to the TSS.
|
|
- */
|
|
- struct tss_struct tss;
|
|
-
|
|
- char entry_trampoline[PAGE_SIZE];
|
|
-
|
|
-#ifdef CONFIG_X86_64
|
|
- /*
|
|
- * Exception stacks used for IST entries.
|
|
- *
|
|
- * In the future, this should have a separate slot for each stack
|
|
- * with guard pages between them.
|
|
- */
|
|
- char exception_stacks[(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ + DEBUG_STKSZ];
|
|
-#endif
|
|
-};
|
|
-
|
|
-#define CPU_ENTRY_AREA_PAGES (sizeof(struct cpu_entry_area) / PAGE_SIZE)
|
|
-
|
|
-extern void setup_cpu_entry_areas(void);
|
|
-
|
|
/*
|
|
* Here we define all the compile-time 'special' virtual
|
|
* addresses. The point is to have a constant address at
|
|
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
|
|
index 7a8a5d436566..96171ce46d61 100644
|
|
--- a/arch/x86/kernel/cpu/common.c
|
|
+++ b/arch/x86/kernel/cpu/common.c
|
|
@@ -482,102 +482,8 @@ static const unsigned int exception_stack_sizes[N_EXCEPTION_STACKS] = {
|
|
[0 ... N_EXCEPTION_STACKS - 1] = EXCEPTION_STKSZ,
|
|
[DEBUG_STACK - 1] = DEBUG_STKSZ
|
|
};
|
|
-
|
|
-static DEFINE_PER_CPU_PAGE_ALIGNED(char, exception_stacks
|
|
- [(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ + DEBUG_STKSZ]);
|
|
-#endif
|
|
-
|
|
-static DEFINE_PER_CPU_PAGE_ALIGNED(struct entry_stack_page,
|
|
- entry_stack_storage);
|
|
-
|
|
-static void __init
|
|
-set_percpu_fixmap_pages(int idx, void *ptr, int pages, pgprot_t prot)
|
|
-{
|
|
- for ( ; pages; pages--, idx--, ptr += PAGE_SIZE)
|
|
- __set_fixmap(idx, per_cpu_ptr_to_phys(ptr), prot);
|
|
-}
|
|
-
|
|
-/* Setup the fixmap mappings only once per-processor */
|
|
-static void __init setup_cpu_entry_area(int cpu)
|
|
-{
|
|
-#ifdef CONFIG_X86_64
|
|
- extern char _entry_trampoline[];
|
|
-
|
|
- /* On 64-bit systems, we use a read-only fixmap GDT and TSS. */
|
|
- pgprot_t gdt_prot = PAGE_KERNEL_RO;
|
|
- pgprot_t tss_prot = PAGE_KERNEL_RO;
|
|
-#else
|
|
- /*
|
|
- * On native 32-bit systems, the GDT cannot be read-only because
|
|
- * our double fault handler uses a task gate, and entering through
|
|
- * a task gate needs to change an available TSS to busy. If the
|
|
- * GDT is read-only, that will triple fault. The TSS cannot be
|
|
- * read-only because the CPU writes to it on task switches.
|
|
- *
|
|
- * On Xen PV, the GDT must be read-only because the hypervisor
|
|
- * requires it.
|
|
- */
|
|
- pgprot_t gdt_prot = boot_cpu_has(X86_FEATURE_XENPV) ?
|
|
- PAGE_KERNEL_RO : PAGE_KERNEL;
|
|
- pgprot_t tss_prot = PAGE_KERNEL;
|
|
-#endif
|
|
-
|
|
- __set_fixmap(get_cpu_entry_area_index(cpu, gdt), get_cpu_gdt_paddr(cpu), gdt_prot);
|
|
- set_percpu_fixmap_pages(get_cpu_entry_area_index(cpu, entry_stack_page),
|
|
- per_cpu_ptr(&entry_stack_storage, cpu), 1,
|
|
- PAGE_KERNEL);
|
|
-
|
|
- /*
|
|
- * The Intel SDM says (Volume 3, 7.2.1):
|
|
- *
|
|
- * Avoid placing a page boundary in the part of the TSS that the
|
|
- * processor reads during a task switch (the first 104 bytes). The
|
|
- * processor may not correctly perform address translations if a
|
|
- * boundary occurs in this area. During a task switch, the processor
|
|
- * reads and writes into the first 104 bytes of each TSS (using
|
|
- * contiguous physical addresses beginning with the physical address
|
|
- * of the first byte of the TSS). So, after TSS access begins, if
|
|
- * part of the 104 bytes is not physically contiguous, the processor
|
|
- * will access incorrect information without generating a page-fault
|
|
- * exception.
|
|
- *
|
|
- * There are also a lot of errata involving the TSS spanning a page
|
|
- * boundary. Assert that we're not doing that.
|
|
- */
|
|
- BUILD_BUG_ON((offsetof(struct tss_struct, x86_tss) ^
|
|
- offsetofend(struct tss_struct, x86_tss)) & PAGE_MASK);
|
|
- BUILD_BUG_ON(sizeof(struct tss_struct) % PAGE_SIZE != 0);
|
|
- set_percpu_fixmap_pages(get_cpu_entry_area_index(cpu, tss),
|
|
- &per_cpu(cpu_tss_rw, cpu),
|
|
- sizeof(struct tss_struct) / PAGE_SIZE,
|
|
- tss_prot);
|
|
-
|
|
-#ifdef CONFIG_X86_32
|
|
- per_cpu(cpu_entry_area, cpu) = get_cpu_entry_area(cpu);
|
|
#endif
|
|
|
|
-#ifdef CONFIG_X86_64
|
|
- BUILD_BUG_ON(sizeof(exception_stacks) % PAGE_SIZE != 0);
|
|
- BUILD_BUG_ON(sizeof(exception_stacks) !=
|
|
- sizeof(((struct cpu_entry_area *)0)->exception_stacks));
|
|
- set_percpu_fixmap_pages(get_cpu_entry_area_index(cpu, exception_stacks),
|
|
- &per_cpu(exception_stacks, cpu),
|
|
- sizeof(exception_stacks) / PAGE_SIZE,
|
|
- PAGE_KERNEL);
|
|
-
|
|
- __set_fixmap(get_cpu_entry_area_index(cpu, entry_trampoline),
|
|
- __pa_symbol(_entry_trampoline), PAGE_KERNEL_RX);
|
|
-#endif
|
|
-}
|
|
-
|
|
-void __init setup_cpu_entry_areas(void)
|
|
-{
|
|
- unsigned int cpu;
|
|
-
|
|
- for_each_possible_cpu(cpu)
|
|
- setup_cpu_entry_area(cpu);
|
|
-}
|
|
-
|
|
/* Load the original GDT from the per-cpu structure */
|
|
void load_direct_gdt(int cpu)
|
|
{
|
|
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
|
|
index 14b462eefa17..ef2d1b8a0516 100644
|
|
--- a/arch/x86/kernel/traps.c
|
|
+++ b/arch/x86/kernel/traps.c
|
|
@@ -57,6 +57,7 @@
|
|
#include <asm/traps.h>
|
|
#include <asm/desc.h>
|
|
#include <asm/fpu/internal.h>
|
|
+#include <asm/cpu_entry_area.h>
|
|
#include <asm/mce.h>
|
|
#include <asm/fixmap.h>
|
|
#include <asm/mach_traps.h>
|
|
diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c
|
|
new file mode 100644
|
|
index 000000000000..235ff9cfaaf4
|
|
--- /dev/null
|
|
+++ b/arch/x86/mm/cpu_entry_area.c
|
|
@@ -0,0 +1,104 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+
|
|
+#include <linux/spinlock.h>
|
|
+#include <linux/percpu.h>
|
|
+
|
|
+#include <asm/cpu_entry_area.h>
|
|
+#include <asm/pgtable.h>
|
|
+#include <asm/fixmap.h>
|
|
+#include <asm/desc.h>
|
|
+
|
|
+static DEFINE_PER_CPU_PAGE_ALIGNED(struct entry_stack_page, entry_stack_storage);
|
|
+
|
|
+#ifdef CONFIG_X86_64
|
|
+static DEFINE_PER_CPU_PAGE_ALIGNED(char, exception_stacks
|
|
+ [(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ + DEBUG_STKSZ]);
|
|
+#endif
|
|
+
|
|
+static void __init
|
|
+set_percpu_fixmap_pages(int idx, void *ptr, int pages, pgprot_t prot)
|
|
+{
|
|
+ for ( ; pages; pages--, idx--, ptr += PAGE_SIZE)
|
|
+ __set_fixmap(idx, per_cpu_ptr_to_phys(ptr), prot);
|
|
+}
|
|
+
|
|
+/* Setup the fixmap mappings only once per-processor */
|
|
+static void __init setup_cpu_entry_area(int cpu)
|
|
+{
|
|
+#ifdef CONFIG_X86_64
|
|
+ extern char _entry_trampoline[];
|
|
+
|
|
+ /* On 64-bit systems, we use a read-only fixmap GDT and TSS. */
|
|
+ pgprot_t gdt_prot = PAGE_KERNEL_RO;
|
|
+ pgprot_t tss_prot = PAGE_KERNEL_RO;
|
|
+#else
|
|
+ /*
|
|
+ * On native 32-bit systems, the GDT cannot be read-only because
|
|
+ * our double fault handler uses a task gate, and entering through
|
|
+ * a task gate needs to change an available TSS to busy. If the
|
|
+ * GDT is read-only, that will triple fault. The TSS cannot be
|
|
+ * read-only because the CPU writes to it on task switches.
|
|
+ *
|
|
+ * On Xen PV, the GDT must be read-only because the hypervisor
|
|
+ * requires it.
|
|
+ */
|
|
+ pgprot_t gdt_prot = boot_cpu_has(X86_FEATURE_XENPV) ?
|
|
+ PAGE_KERNEL_RO : PAGE_KERNEL;
|
|
+ pgprot_t tss_prot = PAGE_KERNEL;
|
|
+#endif
|
|
+
|
|
+ __set_fixmap(get_cpu_entry_area_index(cpu, gdt), get_cpu_gdt_paddr(cpu), gdt_prot);
|
|
+ set_percpu_fixmap_pages(get_cpu_entry_area_index(cpu, entry_stack_page),
|
|
+ per_cpu_ptr(&entry_stack_storage, cpu), 1,
|
|
+ PAGE_KERNEL);
|
|
+
|
|
+ /*
|
|
+ * The Intel SDM says (Volume 3, 7.2.1):
|
|
+ *
|
|
+ * Avoid placing a page boundary in the part of the TSS that the
|
|
+ * processor reads during a task switch (the first 104 bytes). The
|
|
+ * processor may not correctly perform address translations if a
|
|
+ * boundary occurs in this area. During a task switch, the processor
|
|
+ * reads and writes into the first 104 bytes of each TSS (using
|
|
+ * contiguous physical addresses beginning with the physical address
|
|
+ * of the first byte of the TSS). So, after TSS access begins, if
|
|
+ * part of the 104 bytes is not physically contiguous, the processor
|
|
+ * will access incorrect information without generating a page-fault
|
|
+ * exception.
|
|
+ *
|
|
+ * There are also a lot of errata involving the TSS spanning a page
|
|
+ * boundary. Assert that we're not doing that.
|
|
+ */
|
|
+ BUILD_BUG_ON((offsetof(struct tss_struct, x86_tss) ^
|
|
+ offsetofend(struct tss_struct, x86_tss)) & PAGE_MASK);
|
|
+ BUILD_BUG_ON(sizeof(struct tss_struct) % PAGE_SIZE != 0);
|
|
+ set_percpu_fixmap_pages(get_cpu_entry_area_index(cpu, tss),
|
|
+ &per_cpu(cpu_tss_rw, cpu),
|
|
+ sizeof(struct tss_struct) / PAGE_SIZE,
|
|
+ tss_prot);
|
|
+
|
|
+#ifdef CONFIG_X86_32
|
|
+ per_cpu(cpu_entry_area, cpu) = get_cpu_entry_area(cpu);
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_X86_64
|
|
+ BUILD_BUG_ON(sizeof(exception_stacks) % PAGE_SIZE != 0);
|
|
+ BUILD_BUG_ON(sizeof(exception_stacks) !=
|
|
+ sizeof(((struct cpu_entry_area *)0)->exception_stacks));
|
|
+ set_percpu_fixmap_pages(get_cpu_entry_area_index(cpu, exception_stacks),
|
|
+ &per_cpu(exception_stacks, cpu),
|
|
+ sizeof(exception_stacks) / PAGE_SIZE,
|
|
+ PAGE_KERNEL);
|
|
+
|
|
+ __set_fixmap(get_cpu_entry_area_index(cpu, entry_trampoline),
|
|
+ __pa_symbol(_entry_trampoline), PAGE_KERNEL_RX);
|
|
+#endif
|
|
+}
|
|
+
|
|
+void __init setup_cpu_entry_areas(void)
|
|
+{
|
|
+ unsigned int cpu;
|
|
+
|
|
+ for_each_possible_cpu(cpu)
|
|
+ setup_cpu_entry_area(cpu);
|
|
+}
|
|
--
|
|
2.14.2
|
|
|