Skip to content

Commit 39cefc5

Browse files
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 '7e1864332fbc1b993659eab7974da9fe8bf8c128': riscv: fix race when vmap stack overflow
2 parents d556a9a + 7e18643 commit 39cefc5

File tree

3 files changed

+32
-0
lines changed

3 files changed

+32
-0
lines changed

arch/riscv/include/asm/asm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#define REG_L __REG_SEL(ld, lw)
2424
#define REG_S __REG_SEL(sd, sw)
2525
#define REG_SC __REG_SEL(sc.d, sc.w)
26+
#define REG_AMOSWAP_AQ __REG_SEL(amoswap.d.aq, amoswap.w.aq)
2627
#define REG_ASM __REG_SEL(.dword, .word)
2728
#define SZREG __REG_SEL(8, 4)
2829
#define LGREG __REG_SEL(3, 2)

arch/riscv/kernel/entry.S

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,19 @@ handle_syscall_trace_exit:
404404

405405
#ifdef CONFIG_VMAP_STACK
406406
handle_kernel_stack_overflow:
407+
/*
408+
* Takes the psuedo-spinlock for the shadow stack, in case multiple
409+
* harts are concurrently overflowing their kernel stacks. We could
410+
* store any value here, but since we're overflowing the kernel stack
411+
* already we only have SP to use as a scratch register. So we just
412+
* swap in the address of the spinlock, as that's definately non-zero.
413+
*
414+
* Pairs with a store_release in handle_bad_stack().
415+
*/
416+
1: la sp, spin_shadow_stack
417+
REG_AMOSWAP_AQ sp, sp, (sp)
418+
bnez sp, 1b
419+
407420
la sp, shadow_stack
408421
addi sp, sp, SHADOW_OVERFLOW_STACK_SIZE
409422

arch/riscv/kernel/traps.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,11 +221,29 @@ asmlinkage unsigned long get_overflow_stack(void)
221221
OVERFLOW_STACK_SIZE;
222222
}
223223

224+
/*
225+
* A pseudo spinlock to protect the shadow stack from being used by multiple
226+
* harts concurrently. This isn't a real spinlock because the lock side must
227+
* be taken without a valid stack and only a single register, it's only taken
228+
* while in the process of panicing anyway so the performance and error
229+
* checking a proper spinlock gives us doesn't matter.
230+
*/
231+
unsigned long spin_shadow_stack;
232+
224233
asmlinkage void handle_bad_stack(struct pt_regs *regs)
225234
{
226235
unsigned long tsk_stk = (unsigned long)current->stack;
227236
unsigned long ovf_stk = (unsigned long)this_cpu_ptr(overflow_stack);
228237

238+
/*
239+
* We're done with the shadow stack by this point, as we're on the
240+
* overflow stack. Tell any other concurrent overflowing harts that
241+
* they can proceed with panicing by releasing the pseudo-spinlock.
242+
*
243+
* This pairs with an amoswap.aq in handle_kernel_stack_overflow.
244+
*/
245+
smp_store_release(&spin_shadow_stack, 0);
246+
229247
console_verbose();
230248

231249
pr_emerg("Insufficient stack space to handle exception!\n");

0 commit comments

Comments
 (0)