diff options
author | Rabin Vincent <rabin@rab.in> | 2015-02-08 16:57:40 +0100 |
---|---|---|
committer | Jesper Nilsson <jespern@axis.com> | 2015-03-25 10:49:31 +0100 |
commit | db4a35c651a10eddc6b48b69e1db1f46bea303fa (patch) | |
tree | fb86ccca5a5cfcd44be916b8016d5e1922d7eee8 /arch/cris | |
parent | 1eb1390bb21b1aa3b303627eb254296f097988cb (diff) |
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>
Diffstat (limited to 'arch/cris')
-rw-r--r-- | arch/cris/arch-v32/kernel/entry.S | 2 |
1 files changed, 2 insertions, 0 deletions
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 |