Commit 39cefc5f authored by Palmer Dabbelt's avatar Palmer Dabbelt

RISC-V: Fix a race condition during kernel stack overflow

This fixes a concrete bug but is also the basis for some cleanup work,
so I'm merging it based on the offending commit in order to minimize
future conflicts.

* commit '7e186433':
  riscv: fix race when vmap stack overflow
parents d556a9ae 7e186433
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#define REG_L __REG_SEL(ld, lw) #define REG_L __REG_SEL(ld, lw)
#define REG_S __REG_SEL(sd, sw) #define REG_S __REG_SEL(sd, sw)
#define REG_SC __REG_SEL(sc.d, sc.w) #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 REG_ASM __REG_SEL(.dword, .word)
#define SZREG __REG_SEL(8, 4) #define SZREG __REG_SEL(8, 4)
#define LGREG __REG_SEL(3, 2) #define LGREG __REG_SEL(3, 2)
......
...@@ -404,6 +404,19 @@ handle_syscall_trace_exit: ...@@ -404,6 +404,19 @@ handle_syscall_trace_exit:
#ifdef CONFIG_VMAP_STACK #ifdef CONFIG_VMAP_STACK
handle_kernel_stack_overflow: 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 la sp, shadow_stack
addi sp, sp, SHADOW_OVERFLOW_STACK_SIZE addi sp, sp, SHADOW_OVERFLOW_STACK_SIZE
......
...@@ -221,11 +221,29 @@ asmlinkage unsigned long get_overflow_stack(void) ...@@ -221,11 +221,29 @@ asmlinkage unsigned long get_overflow_stack(void)
OVERFLOW_STACK_SIZE; OVERFLOW_STACK_SIZE;
} }
/*
* 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 void handle_bad_stack(struct pt_regs *regs) asmlinkage void handle_bad_stack(struct pt_regs *regs)
{ {
unsigned long tsk_stk = (unsigned long)current->stack; unsigned long tsk_stk = (unsigned long)current->stack;
unsigned long ovf_stk = (unsigned long)this_cpu_ptr(overflow_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(); console_verbose();
pr_emerg("Insufficient stack space to handle exception!\n"); pr_emerg("Insufficient stack space to handle exception!\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment