summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel
diff options
context:
space:
mode:
authorTiezhu Yang <yangtiezhu@loongson.cn>2021-12-20 12:27:38 +0800
committerThomas Bogendoerfer <tsbogend@alpha.franken.de>2022-01-02 14:16:29 +0100
commit0ebd37a2222f6b6f1b55b385e40d16a5ce15cb6a (patch)
tree782f114cdf121210e79d49da61e186b38b620db7 /arch/mips/kernel
parent6f03055d508ff4feb8db02ba3df9303a1db8d381 (diff)
downloadlinux-0ebd37a2222f6b6f1b55b385e40d16a5ce15cb6a.tar.gz
linux-0ebd37a2222f6b6f1b55b385e40d16a5ce15cb6a.tar.bz2
linux-0ebd37a2222f6b6f1b55b385e40d16a5ce15cb6a.zip
MIPS: signal: Protect against sigaltstack wraparound
If a process uses alternative signal stack by using sigaltstack(), then that stack overflows and stack wraparound occurs. Simple Explanation: The accurate sp order is A,B,C,D,... But now the sp points to A,B,C and A,B,C again. This problem can reproduce by the following code: $ cat test_sigaltstack.c #include <stdio.h> #include <signal.h> #include <stdlib.h> #include <string.h> volatile int counter = 0; void print_sp() { unsigned long sp; __asm__ __volatile__("move %0, $sp" : "=r" (sp)); printf("sp = 0x%08lx\n", sp); } void segv_handler() { int *c = NULL; print_sp(); counter++; printf("%d\n", counter); if (counter == 23) abort(); *c = 1; // SEGV } int main() { int *c = NULL; char *s = malloc(SIGSTKSZ); stack_t stack; struct sigaction action; memset(s, 0, SIGSTKSZ); stack.ss_sp = s; stack.ss_flags = 0; stack.ss_size = SIGSTKSZ; if (sigaltstack(&stack, NULL)) { printf("Failed to use sigaltstack!\n"); return -1; } memset(&action, 0, sizeof(action)); action.sa_handler = segv_handler; action.sa_flags = SA_ONSTACK | SA_NODEFER; sigemptyset(&action.sa_mask); sigaction(SIGSEGV, &action, NULL); *c = 0; //SEGV if (!s) free(s); return 0; } $ gcc test_sigaltstack.c -o test_sigaltstack $ ./test_sigaltstack sp = 0x120015c80 1 sp = 0x120015900 2 sp = 0x120015580 3 sp = 0x120015200 4 sp = 0x120014e80 5 sp = 0x120014b00 6 sp = 0x120014780 7 sp = 0x120014400 8 sp = 0x120014080 9 sp = 0x120013d00 10 sp = 0x120015c80 11 # wraparound occurs! the 11nd output is same as 1st. sp = 0x120015900 12 sp = 0x120015580 13 sp = 0x120015200 14 sp = 0x120014e80 15 sp = 0x120014b00 16 sp = 0x120014780 17 sp = 0x120014400 18 sp = 0x120014080 19 sp = 0x120013d00 20 sp = 0x120015c80 21 # wraparound occurs! the 21nd output is same as 1st. sp = 0x120015900 22 sp = 0x120015580 23 Aborted With this patch: $ ./test_sigaltstack sp = 0x120015c80 1 sp = 0x120015900 2 sp = 0x120015580 3 sp = 0x120015200 4 sp = 0x120014e80 5 sp = 0x120014b00 6 sp = 0x120014780 7 sp = 0x120014400 8 sp = 0x120014080 9 Segmentation fault If we are on the alternate signal stack and would overflow it, don't. Return an always-bogus address instead so we will die with SIGSEGV. This patch is similar with commit 83bd01024b1f ("x86: protect against sigaltstack wraparound"). Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn> Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Diffstat (limited to 'arch/mips/kernel')
-rw-r--r--arch/mips/kernel/signal.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c
index c9b2a75563e1..c1632e87b679 100644
--- a/arch/mips/kernel/signal.c
+++ b/arch/mips/kernel/signal.c
@@ -563,6 +563,13 @@ void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
sp = regs->regs[29];
/*
+ * If we are on the alternate signal stack and would overflow it, don't.
+ * Return an always-bogus address instead so we will die with SIGSEGV.
+ */
+ if (on_sig_stack(sp) && !likely(on_sig_stack(sp - frame_size)))
+ return (void __user __force *)(-1UL);
+
+ /*
* FPU emulator may have it's own trampoline active just
* above the user stack, 16-bytes before the next lowest
* 16 byte boundary. Try to avoid trashing it.