]> Git Repo - linux.git/commitdiff
Merge patch series "RISC-V: Align the shadow stack"
authorPalmer Dabbelt <[email protected]>
Mon, 12 Dec 2022 17:30:37 +0000 (09:30 -0800)
committerPalmer Dabbelt <[email protected]>
Mon, 12 Dec 2022 17:30:37 +0000 (09:30 -0800)
Palmer Dabbelt <[email protected]> says:

This contains a pair of cleanups that depend on a fix that has already
landed upstream.

* b4-shazam-merge:
  RISC-V: Add some comments about the shadow and overflow stacks
  RISC-V: Align the shadow stack
  riscv: fix race when vmap stack overflow

Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Palmer Dabbelt <[email protected]>
1  2 
arch/riscv/include/asm/asm.h
arch/riscv/kernel/entry.S
arch/riscv/kernel/traps.c

index 1b471ff731788608a1715f6f25796483a5077da0,e15a1c9f1cf886726ccdf9d0be5cad1484fc766f..816e753de636d48f8bb6f08c8d321824b144ecaa
@@@ -23,6 -23,7 +23,7 @@@
  #define REG_L         __REG_SEL(ld, lw)
  #define REG_S         __REG_SEL(sd, sw)
  #define REG_SC                __REG_SEL(sc.d, sc.w)
+ #define REG_AMOSWAP_AQ        __REG_SEL(amoswap.d.aq, amoswap.w.aq)
  #define REG_ASM               __REG_SEL(.dword, .word)
  #define SZREG         __REG_SEL(8, 4)
  #define LGREG         __REG_SEL(3, 2)
  #error "Unexpected __SIZEOF_SHORT__"
  #endif
  
 +#ifdef __ASSEMBLY__
 +
 +/* Common assembly source macros */
 +
 +/*
 + * NOP sequence
 + */
 +.macro        nops, num
 +      .rept   \num
 +      nop
 +      .endr
 +.endm
 +
 +#endif /* __ASSEMBLY__ */
 +
  #endif /* _ASM_RISCV_ASM_H */
index 32b2ddafedb0cb1083492bbd68ab3e3c2df0bfb7,5fdb6ba096000c4a609c1e70e0734856108660ab..99d38fdf8b18f2f18fe3bae2dce2d7a3e5b11b5a
@@@ -108,15 -108,15 +108,15 @@@ _save_context
  .option pop
  
  #ifdef CONFIG_TRACE_IRQFLAGS
 -      call trace_hardirqs_off
 +      call __trace_hardirqs_off
  #endif
  
 -#ifdef CONFIG_CONTEXT_TRACKING
 -      /* If previous state is in user mode, call context_tracking_user_exit. */
 +#ifdef CONFIG_CONTEXT_TRACKING_USER
 +      /* If previous state is in user mode, call user_exit_callable(). */
        li   a0, SR_PP
        and a0, s1, a0
        bnez a0, skip_context_tracking
 -      call context_tracking_user_exit
 +      call user_exit_callable
  skip_context_tracking:
  #endif
  
  
        /* Handle interrupts */
        move a0, sp /* pt_regs */
 -      la a1, handle_arch_irq
 -      REG_L a1, (a1)
 +      la a1, generic_handle_arch_irq
        jr a1
  1:
        /*
        li t0, EXC_BREAKPOINT
        beq s4, t0, 1f
  #ifdef CONFIG_TRACE_IRQFLAGS
 -      call trace_hardirqs_on
 +      call __trace_hardirqs_on
  #endif
        csrs CSR_STATUS, SR_IE
  
@@@ -176,7 -177,7 +176,7 @@@ handle_syscall
         */
        csrs CSR_STATUS, SR_IE
  #endif
 -#if defined(CONFIG_TRACE_IRQFLAGS) || defined(CONFIG_CONTEXT_TRACKING)
 +#if defined(CONFIG_TRACE_IRQFLAGS) || defined(CONFIG_CONTEXT_TRACKING_USER)
        /* Recover a0 - a7 for system calls */
        REG_L a0, PT_A0(sp)
        REG_L a1, PT_A1(sp)
@@@ -207,27 -208,13 +207,27 @@@ check_syscall_nr
         * Syscall number held in a7.
         * If syscall number is above allowed value, redirect to ni_syscall.
         */
 -      bgeu a7, t0, 1f
 +      bgeu a7, t0, 3f
 +#ifdef CONFIG_COMPAT
 +      REG_L s0, PT_STATUS(sp)
 +      srli s0, s0, SR_UXL_SHIFT
 +      andi s0, s0, (SR_UXL >> SR_UXL_SHIFT)
 +      li t0, (SR_UXL_32 >> SR_UXL_SHIFT)
 +      sub t0, s0, t0
 +      bnez t0, 1f
 +
 +      /* Call compat_syscall */
 +      la s0, compat_sys_call_table
 +      j 2f
 +1:
 +#endif
        /* Call syscall */
        la s0, sys_call_table
 +2:
        slli t0, a7, RISCV_LGPTR
        add s0, s0, t0
        REG_L s0, 0(s0)
 -1:
 +3:
        jalr s0
  
  ret_from_syscall:
         * (If it was configured with SECCOMP_RET_ERRNO/TRACE)
         */
  ret_from_syscall_rejected:
 +#ifdef CONFIG_DEBUG_RSEQ
 +      move a0, sp
 +      call rseq_syscall
 +#endif
        /* Trace syscalls, but only if requested by the user. */
        REG_L t0, TASK_TI_FLAGS(tp)
        andi t0, t0, _TIF_SYSCALL_WORK
        bnez t0, handle_syscall_trace_exit
  
 -ret_from_exception:
 +SYM_CODE_START_NOALIGN(ret_from_exception)
        REG_L s0, PT_STATUS(sp)
        csrc CSR_STATUS, SR_IE
  #ifdef CONFIG_TRACE_IRQFLAGS
 -      call trace_hardirqs_off
 +      call __trace_hardirqs_off
  #endif
  #ifdef CONFIG_RISCV_M_MODE
        /* the MPP value is too large to be used as an immediate arg for addi */
        andi s0, s0, SR_SPP
  #endif
        bnez s0, resume_kernel
 +SYM_CODE_END(ret_from_exception)
  
 -resume_userspace:
        /* Interrupts must be disabled here so flags are checked atomically */
        REG_L s0, TASK_TI_FLAGS(tp) /* current_thread_info->flags */
        andi s1, s0, _TIF_WORK_MASK
 -      bnez s1, work_pending
 -
 -#ifdef CONFIG_CONTEXT_TRACKING
 -      call context_tracking_user_enter
 +      bnez s1, resume_userspace_slow
 +resume_userspace:
 +#ifdef CONFIG_CONTEXT_TRACKING_USER
 +      call user_enter_callable
  #endif
  
        /* Save unwound kernel stack pointer in thread_info */
@@@ -288,10 -271,10 +288,10 @@@ restore_all
        REG_L s1, PT_STATUS(sp)
        andi t0, s1, SR_PIE
        beqz t0, 1f
 -      call trace_hardirqs_on
 +      call __trace_hardirqs_on
        j 2f
  1:
 -      call trace_hardirqs_off
 +      call __trace_hardirqs_off
  2:
  #endif
        REG_L a0, PT_STATUS(sp)
@@@ -368,12 -351,19 +368,12 @@@ resume_kernel
        j restore_all
  #endif
  
 -work_pending:
 +resume_userspace_slow:
        /* Enter slow path for supplementary processing */
 -      la ra, ret_from_exception
 -      andi s1, s0, _TIF_NEED_RESCHED
 -      bnez s1, work_resched
 -work_notifysig:
 -      /* Handle pending signals and notify-resume requests */
 -      csrs CSR_STATUS, SR_IE /* Enable interrupts for do_notify_resume() */
        move a0, sp /* pt_regs */
        move a1, s0 /* current_thread_info->flags */
 -      tail do_notify_resume
 -work_resched:
 -      tail schedule
 +      call do_work_pending
 +      j resume_userspace
  
  /* Slow paths for ptrace. */
  handle_syscall_trace_enter:
@@@ -397,6 -387,19 +397,19 @@@ handle_syscall_trace_exit
  
  #ifdef CONFIG_VMAP_STACK
  handle_kernel_stack_overflow:
+       /*
+        * Takes the psuedo-spinlock for the shadow stack, in case multiple
+        * harts are concurrently overflowing their kernel stacks.  We could
+        * store any value here, but since we're overflowing the kernel stack
+        * already we only have SP to use as a scratch register.  So we just
+        * swap in the address of the spinlock, as that's definately non-zero.
+        *
+        * Pairs with a store_release in handle_bad_stack().
+        */
+ 1:    la sp, spin_shadow_stack
+       REG_AMOSWAP_AQ sp, sp, (sp)
+       bnez sp, 1b
        la sp, shadow_stack
        addi sp, sp, SHADOW_OVERFLOW_STACK_SIZE
  
@@@ -554,6 -557,11 +567,6 @@@ ENTRY(__switch_to
        REG_L s9,  TASK_THREAD_S9_RA(a4)
        REG_L s10, TASK_THREAD_S10_RA(a4)
        REG_L s11, TASK_THREAD_S11_RA(a4)
 -      /* Swap the CPU entry around. */
 -      lw a3, TASK_TI_CPU(a0)
 -      lw a4, TASK_TI_CPU(a1)
 -      sw a3, TASK_TI_CPU(a1)
 -      sw a4, TASK_TI_CPU(a0)
        /* The offset of thread_info in task_struct is zero. */
        move tp, a1
        ret
index f3e96d60a2ff384a4ff7b342ba8daa45e22aaff1,336d4aadadb1cd357ed340870f0e7c9bf44e409d..549bde5c970a13e9412148c7c45aca1ef2034825
  #include <linux/mm.h>
  #include <linux/module.h>
  #include <linux/irq.h>
 +#include <linux/kexec.h>
  
  #include <asm/asm-prototypes.h>
  #include <asm/bug.h>
 +#include <asm/csr.h>
  #include <asm/processor.h>
  #include <asm/ptrace.h>
 -#include <asm/csr.h>
 +#include <asm/thread_info.h>
  
  int show_unhandled_signals = 1;
  
@@@ -33,7 -31,6 +33,7 @@@ void die(struct pt_regs *regs, const ch
  {
        static int die_counter;
        int ret;
 +      long cause;
  
        oops_enter();
  
  
        pr_emerg("%s [#%d]\n", str, ++die_counter);
        print_modules();
 -      show_regs(regs);
 +      if (regs)
 +              show_regs(regs);
  
 -      ret = notify_die(DIE_OOPS, str, regs, 0, regs->cause, SIGSEGV);
 +      cause = regs ? regs->cause : -1;
 +      ret = notify_die(DIE_OOPS, str, regs, 0, cause, SIGSEGV);
 +
 +      if (kexec_should_crash(current))
 +              crash_kexec(regs);
  
        bust_spinlocks(0);
        add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
@@@ -62,7 -54,7 +62,7 @@@
        if (panic_on_oops)
                panic("Fatal exception");
        if (ret != NOTIFY_STOP)
 -              do_exit(SIGSEGV);
 +              make_task_dead(SIGSEGV);
  }
  
  void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr)
@@@ -94,13 -86,8 +94,13 @@@ static void do_trap_error(struct pt_reg
        }
  }
  
 +#if defined(CONFIG_XIP_KERNEL) && defined(CONFIG_RISCV_ALTERNATIVE)
 +#define __trap_section                __section(".xip.traps")
 +#else
 +#define __trap_section
 +#endif
  #define DO_ERROR_INFO(name, signo, code, str)                         \
 -asmlinkage __visible void name(struct pt_regs *regs)                  \
 +asmlinkage __visible __trap_section void name(struct pt_regs *regs)   \
  {                                                                     \
        do_trap_error(regs, signo, code, regs->epc, "Oops - " str);     \
  }
@@@ -124,7 -111,7 +124,7 @@@ DO_ERROR_INFO(do_trap_store_misaligned
  int handle_misaligned_load(struct pt_regs *regs);
  int handle_misaligned_store(struct pt_regs *regs);
  
 -asmlinkage void do_trap_load_misaligned(struct pt_regs *regs)
 +asmlinkage void __trap_section do_trap_load_misaligned(struct pt_regs *regs)
  {
        if (!handle_misaligned_load(regs))
                return;
                      "Oops - load address misaligned");
  }
  
 -asmlinkage void do_trap_store_misaligned(struct pt_regs *regs)
 +asmlinkage void __trap_section do_trap_store_misaligned(struct pt_regs *regs)
  {
        if (!handle_misaligned_store(regs))
                return;
@@@ -159,7 -146,7 +159,7 @@@ static inline unsigned long get_break_i
        return GET_INSN_LENGTH(insn);
  }
  
 -asmlinkage __visible void do_trap_break(struct pt_regs *regs)
 +asmlinkage __visible __trap_section void do_trap_break(struct pt_regs *regs)
  {
  #ifdef CONFIG_KPROBES
        if (kprobe_single_step_handler(regs))
@@@ -207,14 -194,34 +207,29 @@@ int is_valid_bugaddr(unsigned long pc
  }
  #endif /* CONFIG_GENERIC_BUG */
  
 -/* stvec & scratch is already set from head.S */
 -void __init trap_init(void)
 -{
 -}
 -
  #ifdef CONFIG_VMAP_STACK
+ /*
+  * Extra stack space that allows us to provide panic messages when the kernel
+  * has overflowed its stack.
+  */
  static DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)],
                overflow_stack)__aligned(16);
  /*
-  * shadow stack, handled_ kernel_ stack_ overflow(in kernel/entry.S) is used
-  * to get per-cpu overflow stack(get_overflow_stack).
+  * A temporary stack for use by handle_kernel_stack_overflow.  This is used so
+  * we can call into C code to get the per-hart overflow stack.  Usage of this
+  * stack must be protected by spin_shadow_stack.
   */
- long shadow_stack[SHADOW_OVERFLOW_STACK_SIZE/sizeof(long)];
+ long shadow_stack[SHADOW_OVERFLOW_STACK_SIZE/sizeof(long)] __aligned(16);
+ /*
+  * A pseudo spinlock to protect the shadow stack from being used by multiple
+  * harts concurrently.  This isn't a real spinlock because the lock side must
+  * be taken without a valid stack and only a single register, it's only taken
+  * while in the process of panicing anyway so the performance and error
+  * checking a proper spinlock gives us doesn't matter.
+  */
+ unsigned long spin_shadow_stack;
  asmlinkage unsigned long get_overflow_stack(void)
  {
        return (unsigned long)this_cpu_ptr(overflow_stack) +
@@@ -226,6 -233,15 +241,15 @@@ asmlinkage void handle_bad_stack(struc
        unsigned long tsk_stk = (unsigned long)current->stack;
        unsigned long ovf_stk = (unsigned long)this_cpu_ptr(overflow_stack);
  
+       /*
+        * We're done with the shadow stack by this point, as we're on the
+        * overflow stack.  Tell any other concurrent overflowing harts that
+        * they can proceed with panicing by releasing the pseudo-spinlock.
+        *
+        * This pairs with an amoswap.aq in handle_kernel_stack_overflow.
+        */
+       smp_store_release(&spin_shadow_stack, 0);
        console_verbose();
  
        pr_emerg("Insufficient stack space to handle exception!\n");
This page took 0.096502 seconds and 4 git commands to generate.