]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
x86/process/64: Move cpu_current_top_of_stack out of TSS
authorLai Jiangshan <laijs@linux.alibaba.com>
Mon, 25 Jan 2021 17:34:29 +0000 (01:34 +0800)
committerThomas Gleixner <tglx@linutronix.de>
Sun, 28 Mar 2021 20:40:10 +0000 (22:40 +0200)
cpu_current_top_of_stack is currently stored in TSS.sp1. TSS is exposed
through the cpu_entry_area which is visible with user CR3 when PTI is
enabled and active.

This makes it a coveted fruit for attackers.  An attacker can fetch the
kernel stack top from it and continue next steps of actions based on the
kernel stack.

But it is actualy not necessary to be stored in the TSS.  It is only
accessed after the entry code switched to kernel CR3 and kernel GS_BASE
which means it can be in any regular percpu variable.

The reason why it is in TSS is historical (pre PTI) because TSS is also
used as scratch space in SYSCALL_64 and therefore cache hot.

A syscall also needs the per CPU variable current_task and eventually
__preempt_count, so placing cpu_current_top_of_stack next to them makes it
likely that they end up in the same cache line which should avoid
performance regressions. This is not enforced as the compiler is free to
place these variables, so these entry relevant variables should move into
a data structure to make this enforceable.

The seccomp_benchmark doesn't show any performance loss in the "getpid
native" test result.  Actually, the result changes from 93ns before to 92ns
with this change when KPTI is disabled. The test is very stable and
although the test doesn't show a higher degree of precision it gives enough
confidence that moving cpu_current_top_of_stack does not cause a
regression.

[ tglx: Removed unneeded export. Massaged changelog ]

Signed-off-by: Lai Jiangshan <laijs@linux.alibaba.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20210125173444.22696-2-jiangshanlai@gmail.com
arch/x86/include/asm/processor.h
arch/x86/include/asm/switch_to.h
arch/x86/include/asm/thread_info.h
arch/x86/kernel/cpu/common.c
arch/x86/kernel/process.c
arch/x86/mm/pti.c

index 8b3ed21c91912b93db2f83f880adbfc4ccd17bb3..185142b84ebe8fa96d289aa794d0509c1cc3c4ce 100644 (file)
@@ -314,11 +314,6 @@ struct x86_hw_tss {
 struct x86_hw_tss {
        u32                     reserved1;
        u64                     sp0;
-
-       /*
-        * We store cpu_current_top_of_stack in sp1 so it's always accessible.
-        * Linux does not use ring 1, so sp1 is not otherwise needed.
-        */
        u64                     sp1;
 
        /*
@@ -426,12 +421,7 @@ struct irq_stack {
        char            stack[IRQ_STACK_SIZE];
 } __aligned(IRQ_STACK_SIZE);
 
-#ifdef CONFIG_X86_32
 DECLARE_PER_CPU(unsigned long, cpu_current_top_of_stack);
-#else
-/* The RO copy can't be accessed with this_cpu_xyz(), so use the RW copy. */
-#define cpu_current_top_of_stack cpu_tss_rw.x86_tss.sp1
-#endif
 
 #ifdef CONFIG_X86_64
 struct fixed_percpu_data {
index 9f69cc497f4b68f3f49029f5b2b7a3209d8d890e..b5f0d2ff47e47366d0df6f0987cd1e4e2ea82adf 100644 (file)
@@ -71,12 +71,7 @@ static inline void update_task_stack(struct task_struct *task)
        else
                this_cpu_write(cpu_tss_rw.x86_tss.sp1, task->thread.sp0);
 #else
-       /*
-        * x86-64 updates x86_tss.sp1 via cpu_current_top_of_stack. That
-        * doesn't work on x86-32 because sp1 and
-        * cpu_current_top_of_stack have different values (because of
-        * the non-zero stack-padding on 32bit).
-        */
+       /* Xen PV enters the kernel on the thread stack. */
        if (static_cpu_has(X86_FEATURE_XENPV))
                load_sp0(task_top_of_stack(task));
 #endif
index 06b740bae431d2a0b4a163553e548dc5d64d7c0d..de406d93b515dbc7b93f31f17ece2334ea3b4443 100644 (file)
@@ -197,13 +197,7 @@ static inline int arch_within_stack_frames(const void * const stack,
 #endif
 }
 
-#else /* !__ASSEMBLY__ */
-
-#ifdef CONFIG_X86_64
-# define cpu_current_top_of_stack (cpu_tss_rw + TSS_sp1)
-#endif
-
-#endif
+#endif  /* !__ASSEMBLY__ */
 
 /*
  * Thread-synchronous status.
index 1aa5f0ac598f22dfa2e27cd249a8cc650f801035..340107800b36bd5243b9c7f14ef0b94e9db18d9f 100644 (file)
@@ -1748,6 +1748,8 @@ DEFINE_PER_CPU(bool, hardirq_stack_inuse);
 DEFINE_PER_CPU(int, __preempt_count) = INIT_PREEMPT_COUNT;
 EXPORT_PER_CPU_SYMBOL(__preempt_count);
 
+DEFINE_PER_CPU(unsigned long, cpu_current_top_of_stack) = TOP_OF_INIT_STACK;
+
 /* May not be marked __init: used by software suspend */
 void syscall_init(void)
 {
index cdfe5b4e99b36a942154e7fde5778bd0be811a84..43cbfc84153ae2c6ba4bcff93fe719e3902a0d1b 100644 (file)
@@ -63,14 +63,9 @@ __visible DEFINE_PER_CPU_PAGE_ALIGNED(struct tss_struct, cpu_tss_rw) = {
                 */
                .sp0 = (1UL << (BITS_PER_LONG-1)) + 1,
 
-               /*
-                * .sp1 is cpu_current_top_of_stack.  The init task never
-                * runs user code, but cpu_current_top_of_stack should still
-                * be well defined before the first context switch.
-                */
+#ifdef CONFIG_X86_32
                .sp1 = TOP_OF_INIT_STACK,
 
-#ifdef CONFIG_X86_32
                .ss0 = __KERNEL_DS,
                .ss1 = __KERNEL_CS,
 #endif
index b377604fb112ec78c32a2370f30eb4ce50030b0e..5d5c7bb50ce9e2e9646f783d26adb1e33ea094aa 100644 (file)
@@ -440,10 +440,9 @@ static void __init pti_clone_user_shared(void)
 
        for_each_possible_cpu(cpu) {
                /*
-                * The SYSCALL64 entry code needs to be able to find the
-                * thread stack and needs one word of scratch space in which
-                * to spill a register.  All of this lives in the TSS, in
-                * the sp1 and sp2 slots.
+                * The SYSCALL64 entry code needs one word of scratch space
+                * in which to spill a register.  It lives in the sp2 slot
+                * of the CPU's TSS.
                 *
                 * This is done for all possible CPUs during boot to ensure
                 * that it's propagated to all mms.