LCOV - code coverage report
Current view: directory - src/trusted/service_runtime/posix - nacl_signal.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 100 51 51.0 %
Date: 2012-02-16 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2012 The Native Client Authors. All rights reserved.
       3                 :  * Use of this source code is governed by a BSD-style license that can be
       4                 :  * found in the LICENSE file.
       5                 :  */
       6                 : 
       7                 : #include <errno.h>
       8                 : #include <signal.h>
       9                 : #include <string.h>
      10                 : #include <sys/mman.h>
      11                 : #include <unistd.h>
      12                 : 
      13                 : #include "native_client/src/shared/platform/nacl_check.h"
      14                 : #include "native_client/src/shared/platform/nacl_exit.h"
      15                 : #include "native_client/src/shared/platform/nacl_log.h"
      16                 : #include "native_client/src/trusted/service_runtime/arch/sel_ldr_arch.h"
      17                 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
      18                 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
      19                 : #include "native_client/src/trusted/service_runtime/nacl_globals.h"
      20                 : #include "native_client/src/trusted/service_runtime/nacl_signal.h"
      21                 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
      22                 : #include "native_client/src/trusted/service_runtime/sel_rt.h"
      23                 : 
      24                 : 
      25                 : struct ExceptionFrame {
      26                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
      27                 :   nacl_reg_t return_addr;
      28                 : # if NACL_BUILD_SUBARCH == 32
      29                 :   uint32_t prog_ctr; /* Function argument 1 */
      30                 :   uint32_t stack_ptr; /* Function argument 2 */
      31                 : # endif
      32                 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
      33                 :   /*
      34                 :    * Empty structs are a corner case and can have different sizeof()s
      35                 :    * in C and C++, so to avoid potential confusion we have a dummy
      36                 :    * entry here.
      37                 :    * TODO(mseaborn): Eventually we will push a proper stack frame
      38                 :    * containing a full register dump, so this dummy field will go
      39                 :    * away.
      40                 :    */
      41                 :   uint32_t dummy;
      42                 : #else
      43                 : # error Unknown architecture
      44                 : #endif
      45                 : };
      46                 : 
      47                 : /*
      48                 :  * This module is based on the Posix signal model.  See:
      49                 :  * http://www.opengroup.org/onlinepubs/009695399/functions/sigaction.html
      50                 :  */
      51                 : 
      52                 : #ifndef MAP_ANONYMOUS
      53                 : #define MAP_ANONYMOUS MAP_ANON
      54                 : #endif
      55                 : 
      56                 : /*
      57                 :  * TODO(noelallen) split these macros and conditional compiles
      58                 :  * into architecture specific files. Bug #955
      59                 :  */
      60                 : 
      61                 : /* Use 4K more than the minimum to allow breakpad to run. */
      62                 : #define SIGNAL_STACK_SIZE (SIGSTKSZ + 4096)
      63                 : #define STACK_GUARD_SIZE NACL_PAGESIZE
      64                 : 
      65                 : #ifdef SIGSTKFLT
      66                 : #define SIGNAL_COUNT 8
      67                 : static int s_Signals[SIGNAL_COUNT] = {
      68                 :   SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV, SIGSTKFLT
      69                 : };
      70                 : #else
      71                 : #define SIGNAL_COUNT 7
      72                 : static int s_Signals[SIGNAL_COUNT] = {
      73                 :   SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV
      74                 : };
      75                 : #endif
      76                 : 
      77                 : static struct sigaction s_OldActions[SIGNAL_COUNT];
      78                 : 
      79               3 : int NaClSignalStackAllocate(void **result) {
      80                 :   /*
      81                 :    * We use mmap() to allocate the signal stack for two reasons:
      82                 :    *
      83                 :    * 1) By page-aligning the memory allocation (which malloc() does
      84                 :    * not do for small allocations), we avoid allocating any real
      85                 :    * memory in the common case in which the signal handler is never
      86                 :    * run.
      87                 :    *
      88                 :    * 2) We get to create a guard page, to guard against the unlikely
      89                 :    * occurrence of the signal handler both overrunning and doing so in
      90                 :    * an exploitable way.
      91                 :    */
      92                 :   uint8_t *stack = mmap(NULL, SIGNAL_STACK_SIZE + STACK_GUARD_SIZE,
      93                 :                         PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
      94               3 :                         -1, 0);
      95               3 :   if (stack == MAP_FAILED) {
      96               0 :     return 0;
      97                 :   }
      98                 :   /* We assume that the stack grows downwards. */
      99               3 :   if (mprotect(stack, STACK_GUARD_SIZE, PROT_NONE) != 0) {
     100               0 :     NaClLog(LOG_FATAL, "Failed to mprotect() the stack guard page:\n\t%s\n",
     101                 :       strerror(errno));
     102                 :   }
     103               3 :   *result = stack;
     104               3 :   return 1;
     105                 : }
     106                 : 
     107               3 : void NaClSignalStackFree(void *stack) {
     108               3 :   CHECK(stack != NULL);
     109               3 :   if (munmap(stack, SIGNAL_STACK_SIZE + STACK_GUARD_SIZE) != 0) {
     110               0 :     NaClLog(LOG_FATAL, "Failed to munmap() signal stack:\n\t%s\n",
     111                 :       strerror(errno));
     112                 :   }
     113               3 : }
     114                 : 
     115               3 : void NaClSignalStackRegister(void *stack) {
     116                 :   /*
     117                 :    * If we set up signal handlers, we must ensure that any thread that
     118                 :    * runs untrusted code has an alternate signal stack set up.  The
     119                 :    * default for a new thread is to use the stack pointer from the
     120                 :    * point at which the fault occurs, but it would not be safe to use
     121                 :    * untrusted code's %esp/%rsp value.
     122                 :    */
     123                 :   stack_t st;
     124               3 :   st.ss_size = SIGNAL_STACK_SIZE;
     125               3 :   st.ss_sp = ((uint8_t *) stack) + STACK_GUARD_SIZE;
     126               3 :   st.ss_flags = 0;
     127               3 :   if (sigaltstack(&st, NULL) != 0) {
     128               0 :     NaClLog(LOG_FATAL, "Failed to register signal stack:\n\t%s\n",
     129                 :       strerror(errno));
     130                 :   }
     131               3 : }
     132                 : 
     133               3 : void NaClSignalStackUnregister(void) {
     134                 :   /*
     135                 :    * Unregister the signal stack in case a fault occurs between the
     136                 :    * thread deallocating the signal stack and exiting.  Such a fault
     137                 :    * could be unsafe if the address space were reallocated before the
     138                 :    * fault, although that is unlikely.
     139                 :    */
     140                 :   stack_t st;
     141                 : #if NACL_OSX
     142                 :   /*
     143                 :    * This is a workaround for a bug in Mac OS X's libc, in which new
     144                 :    * versions of the sigaltstack() wrapper return ENOMEM if ss_size is
     145                 :    * less than MINSIGSTKSZ, even when ss_size should be ignored
     146                 :    * because we are unregistering the signal stack.
     147                 :    * See http://code.google.com/p/nativeclient/issues/detail?id=1053
     148                 :    */
     149               3 :   st.ss_size = MINSIGSTKSZ;
     150                 : #else
     151                 :   st.ss_size = 0;
     152                 : #endif
     153               3 :   st.ss_sp = NULL;
     154               3 :   st.ss_flags = SS_DISABLE;
     155               3 :   if (sigaltstack(&st, NULL) != 0) {
     156               0 :     NaClLog(LOG_FATAL, "Failed to unregister signal stack:\n\t%s\n",
     157                 :       strerror(errno));
     158                 :   }
     159               3 : }
     160                 : 
     161               2 : static void FindAndRunHandler(int sig, siginfo_t *info, void *uc) {
     162               2 :   if (NaClSignalHandlerFind(sig, uc) == NACL_SIGNAL_SEARCH) {
     163                 :     int a;
     164                 : 
     165                 :     /* If we need to keep searching, try the old signal handler. */
     166               0 :     for (a = 0; a < SIGNAL_COUNT; a++) {
     167                 :       /* If we handle this signal */
     168               0 :       if (s_Signals[a] == sig) {
     169                 :         /* If this is a real sigaction pointer... */
     170               0 :         if (s_OldActions[a].sa_flags & SA_SIGINFO) {
     171                 :           /* then call the old handler. */
     172               0 :           s_OldActions[a].sa_sigaction(sig, info, uc);
     173               0 :           break;
     174                 :         }
     175                 :         /* otherwise check if it is a real signal pointer */
     176               0 :         if ((s_OldActions[a].sa_handler != SIG_DFL) &&
     177                 :             (s_OldActions[a].sa_handler != SIG_IGN)) {
     178                 :           /* and call the old signal. */
     179               0 :           s_OldActions[a].sa_handler(sig);
     180               0 :           break;
     181                 :         }
     182                 :         /*
     183                 :          * We matched the signal, but didn't handle it, so we emulate
     184                 :          * the default behavior which is to exit the app with the signal
     185                 :          * number as the error code.
     186                 :          */
     187               0 :         NaClSignalErrorMessage("Failed to handle signal.\n");
     188               0 :         NaClExit(-sig);
     189                 :       }
     190                 :     }
     191                 :   }
     192               0 : }
     193                 : 
     194                 : /*
     195                 :  * This function checks whether we can dispatch the signal to an
     196                 :  * untrusted exception handler.  If we can, it modifies the register
     197                 :  * state to call the handler and writes a stack frame into into
     198                 :  * untrusted address space, and returns true.  Otherwise, it returns
     199                 :  * false.
     200                 :  */
     201                 : static int DispatchToUntrustedHandler(struct NaClAppThread *natp,
     202               0 :                                       struct NaClSignalContext *regs) {
     203               0 :   struct NaClApp *nap = natp->nap;
     204                 :   uintptr_t frame_addr;
     205                 :   volatile struct ExceptionFrame *frame;
     206                 :   uint32_t new_stack_ptr;
     207                 :   /*
     208                 :    * Returning from the exception handler is not possible, so to avoid
     209                 :    * any confusion that might arise from jumping to an uninitialised
     210                 :    * address, we set the return address to zero.
     211                 :    */
     212               0 :   const uint32_t kReturnAddr = 0;
     213                 : 
     214               0 :   if (nap->exception_handler == 0) {
     215               0 :     return 0;
     216                 :   }
     217               0 :   if (natp->user.exception_flag) {
     218               0 :     return 0;
     219                 :   }
     220                 : 
     221               0 :   natp->user.exception_flag = 1;
     222                 : 
     223               0 :   if (natp->user.exception_stack == 0) {
     224               0 :     new_stack_ptr = regs->stack_ptr - NACL_STACK_RED_ZONE;
     225                 :   } else {
     226               0 :     new_stack_ptr = natp->user.exception_stack;
     227                 :   }
     228                 :   /* Allocate space for the stack frame, and ensure its alignment. */
     229               0 :   new_stack_ptr -= sizeof(struct ExceptionFrame) - NACL_STACK_PAD_BELOW_ALIGN;
     230               0 :   new_stack_ptr = new_stack_ptr & ~NACL_STACK_ALIGN_MASK;
     231               0 :   new_stack_ptr -= NACL_STACK_PAD_BELOW_ALIGN;
     232               0 :   frame_addr = NaClUserToSysAddrRange(nap, new_stack_ptr,
     233                 :                                       sizeof(struct ExceptionFrame));
     234               0 :   if (frame_addr == kNaClBadAddress) {
     235                 :     /* We cannot write the stack frame. */
     236               0 :     return 0;
     237                 :   }
     238               0 :   frame = (struct ExceptionFrame *) frame_addr;
     239                 : 
     240                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
     241               0 :   frame->return_addr = kReturnAddr;
     242               0 :   regs->flags &= ~NACL_X86_DIRECTION_FLAG;
     243                 : #endif
     244                 : 
     245                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
     246               0 :   frame->prog_ctr = regs->prog_ctr; /* Argument 1 */
     247               0 :   frame->stack_ptr = regs->stack_ptr; /* Argument 2 */
     248                 : 
     249               0 :   regs->prog_ctr = nap->exception_handler;
     250               0 :   regs->stack_ptr = new_stack_ptr;
     251                 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
     252                 :   /* Truncate the saved %rsp and %rbp values for portability. */
     253                 :   regs->rdi = (uint32_t) regs->prog_ctr; /* Argument 1 */
     254                 :   regs->rsi = (uint32_t) regs->stack_ptr; /* Argument 2 */
     255                 : 
     256                 :   regs->prog_ctr = NaClUserToSys(nap, nap->exception_handler);
     257                 :   regs->stack_ptr = NaClUserToSys(nap, new_stack_ptr);
     258                 :   /*
     259                 :    * We cannot leave %rbp unmodified because the x86-64 sandbox allows
     260                 :    * %rbp to point temporarily to the lower 4GB of address space, and
     261                 :    * we could have received an asynchronous signal while %rbp is in
     262                 :    * this state.  Even SIGSEGV can be asynchronous if sent with
     263                 :    * kill().
     264                 :    *
     265                 :    * For now, reset %rbp to zero in untrusted address space.  In the
     266                 :    * future, we might want to allow the stack to be unwound past the
     267                 :    * exception frame, and so we might want to treat %rbp differently.
     268                 :    */
     269                 :   regs->rbp = nap->mem_start;
     270                 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
     271                 :   /*
     272                 :    * We write to memory here to get consistent behaviour for
     273                 :    * unwritable stacks across architectures, to simplify testing.
     274                 :    * TODO(mseaborn): This will eventually be replaced with writing a
     275                 :    * register dump to the stack frame.
     276                 :    */
     277                 :   frame->dummy = 0;
     278                 : 
     279                 :   regs->lr = kReturnAddr;
     280                 : 
     281                 :   regs->r0 = regs->prog_ctr; /* Argument 1 */
     282                 :   regs->r1 = regs->stack_ptr; /* Argument 2 */
     283                 : 
     284                 :   regs->prog_ctr = NaClUserToSys(nap, nap->exception_handler);
     285                 :   regs->stack_ptr = NaClUserToSys(nap, new_stack_ptr);
     286                 : #else
     287                 : # error Unsupported architecture
     288                 : #endif
     289                 : 
     290               0 :   return 1;
     291                 : }
     292                 : 
     293               2 : static void SignalCatch(int sig, siginfo_t *info, void *uc) {
     294                 :   struct NaClSignalContext sigCtx;
     295                 :   int is_untrusted;
     296                 :   struct NaClAppThread *natp;
     297                 : 
     298                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
     299                 :   /*
     300                 :    * Reset the x86 direction flag.  New versions of gcc and libc
     301                 :    * assume that the direction flag is clear on entry to a function,
     302                 :    * as the x86 ABI requires.  However, untrusted code can set this
     303                 :    * flag, and versions of Linux before 2.6.25 do not clear the flag
     304                 :    * before running the signal handler, so we clear it here for safety.
     305                 :    * See http://code.google.com/p/nativeclient/issues/detail?id=1495
     306                 :    */
     307               2 :   __asm__("cld");
     308                 : #endif
     309                 : 
     310               2 :   NaClSignalContextFromHandler(&sigCtx, uc);
     311               2 :   NaClSignalContextGetCurrentThread(&sigCtx, &is_untrusted, &natp);
     312                 : 
     313                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
     314               2 :   if (is_untrusted) {
     315                 :     /*
     316                 :      * On Linux, the kernel does not restore %gs when entering the
     317                 :      * signal handler, so we must do that here.  We need to do this
     318                 :      * for glibc's TLS to work.  Some builds of glibc fetch a syscall
     319                 :      * function pointer from glibc's static TLS area.  If we failed to
     320                 :      * restore %gs, this function pointer would be under the control
     321                 :      * of untrusted code, and we would have a vulnerability.
     322                 :      *
     323                 :      * Note that, in comparison, Breakpad tries to avoid using libc
     324                 :      * calls at all when a crash occurs.
     325                 :      *
     326                 :      * On Mac OS X, the kernel *does* restore the original %gs when
     327                 :      * entering the signal handler.  Our assignment to %gs here is
     328                 :      * therefore not strictly necessary, but not harmful.  However,
     329                 :      * this does mean we need to check the original %gs value (from
     330                 :      * the signal frame) rather than the current %gs value (from
     331                 :      * NaClGetGs()).
     332                 :      *
     333                 :      * Both systems necessarily restore %cs, %ds, and %ss otherwise
     334                 :      * we would have a hard time handling signals in untrusted code
     335                 :      * at all.
     336                 :      */
     337               0 :     NaClSetGs(natp->sys.gs);
     338                 :   }
     339                 : #endif
     340                 : 
     341               2 :   if (is_untrusted && sig == SIGSEGV) {
     342               0 :     if (DispatchToUntrustedHandler(natp, &sigCtx)) {
     343               0 :       NaClSignalContextToHandler(uc, &sigCtx);
     344                 :       /* Resume untrusted code using the modified register state. */
     345               0 :       return;
     346                 :     }
     347                 :   }
     348                 : 
     349               2 :   FindAndRunHandler(sig, info, uc);
     350                 : }
     351                 : 
     352                 : 
     353              14 : void NaClSignalHandlerInitPlatform() {
     354                 :   struct sigaction sa;
     355                 :   int a;
     356                 : 
     357              14 :   memset(&sa, 0, sizeof(sa));
     358              14 :   sigemptyset(&sa.sa_mask);
     359              14 :   sa.sa_sigaction = SignalCatch;
     360              14 :   sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
     361                 : 
     362                 :   /* Mask all exceptions we catch to prevent re-entry */
     363             112 :   for (a = 0; a < SIGNAL_COUNT; a++) {
     364              98 :     sigaddset(&sa.sa_mask, s_Signals[a]);
     365                 :   }
     366                 : 
     367                 :   /* Install all handlers */
     368             112 :   for (a = 0; a < SIGNAL_COUNT; a++) {
     369              98 :     if (sigaction(s_Signals[a], &sa, &s_OldActions[a]) != 0) {
     370               0 :       NaClLog(LOG_FATAL, "Failed to install handler for %d.\n\tERR:%s\n",
     371                 :                           s_Signals[a], strerror(errno));
     372                 :     }
     373                 :   }
     374              14 : }
     375                 : 
     376              12 : void NaClSignalHandlerFiniPlatform() {
     377                 :   int a;
     378                 : 
     379                 :   /* Remove all handlers */
     380              96 :   for (a = 0; a < SIGNAL_COUNT; a++) {
     381              84 :     if (sigaction(s_Signals[a], &s_OldActions[a], NULL) != 0) {
     382               0 :       NaClLog(LOG_FATAL, "Failed to unregister handler for %d.\n\tERR:%s\n",
     383                 :                           s_Signals[a], strerror(errno));
     384                 :     }
     385                 :   }
     386              12 : }
     387                 : 
     388                 : /*
     389                 :  * Check that signal handlers are not registered.  We want to
     390                 :  * discourage Chrome or libraries from registering signal handlers
     391                 :  * themselves, because those signal handlers are often not safe when
     392                 :  * triggered from untrusted code.  For background, see:
     393                 :  * http://code.google.com/p/nativeclient/issues/detail?id=1607
     394                 :  */
     395              12 : void NaClSignalAssertNoHandlers() {
     396                 :   int index;
     397              96 :   for (index = 0; index < SIGNAL_COUNT; index++) {
     398              84 :     int signum = s_Signals[index];
     399                 :     struct sigaction sa;
     400              84 :     if (sigaction(signum, NULL, &sa) != 0) {
     401               0 :       NaClLog(LOG_FATAL, "NaClSignalAssertNoHandlers: "
     402                 :               "sigaction() call failed\n");
     403                 :     }
     404              84 :     if ((sa.sa_flags & SA_SIGINFO) != 0
     405                 :         ? sa.sa_sigaction != NULL
     406                 :         : (sa.sa_handler != SIG_DFL && sa.sa_handler != SIG_IGN)) {
     407               0 :       NaClLog(LOG_FATAL, "NaClSignalAssertNoHandlers: "
     408                 :               "A signal handler is registered for signal %d.  "
     409                 :               "Did Breakpad register this?\n", signum);
     410                 :     }
     411                 :   }
     412              12 : }

Generated by: LCOV version 1.7