CRISv32: don't attempt syscall restart on irq exit
r9 is used to determine whether syscall restarting must be performed or not. Unfortunately, r9 is never set to zero in the non-syscall path, and r9 is on top of that a callee-saved register which can be set to non-zero by the C functions that are called during IRQ handling. This means that if r10 (used for the syscall return value) is one of the -ERESTART* values when a hardware interrupt occurs which leads to a signal being delivered to the process, the kernel will "restart" a syscall which never occurred. This will lead to the PC being moved back by 2 on return to user space. Fix the problem by setting r9 to zero in the interrupt path. Test case (should loop forever but ends up executing the break 8 trap instruction): #include <signal.h> #include <stdlib.h> #include <sys/time.h> void f(int n) { register int r9 asm ("r9") = 1; register int r10 asm ("r10") = n; __asm__ __volatile__( "ba 1f \n" "nop \n" "break 8 \n" "1: ba . \n" "nop \n" : : "r" (r9), "r" (r10) : "memory"); } void handler1(int sig) { } int main(int argc, char *argv[]) { struct itimerval t1 = { .it_value = {1} }; signal(SIGALRM, handler1); setitimer(ITIMER_REAL, &t1, NULL); f(-513); /* -ERESTARTNOINTR */ return 0; } Signed-off-by: Rabin Vincent <rabin@rab.in> Signed-off-by: Jesper Nilsson <jespern@axis.com>
This commit is contained in:
parent
1eb1390bb2
commit
db4a35c651
|
@ -99,6 +99,8 @@ ret_from_kernel_thread:
|
|||
|
||||
.type ret_from_intr,@function
|
||||
ret_from_intr:
|
||||
moveq 0, $r9 ; not a syscall
|
||||
|
||||
;; Check for resched if preemptive kernel, or if we're going back to
|
||||
;; user-mode. This test matches the user_regs(regs) macro. Don't simply
|
||||
;; test CCS since that doesn't necessarily reflect what mode we'll
|
||||
|
|
Loading…
Reference in New Issue