LCOV - code coverage report
Current view: directory - src/trusted/service_runtime/linux - nacl_signal.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 119 92 77.3 %
Date: 2014-07-02 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 <stddef.h>
      10                 : #include <string.h>
      11                 : 
      12                 : #include "native_client/src/include/nacl_macros.h"
      13                 : #include "native_client/src/include/portability_io.h"
      14                 : #include "native_client/src/shared/platform/nacl_check.h"
      15                 : #include "native_client/src/shared/platform/nacl_exit.h"
      16                 : #include "native_client/src/shared/platform/nacl_log.h"
      17                 : #include "native_client/src/trusted/service_runtime/arch/sel_ldr_arch.h"
      18                 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
      19                 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
      20                 : #include "native_client/src/trusted/service_runtime/nacl_exception.h"
      21                 : #include "native_client/src/trusted/service_runtime/nacl_globals.h"
      22                 : #include "native_client/src/trusted/service_runtime/nacl_signal.h"
      23                 : #include "native_client/src/trusted/service_runtime/nacl_tls.h"
      24                 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
      25                 : #include "native_client/src/trusted/service_runtime/sel_rt.h"
      26                 : #include "native_client/src/trusted/service_runtime/thread_suspension.h"
      27                 : 
      28                 : 
      29                 : /*
      30                 :  * This module is based on the Posix signal model.  See:
      31                 :  * http://www.opengroup.org/onlinepubs/009695399/functions/sigaction.html
      32                 :  */
      33                 : 
      34                 : /*
      35                 :  * The signals listed here should either be handled by NaCl (or otherwise
      36                 :  * trusted) or have handlers that are expected to crash.
      37                 :  * Signals for which handlers are expected to crash should be listed
      38                 :  * in the NaClSignalHandleCustomCrashHandler function below.
      39                 :  */
      40                 : static int s_Signals[] = {
      41                 : #if NACL_ARCH(NACL_BUILD_ARCH) != NACL_mips
      42                 :   /* This signal does not exist on MIPS. */
      43                 :   SIGSTKFLT,
      44                 : #endif
      45                 :   SIGSYS, /* Used to support a seccomp-bpf sandbox. */
      46                 :   NACL_THREAD_SUSPEND_SIGNAL,
      47                 :   SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV,
      48                 :   /* Handle SIGABRT in case someone sends it asynchronously using kill(). */
      49                 :   SIGABRT
      50                 : };
      51                 : 
      52                 : static struct sigaction s_OldActions[NACL_ARRAY_SIZE_UNSAFE(s_Signals)];
      53                 : 
      54                 : static NaClSignalHandler g_handler_func;
      55                 : 
      56               2 : void NaClSignalHandlerSet(NaClSignalHandler func) {
      57               2 :   g_handler_func = func;
      58               2 : }
      59                 : 
      60                 : /*
      61                 :  * Returns, via is_untrusted, whether the signal happened while
      62                 :  * executing untrusted code.
      63                 :  *
      64                 :  * Returns, via result_thread, the NaClAppThread that untrusted code
      65                 :  * was running in.
      66                 :  *
      67                 :  * Note that this should only be called from the thread in which the
      68                 :  * signal occurred, because on x86-64 it reads a thread-local variable
      69                 :  * (nacl_current_thread).
      70                 :  */
      71          140375 : static void GetCurrentThread(const struct NaClSignalContext *sig_ctx,
      72                 :                              int *is_untrusted,
      73                 :                              struct NaClAppThread **result_thread) {
      74                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
      75                 :   /*
      76                 :    * For x86-32, if %cs does not match, it is untrusted code.
      77                 :    *
      78                 :    * Note that this check might not be valid on Mac OS X, because
      79                 :    * thread_get_state() does not return the value of NaClGetGlobalCs()
      80                 :    * for a thread suspended inside a syscall.  However, this code is
      81                 :    * not used on Mac OS X.
      82                 :    */
      83          140375 :   *is_untrusted = (NaClGetGlobalCs() != sig_ctx->cs);
      84          140375 :   *result_thread = NaClAppThreadGetFromIndex(sig_ctx->gs >> 3);
      85                 : #elif (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64) || \
      86                 :       NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm || \
      87                 :       NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
      88                 :   struct NaClAppThread *natp = NaClTlsGetCurrentThread();
      89                 :   if (natp == NULL) {
      90                 :     *is_untrusted = 0;
      91                 :     *result_thread = NULL;
      92                 :   } else {
      93                 :     /*
      94                 :      * Get the address of an arbitrary local, stack-allocated variable,
      95                 :      * just for the purpose of doing a sanity check.
      96                 :      */
      97                 :     void *pointer_into_stack = &natp;
      98                 :     /*
      99                 :      * Sanity check: Make sure the stack we are running on is not
     100                 :      * allocated in untrusted memory.  This checks that the alternate
     101                 :      * signal stack is correctly set up, because otherwise, if it is
     102                 :      * not set up, the test case would not detect that.
     103                 :      *
     104                 :      * There is little point in doing a CHECK instead of a DCHECK,
     105                 :      * because if we are running off an untrusted stack, we have already
     106                 :      * lost.
     107                 :      */
     108                 :     DCHECK(!NaClIsUserAddr(natp->nap, (uintptr_t) pointer_into_stack));
     109                 :     *is_untrusted = NaClIsUserAddr(natp->nap, sig_ctx->prog_ctr);
     110                 :     *result_thread = natp;
     111                 :   }
     112                 : #else
     113                 : # error Unsupported architecture
     114                 : #endif
     115                 : 
     116                 :   /*
     117                 :    * Trusted code could accidentally jump into sandbox address space,
     118                 :    * so don't rely on prog_ctr on its own for determining whether a
     119                 :    * crash comes from untrusted code.  We don't want to restore
     120                 :    * control to an untrusted exception handler if trusted code
     121                 :    * crashes.
     122                 :    */
     123          258985 :   if (*is_untrusted &&
     124          118610 :       ((*result_thread)->suspend_state & NACL_APP_THREAD_UNTRUSTED) == 0) {
     125               0 :     *is_untrusted = 0;
     126                 :   }
     127          140375 : }
     128                 : 
     129                 : /*
     130                 :  * If |sig| had a handler that was expected to crash, exit.
     131                 :  */
     132               0 : static void NaClSignalHandleCustomCrashHandler(int sig) {
     133                 :   /* Only SIGSYS is expected to have a custom crash handler. */
     134               0 :   if (sig == SIGSYS) {
     135                 :     char tmp[128];
     136               0 :     SNPRINTF(tmp, sizeof(tmp),
     137                 :         "\n** Signal %d has a custom crash handler but did not crash.\n",
     138                 :         sig);
     139               0 :     NaClSignalErrorMessage(tmp);
     140               0 :     NaClExit(-sig);
     141                 :   }
     142               0 : }
     143                 : 
     144               2 : static void FindAndRunHandler(int sig, siginfo_t *info, void *uc) {
     145                 :   unsigned int a;
     146                 : 
     147                 :   /* If we need to keep searching, try the old signal handler. */
     148              20 :   for (a = 0; a < NACL_ARRAY_SIZE(s_Signals); a++) {
     149                 :     /* If we handle this signal */
     150              20 :     if (s_Signals[a] == sig) {
     151                 :       /* If this is a real sigaction pointer... */
     152               2 :       if ((s_OldActions[a].sa_flags & SA_SIGINFO) != 0) {
     153                 :         /*
     154                 :          * On Mac OS X, sigaction() can return a "struct sigaction"
     155                 :          * with SA_SIGINFO set but with a NULL sa_sigaction if no
     156                 :          * signal handler was previously registered.  This is allowed
     157                 :          * by POSIX, which does not require a struct returned by
     158                 :          * sigaction() to be intelligible.  We check for NULL here to
     159                 :          * avoid a crash.
     160                 :          */
     161               0 :         if (s_OldActions[a].sa_sigaction != NULL) {
     162                 :           /* then call the old handler. */
     163               0 :           s_OldActions[a].sa_sigaction(sig, info, uc);
     164               0 :           NaClSignalHandleCustomCrashHandler(sig);
     165               0 :           break;
     166                 :         }
     167                 :       } else {
     168                 :         /* otherwise check if it is a real signal pointer */
     169               2 :         if ((s_OldActions[a].sa_handler != SIG_DFL) &&
     170               0 :             (s_OldActions[a].sa_handler != SIG_IGN)) {
     171                 :           /* and call the old signal. */
     172               0 :           s_OldActions[a].sa_handler(sig);
     173               0 :           NaClSignalHandleCustomCrashHandler(sig);
     174               0 :           break;
     175                 :         }
     176                 :       }
     177                 :       /*
     178                 :        * We matched the signal, but didn't handle it, so we emulate
     179                 :        * the default behavior which is to exit the app with the signal
     180                 :        * number as the error code.
     181                 :        */
     182               2 :       NaClExit(-sig);
     183                 :     }
     184                 :   }
     185               0 : }
     186                 : 
     187                 : /*
     188                 :  * This function checks whether we can dispatch the signal to an
     189                 :  * untrusted exception handler.  If we can, it modifies the register
     190                 :  * state to call the handler and writes a stack frame into into
     191                 :  * untrusted address space, and returns true.  Otherwise, it returns
     192                 :  * false.
     193                 :  */
     194          115363 : static int DispatchToUntrustedHandler(struct NaClAppThread *natp,
     195                 :                                       struct NaClSignalContext *regs) {
     196          115363 :   struct NaClApp *nap = natp->nap;
     197                 :   uintptr_t frame_addr;
     198                 :   volatile struct NaClExceptionFrame *frame;
     199                 :   uint32_t new_stack_ptr;
     200                 :   uintptr_t context_user_addr;
     201                 : 
     202          115363 :   if (!NaClSignalCheckSandboxInvariants(regs, natp)) {
     203               0 :     return 0;
     204                 :   }
     205          115363 :   if (nap->exception_handler == 0) {
     206              12 :     return 0;
     207                 :   }
     208          115351 :   if (natp->exception_flag) {
     209               1 :     return 0;
     210                 :   }
     211                 : 
     212          115350 :   natp->exception_flag = 1;
     213                 : 
     214          115350 :   if (natp->exception_stack == 0) {
     215          115309 :     new_stack_ptr = regs->stack_ptr - NACL_STACK_RED_ZONE;
     216                 :   } else {
     217              41 :     new_stack_ptr = natp->exception_stack;
     218                 :   }
     219                 :   /* Allocate space for the stack frame, and ensure its alignment. */
     220          115350 :   new_stack_ptr -=
     221                 :       sizeof(struct NaClExceptionFrame) - NACL_STACK_PAD_BELOW_ALIGN;
     222          115350 :   new_stack_ptr = new_stack_ptr & ~NACL_STACK_ALIGN_MASK;
     223          115350 :   new_stack_ptr -= NACL_STACK_ARGS_SIZE;
     224          115350 :   new_stack_ptr -= NACL_STACK_PAD_BELOW_ALIGN;
     225          115350 :   frame_addr = NaClUserToSysAddrRange(nap, new_stack_ptr,
     226                 :                                       sizeof(struct NaClExceptionFrame));
     227          115350 :   if (frame_addr == kNaClBadAddress) {
     228                 :     /* We cannot write the stack frame. */
     229               1 :     return 0;
     230                 :   }
     231          115349 :   context_user_addr = new_stack_ptr + offsetof(struct NaClExceptionFrame,
     232                 :                                                context);
     233                 : 
     234          115349 :   frame = (struct NaClExceptionFrame *) frame_addr;
     235          115349 :   NaClSignalSetUpExceptionFrame(frame, regs, context_user_addr);
     236                 : 
     237                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
     238          115349 :   regs->prog_ctr = nap->exception_handler;
     239          115349 :   regs->stack_ptr = new_stack_ptr;
     240                 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
     241                 :   regs->rdi = context_user_addr; /* Argument 1 */
     242                 :   regs->prog_ctr = NaClUserToSys(nap, nap->exception_handler);
     243                 :   regs->stack_ptr = NaClUserToSys(nap, new_stack_ptr);
     244                 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
     245                 :   /*
     246                 :    * Returning from the exception handler is not possible, so to avoid
     247                 :    * any confusion that might arise from jumping to an uninitialised
     248                 :    * address, we set the return address to zero.
     249                 :    */
     250                 :   regs->lr = 0;
     251                 :   regs->r0 = context_user_addr;  /* Argument 1 */
     252                 :   regs->prog_ctr = NaClUserToSys(nap, nap->exception_handler);
     253                 :   regs->stack_ptr = NaClUserToSys(nap, new_stack_ptr);
     254                 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
     255                 :   regs->return_addr = 0;
     256                 :   regs->a0 = context_user_addr;
     257                 :   regs->prog_ctr = NaClUserToSys(nap, nap->exception_handler);
     258                 :   regs->stack_ptr = NaClUserToSys(nap, new_stack_ptr);
     259                 :   /*
     260                 :    * Per Linux/MIPS convention, PIC functions assume that t9 holds
     261                 :    * the function's address on entry.
     262                 :    */
     263                 :   regs->t9 = regs->prog_ctr;
     264                 : #else
     265                 : # error Unsupported architecture
     266                 : #endif
     267                 : 
     268                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
     269          115349 :   regs->flags &= ~NACL_X86_DIRECTION_FLAG;
     270                 : #endif
     271                 : 
     272          115349 :   return 1;
     273                 : }
     274                 : 
     275          140375 : static void SignalCatch(int sig, siginfo_t *info, void *uc) {
     276                 :   struct NaClSignalContext sig_ctx;
     277                 :   int is_untrusted;
     278                 :   struct NaClAppThread *natp;
     279                 : 
     280                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
     281                 :   /*
     282                 :    * Reset the x86 direction flag.  New versions of gcc and libc
     283                 :    * assume that the direction flag is clear on entry to a function,
     284                 :    * as the x86 ABI requires.  However, untrusted code can set this
     285                 :    * flag, and versions of Linux before 2.6.25 do not clear the flag
     286                 :    * before running the signal handler, so we clear it here for safety.
     287                 :    * See http://code.google.com/p/nativeclient/issues/detail?id=1495
     288                 :    */
     289          140375 :   __asm__("cld");
     290                 : #endif
     291                 : 
     292          140375 :   NaClSignalContextFromHandler(&sig_ctx, uc);
     293          140375 :   GetCurrentThread(&sig_ctx, &is_untrusted, &natp);
     294                 : 
     295                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
     296                 :   /*
     297                 :    * On Linux, the kernel does not restore %gs when entering the
     298                 :    * signal handler, so we must do that here.  We need to do this for
     299                 :    * TLS to work and for glibc's syscall wrappers to work, because
     300                 :    * some builds of glibc fetch a syscall function pointer from the
     301                 :    * static TLS area.  There is the potential for vulnerabilities if
     302                 :    * we call glibc without restoring %gs (such as
     303                 :    * http://code.google.com/p/nativeclient/issues/detail?id=1607),
     304                 :    * although the risk is reduced because the untrusted %gs segment
     305                 :    * has an extent of only 4 bytes (see
     306                 :    * http://code.google.com/p/nativeclient/issues/detail?id=2176).
     307                 :    *
     308                 :    * Note that, in comparison, Breakpad tries to avoid using libc
     309                 :    * calls at all when a crash occurs.
     310                 :    *
     311                 :    * For comparison, on Mac OS X, the kernel *does* restore the
     312                 :    * original %gs when entering the signal handler.  On Mac, our
     313                 :    * assignment to %gs here wouldn't be necessary, but it wouldn't be
     314                 :    * harmful either.  However, this code is not currently used on Mac
     315                 :    * OS X.
     316                 :    *
     317                 :    * Both Linux and Mac OS X necessarily restore %cs, %ds, and %ss
     318                 :    * otherwise we would have a hard time handling signals generated by
     319                 :    * untrusted code at all.
     320                 :    *
     321                 :    * Note that we check natp (which is based on %gs) rather than
     322                 :    * is_untrusted (which is based on %cs) because we need to handle
     323                 :    * the case where %gs is set to the untrusted-code value but %cs is
     324                 :    * not.
     325                 :    *
     326                 :    * GCC's stack protector (-fstack-protector) will make use of %gs even before
     327                 :    * we have a chance to restore it. It is important that this function is not
     328                 :    * compiled with -fstack-protector.
     329                 :    */
     330          140375 :   if (natp != NULL) {
     331          121191 :     NaClSetGs(natp->user.trusted_gs);
     332                 :   }
     333                 : #endif
     334                 : 
     335          140375 :   if (sig != SIGINT && sig != SIGQUIT) {
     336          140375 :     if (NaClThreadSuspensionSignalHandler(sig, &sig_ctx, is_untrusted, natp)) {
     337           10594 :       NaClSignalContextToHandler(uc, &sig_ctx);
     338                 :       /* Resume untrusted code using possibly modified register state. */
     339           10594 :       return;
     340                 :     }
     341                 :   }
     342                 : 
     343          129764 :   if (is_untrusted &&
     344            3057 :       (sig == SIGSEGV || sig == SIGILL || sig == SIGFPE ||
     345                 :        (NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips && sig == SIGTRAP))) {
     346          115363 :     if (DispatchToUntrustedHandler(natp, &sig_ctx)) {
     347          115349 :       NaClSignalContextToHandler(uc, &sig_ctx);
     348                 :       /* Resume untrusted code using the modified register state. */
     349          115349 :       return;
     350                 :     }
     351                 :   }
     352                 : 
     353           14415 :   if (g_handler_func != NULL) {
     354           14399 :     g_handler_func(sig, &sig_ctx, is_untrusted);
     355           14399 :     return;
     356                 :   }
     357                 : 
     358              16 :   NaClSignalHandleUntrusted(sig, &sig_ctx, is_untrusted);
     359                 : 
     360               2 :   FindAndRunHandler(sig, info, uc);
     361                 : }
     362                 : 
     363                 : 
     364                 : /*
     365                 :  * Check that the current process has no signal handlers registered
     366                 :  * that we won't override with safe handlers.
     367                 :  *
     368                 :  * We want to discourage Chrome or libraries from registering signal
     369                 :  * handlers themselves, because those signal handlers are often not
     370                 :  * safe when triggered from untrusted code.  For background, see:
     371                 :  * http://code.google.com/p/nativeclient/issues/detail?id=1607
     372                 :  */
     373             289 : static void AssertNoOtherSignalHandlers(void) {
     374                 :   unsigned int index;
     375                 :   int signum;
     376                 :   char handled_by_nacl[NSIG];
     377                 : 
     378                 :   /* 0 is not a valid signal number. */
     379           18785 :   for (signum = 1; signum < NSIG; signum++) {
     380           18496 :     handled_by_nacl[signum] = 0;
     381                 :   }
     382            3468 :   for (index = 0; index < NACL_ARRAY_SIZE(s_Signals); index++) {
     383            3179 :     signum = s_Signals[index];
     384            3179 :     CHECK(signum > 0);
     385            3179 :     CHECK(signum < NSIG);
     386            3179 :     handled_by_nacl[signum] = 1;
     387                 :   }
     388           18785 :   for (signum = 1; signum < NSIG; signum++) {
     389                 :     struct sigaction sa;
     390                 : 
     391           18496 :     if (handled_by_nacl[signum])
     392            3179 :       continue;
     393                 : 
     394           15317 :     if (sigaction(signum, NULL, &sa) != 0) {
     395                 :       /*
     396                 :        * Don't complain if the kernel does not consider signum to be a
     397                 :        * valid signal number, which produces EINVAL.
     398                 :        */
     399             578 :       if (errno != EINVAL) {
     400               0 :         NaClLog(LOG_FATAL, "AssertNoOtherSignalHandlers: "
     401                 :                 "sigaction() call failed for signal %d: errno=%d\n",
     402               0 :                 signum, errno);
     403                 :       }
     404                 :     } else {
     405           14739 :       if ((sa.sa_flags & SA_SIGINFO) == 0) {
     406           14739 :         if (sa.sa_handler == SIG_DFL || sa.sa_handler == SIG_IGN)
     407           14739 :           continue;
     408                 :       } else {
     409                 :         /*
     410                 :          * It is not strictly legal for sa_sigaction to contain NULL
     411                 :          * or SIG_IGN, but Valgrind reports SIG_IGN for signal 64, so
     412                 :          * we allow it here.
     413                 :          */
     414               0 :         if (sa.sa_sigaction == NULL ||
     415               0 :             sa.sa_sigaction == (void (*)(int, siginfo_t *, void *)) SIG_IGN)
     416               0 :           continue;
     417                 :       }
     418               0 :       NaClLog(LOG_FATAL, "AssertNoOtherSignalHandlers: "
     419                 :               "A signal handler is registered for signal %d\n", signum);
     420                 :     }
     421                 :   }
     422             289 : }
     423                 : 
     424             289 : void NaClSignalHandlerInit(void) {
     425                 :   struct sigaction sa;
     426                 :   unsigned int a;
     427                 : 
     428                 :   /*
     429                 :    * Android adds a handler for SIGPIPE in the dynamic linker.
     430                 :    */
     431                 :   if (NACL_ANDROID)
     432                 :     CHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
     433                 : 
     434             289 :   AssertNoOtherSignalHandlers();
     435                 : 
     436             289 :   memset(&sa, 0, sizeof(sa));
     437             289 :   sigemptyset(&sa.sa_mask);
     438             289 :   sa.sa_sigaction = SignalCatch;
     439             289 :   sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
     440                 : 
     441                 :   /*
     442                 :    * Mask all signals we catch to prevent re-entry.
     443                 :    *
     444                 :    * In particular, NACL_THREAD_SUSPEND_SIGNAL must be masked while we
     445                 :    * are handling a fault from untrusted code, otherwise the
     446                 :    * suspension signal will interrupt the trusted fault handler.  That
     447                 :    * would cause NaClAppThreadGetSuspendedRegisters() to report
     448                 :    * trusted-code register state rather than untrusted-code register
     449                 :    * state from the point where the fault occurred.
     450                 :    */
     451            3468 :   for (a = 0; a < NACL_ARRAY_SIZE(s_Signals); a++) {
     452            3179 :     sigaddset(&sa.sa_mask, s_Signals[a]);
     453                 :   }
     454                 : 
     455                 :   /* Install all handlers */
     456            3468 :   for (a = 0; a < NACL_ARRAY_SIZE(s_Signals); a++) {
     457            3179 :     if (sigaction(s_Signals[a], &sa, &s_OldActions[a]) != 0) {
     458               0 :       NaClLog(LOG_FATAL, "Failed to install handler for %d.\n\tERR:%s\n",
     459               0 :                           s_Signals[a], strerror(errno));
     460                 :     }
     461                 :   }
     462             289 : }
     463                 : 
     464              11 : void NaClSignalHandlerFini(void) {
     465                 :   unsigned int a;
     466                 : 
     467                 :   /* Remove all handlers */
     468             132 :   for (a = 0; a < NACL_ARRAY_SIZE(s_Signals); a++) {
     469             121 :     if (sigaction(s_Signals[a], &s_OldActions[a], NULL) != 0) {
     470               0 :       NaClLog(LOG_FATAL, "Failed to unregister handler for %d.\n\tERR:%s\n",
     471               0 :                           s_Signals[a], strerror(errno));
     472                 :     }
     473                 :   }
     474              11 : }

Generated by: LCOV version 1.7