]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
CRISv32: prevent bogus restarts on sigreturn
authorRabin Vincent <rabin@rab.in>
Sun, 8 Feb 2015 16:53:22 +0000 (17:53 +0100)
committerJesper Nilsson <jespern@axis.com>
Wed, 25 Mar 2015 09:51:53 +0000 (10:51 +0100)
Al Viro noted that CRIS is vulnerable to bogus restarts on sigreturn.

The fixes CRISv32 by using regs->exs as an additional indicator to
whether we should attempt to restart the syscall or not.  EXS is only
used in the sigtrap handling, and in that path we already have r9 (the
other indicator, which indicates if we're in a syscall or not) cleared.

Test case, a port of Al's ARM version from 653d48b22166db2d8 ("arm: fix
really nasty sigreturn bug"):

  #include <unistd.h>
  #include <signal.h>
  #include <stdlib.h>
  #include <sys/time.h>
  #include <errno.h>

  void f(int n)
  {
   register int r10 asm ("r10") = n;

          __asm__ __volatile__(
   "ba 1f \n"
   "nop \n"
   "break 8 \n"
   "1: ba . \n"
   "nop \n"
   :
   : "r" (r10)
   : "memory");
  }

  void handler1(int sig) { }
  void handler2(int sig) { raise(1); }
  void handler3(int sig) { exit(0); }

  int main(int argc, char *argv[])
  {
          struct sigaction s = {.sa_handler = handler2};
          struct itimerval t1 = { .it_value = {1} };
          struct itimerval t2 = { .it_value = {2} };

          signal(1, handler1);

          sigemptyset(&s.sa_mask);
          sigaddset(&s.sa_mask, 1);
          sigaction(SIGALRM, &s, NULL);

          signal(SIGVTALRM, handler3);

          setitimer(ITIMER_REAL, &t1, NULL);
          setitimer(ITIMER_VIRTUAL, &t2, NULL);

          f(-513); /* -ERESTARTNOINTR */

          return 0;
  }

Reported-by: Al Viro <viro@ZenIV.linux.org.uk>
Link: http://lkml.kernel.org/r/20121208074429.GC4939@ZenIV.linux.org.uk
Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: Jesper Nilsson <jespern@axis.com>
arch/cris/arch-v32/kernel/entry.S
arch/cris/arch-v32/kernel/signal.c

index d4c088b4044c1402031cadf2ec696d8c4d0c90a7..1ea29b7f263c2dfa6bff2a077af3de8050e6d157 100644 (file)
@@ -147,7 +147,7 @@ system_call:
        ;; Stack-frame similar to the irq heads, which is reversed in
        ;; ret_from_sys_call.
 
-       sub.d   92, $sp         ; Skip EXS and EDA.
+       sub.d   92, $sp         ; Skip EDA.
        movem   $r13, [$sp]
        move.d  $sp, $r8
        addq    14*4, $r8
@@ -158,8 +158,9 @@ system_call:
        move    $ccs, $r4
        move    $srp, $r5
        move    $erp, $r6
+       move.d  $r9, $r7        ; Store syscall number in EXS
        subq    4, $sp
-       movem   $r6, [$r8]
+       movem   $r7, [$r8]
        ei                      ; Enable interrupts while processing syscalls.
        move.d  $r10, [$sp]
 
index 870e3e069318522ef26912ee92c4453721782ec9..d8d13beafc02045303b7af6dfd859a17f57c5996 100644 (file)
@@ -72,6 +72,9 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
        /* Make that the user-mode flag is set. */
        regs->ccs |= (1 << (U_CCS_BITNR + CCS_SHIFT));
 
+       /* Don't perform syscall restarting */
+       regs->exs = -1;
+
        /* Restore the old USP. */
        err |= __get_user(old_usp, &sc->usp);
        wrusp(old_usp);
@@ -427,6 +430,8 @@ do_signal(int canrestart, struct pt_regs *regs)
 {
        struct ksignal ksig;
 
+       canrestart = canrestart && ((int)regs->exs >= 0);
+
        /*
         * The common case should go fast, which is why this point is
         * reached from kernel-mode. If that's the case, just return