x86/irq/64: Split the IRQ stack into its own pages
authorAndy Lutomirski <luto@kernel.org>
Sun, 14 Apr 2019 16:00:06 +0000 (18:00 +0200)
committerBorislav Petkov <bp@suse.de>
Wed, 17 Apr 2019 13:37:02 +0000 (15:37 +0200)
Currently, the IRQ stack is hardcoded as the first page of the percpu
area, and the stack canary lives on the IRQ stack. The former gets in
the way of adding an IRQ stack guard page, and the latter is a potential
weakness in the stack canary mechanism.

Split the IRQ stack into its own private percpu pages.

[ tglx: Make 64 and 32 bit share struct irq_stack ]

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: "Chang S. Bae" <chang.seok.bae@intel.com>
Cc: Dominik Brodowski <linux@dominikbrodowski.net>
Cc: Feng Tang <feng.tang@intel.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jan Beulich <JBeulich@suse.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Cc: Joerg Roedel <jroedel@suse.de>
Cc: Jordan Borgner <mail@jordan-borgner.de>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Juergen Gross <jgross@suse.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Maran Wilson <maran.wilson@oracle.com>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@linux.vnet.ibm.com>
Cc: Nick Desaulniers <ndesaulniers@google.com>
Cc: Nicolai Stange <nstange@suse.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Pu Wen <puwen@hygon.cn>
Cc: "Rafael Ávila de Espíndola" <rafael@espindo.la>
Cc: Sean Christopherson <sean.j.christopherson@intel.com>
Cc: Stefano Stabellini <sstabellini@kernel.org>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: x86-ml <x86@kernel.org>
Cc: xen-devel@lists.xenproject.org
Link: https://lkml.kernel.org/r/20190414160146.267376656@linutronix.de
arch/x86/entry/entry_64.S
arch/x86/include/asm/processor.h
arch/x86/include/asm/stackprotector.h
arch/x86/kernel/asm-offsets_64.c
arch/x86/kernel/cpu/common.c
arch/x86/kernel/head_64.S
arch/x86/kernel/irq_64.c
arch/x86/kernel/setup_percpu.c
arch/x86/kernel/vmlinux.lds.S
arch/x86/tools/relocs.c
arch/x86/xen/xen-head.S

index 726abbe6c6d84666c2fbd3bdb54613b89980225b..cfe4d6ea258dc5dd792ed5cd086fa3a194719bc5 100644 (file)
@@ -298,7 +298,7 @@ ENTRY(__switch_to_asm)
 
 #ifdef CONFIG_STACKPROTECTOR
        movq    TASK_stack_canary(%rsi), %rbx
-       movq    %rbx, PER_CPU_VAR(irq_stack_union)+stack_canary_offset
+       movq    %rbx, PER_CPU_VAR(fixed_percpu_data) + stack_canary_offset
 #endif
 
 #ifdef CONFIG_RETPOLINE
@@ -430,7 +430,7 @@ END(irq_entries_start)
         * it before we actually move ourselves to the IRQ stack.
         */
 
-       movq    \old_rsp, PER_CPU_VAR(irq_stack_union + IRQ_STACK_SIZE - 8)
+       movq    \old_rsp, PER_CPU_VAR(irq_stack_backing_store + IRQ_STACK_SIZE - 8)
        movq    PER_CPU_VAR(hardirq_stack_ptr), %rsp
 
 #ifdef CONFIG_DEBUG_ENTRY
index 5e3dd4e2136dda6ca745ea1950776c89e538858f..7e99ef67bff08301d5d43097421e6ffe10ee7d16 100644 (file)
@@ -367,6 +367,13 @@ DECLARE_PER_CPU_PAGE_ALIGNED(struct tss_struct, cpu_tss_rw);
 #define __KERNEL_TSS_LIMIT     \
        (IO_BITMAP_OFFSET + IO_BITMAP_BYTES + sizeof(unsigned long) - 1)
 
+/* Per CPU interrupt stacks */
+struct irq_stack {
+       char            stack[IRQ_STACK_SIZE];
+} __aligned(IRQ_STACK_SIZE);
+
+DECLARE_PER_CPU(struct irq_stack *, hardirq_stack_ptr);
+
 #ifdef CONFIG_X86_32
 DECLARE_PER_CPU(unsigned long, cpu_current_top_of_stack);
 #else
@@ -375,28 +382,24 @@ DECLARE_PER_CPU(unsigned long, cpu_current_top_of_stack);
 #endif
 
 #ifdef CONFIG_X86_64
-union irq_stack_union {
-       char irq_stack[IRQ_STACK_SIZE];
+struct fixed_percpu_data {
        /*
         * GCC hardcodes the stack canary as %gs:40.  Since the
         * irq_stack is the object at %gs:0, we reserve the bottom
         * 48 bytes of the irq stack for the canary.
         */
-       struct {
-               char gs_base[40];
-               unsigned long stack_canary;
-       };
+       char            gs_base[40];
+       unsigned long   stack_canary;
 };
 
-DECLARE_PER_CPU_FIRST(union irq_stack_union, irq_stack_union) __visible;
-DECLARE_INIT_PER_CPU(irq_stack_union);
+DECLARE_PER_CPU_FIRST(struct fixed_percpu_data, fixed_percpu_data) __visible;
+DECLARE_INIT_PER_CPU(fixed_percpu_data);
 
 static inline unsigned long cpu_kernelmode_gs_base(int cpu)
 {
-       return (unsigned long)per_cpu(irq_stack_union.gs_base, cpu);
+       return (unsigned long)per_cpu(fixed_percpu_data.gs_base, cpu);
 }
 
-DECLARE_PER_CPU(char *, hardirq_stack_ptr);
 DECLARE_PER_CPU(unsigned int, irq_count);
 extern asmlinkage void ignore_sysret(void);
 
@@ -418,14 +421,7 @@ struct stack_canary {
 };
 DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary);
 #endif
-/*
- * per-CPU IRQ handling stacks
- */
-struct irq_stack {
-       char                    stack[IRQ_STACK_SIZE];
-} __aligned(IRQ_STACK_SIZE);
-
-DECLARE_PER_CPU(struct irq_stack *, hardirq_stack_ptr);
+/* Per CPU softirq stack pointer */
 DECLARE_PER_CPU(struct irq_stack *, softirq_stack_ptr);
 #endif /* X86_64 */
 
index 8ec97a62c245175e87d96c0376eb962f51f8e91d..91e29b6a86a5e9203c7e432f01c0046bf0bc561e 100644 (file)
@@ -13,7 +13,7 @@
  * On x86_64, %gs is shared by percpu area and stack canary.  All
  * percpu symbols are zero based and %gs points to the base of percpu
  * area.  The first occupant of the percpu area is always
- * irq_stack_union which contains stack_canary at offset 40.  Userland
+ * fixed_percpu_data which contains stack_canary at offset 40.  Userland
  * %gs is always saved and restored on kernel entry and exit using
  * swapgs, so stack protector doesn't add any complexity there.
  *
@@ -64,7 +64,7 @@ static __always_inline void boot_init_stack_canary(void)
        u64 tsc;
 
 #ifdef CONFIG_X86_64
-       BUILD_BUG_ON(offsetof(union irq_stack_union, stack_canary) != 40);
+       BUILD_BUG_ON(offsetof(struct fixed_percpu_data, stack_canary) != 40);
 #endif
        /*
         * We both use the random pool and the current TSC as a source
@@ -79,7 +79,7 @@ static __always_inline void boot_init_stack_canary(void)
 
        current->stack_canary = canary;
 #ifdef CONFIG_X86_64
-       this_cpu_write(irq_stack_union.stack_canary, canary);
+       this_cpu_write(fixed_percpu_data.stack_canary, canary);
 #else
        this_cpu_write(stack_canary.canary, canary);
 #endif
index f5281567e28ef70a4a00d87963ee4166b1bbb04c..d3d075226c0aa39761e9f4a33ae05cec321d36d7 100644 (file)
@@ -73,7 +73,7 @@ int main(void)
        BLANK();
 
 #ifdef CONFIG_STACKPROTECTOR
-       DEFINE(stack_canary_offset, offsetof(union irq_stack_union, stack_canary));
+       DEFINE(stack_canary_offset, offsetof(struct fixed_percpu_data, stack_canary));
        BLANK();
 #endif
 
index 1222080838dac3c208aace3a60ccc1c4fe8b9888..801c6f040faa76229db9ba1497e0a833b6eebc93 100644 (file)
@@ -1498,9 +1498,9 @@ static __init int setup_clearcpuid(char *arg)
 __setup("clearcpuid=", setup_clearcpuid);
 
 #ifdef CONFIG_X86_64
-DEFINE_PER_CPU_FIRST(union irq_stack_union,
-                    irq_stack_union) __aligned(PAGE_SIZE) __visible;
-EXPORT_PER_CPU_SYMBOL_GPL(irq_stack_union);
+DEFINE_PER_CPU_FIRST(struct fixed_percpu_data,
+                    fixed_percpu_data) __aligned(PAGE_SIZE) __visible;
+EXPORT_PER_CPU_SYMBOL_GPL(fixed_percpu_data);
 
 /*
  * The following percpu variables are hot.  Align current_task to
@@ -1510,7 +1510,7 @@ DEFINE_PER_CPU(struct task_struct *, current_task) ____cacheline_aligned =
        &init_task;
 EXPORT_PER_CPU_SYMBOL(current_task);
 
-DEFINE_PER_CPU(char *, hardirq_stack_ptr);
+DEFINE_PER_CPU(struct irq_stack *, hardirq_stack_ptr);
 DEFINE_PER_CPU(unsigned int, irq_count) __visible = -1;
 
 DEFINE_PER_CPU(int, __preempt_count) = INIT_PREEMPT_COUNT;
index d1dbe8e4eb8243e725b0c23609e7e28cad238f87..bcd206c8ac90064e9e702bc4bc1f2aa967a38065 100644 (file)
@@ -265,7 +265,7 @@ ENDPROC(start_cpu0)
        GLOBAL(initial_code)
        .quad   x86_64_start_kernel
        GLOBAL(initial_gs)
-       .quad   INIT_PER_CPU_VAR(irq_stack_union)
+       .quad   INIT_PER_CPU_VAR(fixed_percpu_data)
        GLOBAL(initial_stack)
        /*
         * The SIZEOF_PTREGS gap is a convention which helps the in-kernel
index c0bea0d7d76aebf59ef3af728adaae380253f8c3..c0f89d136b8033923eea9c9f382d8234ea6de507 100644 (file)
@@ -23,6 +23,9 @@
 #include <asm/io_apic.h>
 #include <asm/apic.h>
 
+DEFINE_PER_CPU_PAGE_ALIGNED(struct irq_stack, irq_stack_backing_store) __visible;
+DECLARE_INIT_PER_CPU(irq_stack_backing_store);
+
 int sysctl_panic_on_stackoverflow;
 
 /*
@@ -90,7 +93,7 @@ bool handle_irq(struct irq_desc *desc, struct pt_regs *regs)
 
 static int map_irq_stack(unsigned int cpu)
 {
-       void *va = per_cpu_ptr(irq_stack_union.irq_stack, cpu);
+       void *va = per_cpu_ptr(&irq_stack_backing_store, cpu);
 
        per_cpu(hardirq_stack_ptr, cpu) = va + IRQ_STACK_SIZE;
        return 0;
index 657343ecc2da4a5c56a980433491c7e54009935b..86663874ef0426edeefe32fc80851b4ac15354ad 100644 (file)
@@ -244,11 +244,6 @@ void __init setup_per_cpu_areas(void)
                per_cpu(x86_cpu_to_logical_apicid, cpu) =
                        early_per_cpu_map(x86_cpu_to_logical_apicid, cpu);
 #endif
-#ifdef CONFIG_X86_64
-               per_cpu(hardirq_stack_ptr, cpu) =
-                       per_cpu(irq_stack_union.irq_stack, cpu) +
-                       IRQ_STACK_SIZE;
-#endif
 #ifdef CONFIG_NUMA
                per_cpu(x86_cpu_to_node_map, cpu) =
                        early_per_cpu_map(x86_cpu_to_node_map, cpu);
index bad8c51fee6eea6be91d7a594e820470c121c2a9..a5af9a7c4be47f2df01a38a477223c9323c40aa0 100644 (file)
@@ -403,7 +403,8 @@ SECTIONS
  */
 #define INIT_PER_CPU(x) init_per_cpu__##x = ABSOLUTE(x) + __per_cpu_load
 INIT_PER_CPU(gdt_page);
-INIT_PER_CPU(irq_stack_union);
+INIT_PER_CPU(fixed_percpu_data);
+INIT_PER_CPU(irq_stack_backing_store);
 
 /*
  * Build-time check on the image size:
@@ -412,8 +413,8 @@ INIT_PER_CPU(irq_stack_union);
           "kernel image bigger than KERNEL_IMAGE_SIZE");
 
 #ifdef CONFIG_SMP
-. = ASSERT((irq_stack_union == 0),
-           "irq_stack_union is not at start of per-cpu area");
+. = ASSERT((fixed_percpu_data == 0),
+           "fixed_percpu_data is not at start of per-cpu area");
 #endif
 
 #endif /* CONFIG_X86_32 */
index b629f6992d9f65dd6ec587ece3e808843ffd748f..efa483205e436cb8d7a5b4d52b939c84ce8de0d8 100644 (file)
@@ -738,7 +738,7 @@ static void percpu_init(void)
  *     __per_cpu_load
  *
  * The "gold" linker incorrectly associates:
- *     init_per_cpu__irq_stack_union
+ *     init_per_cpu__fixed_percpu_data
  *     init_per_cpu__gdt_page
  */
 static int is_percpu_sym(ElfW(Sym) *sym, const char *symname)
index 5077ead5e59cad4b4fc1db004237a746bf6ca960..c1d8b90aa4e2d5af3990e9d708e133aa27888675 100644 (file)
@@ -40,13 +40,13 @@ ENTRY(startup_xen)
 #ifdef CONFIG_X86_64
        /* Set up %gs.
         *
-        * The base of %gs always points to the bottom of the irqstack
-        * union.  If the stack protector canary is enabled, it is
-        * located at %gs:40.  Note that, on SMP, the boot cpu uses
-        * init data section till per cpu areas are set up.
+        * The base of %gs always points to fixed_percpu_data.  If the
+        * stack protector canary is enabled, it is located at %gs:40.
+        * Note that, on SMP, the boot cpu uses init data section until
+        * the per cpu areas are set up.
         */
        movl    $MSR_GS_BASE,%ecx
-       movq    $INIT_PER_CPU_VAR(irq_stack_union),%rax
+       movq    $INIT_PER_CPU_VAR(fixed_percpu_data),%rax
        cdq
        wrmsr
 #endif