From: Rabin Vincent Date: Sun, 8 Feb 2015 15:57:40 +0000 (+0100) Subject: CRISv32: don't attempt syscall restart on irq exit X-Git-Tag: firefly_0821_release~176^2~1858^2~13 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=db4a35c651a10eddc6b48b69e1db1f46bea303fa;p=firefly-linux-kernel-4.4.55.git 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 #include #include 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 Signed-off-by: Jesper Nilsson --- diff --git a/arch/cris/arch-v32/kernel/entry.S b/arch/cris/arch-v32/kernel/entry.S index 2f19ac6217aa..d4c088b4044c 100644 --- a/arch/cris/arch-v32/kernel/entry.S +++ b/arch/cris/arch-v32/kernel/entry.S @@ -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