LCOV - code coverage report
Current view: directory - src/trusted/service_runtime/osx - mach_exception_handler.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 241 167 69.3 %
Date: 2014-06-18 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 "native_client/src/trusted/service_runtime/osx/mach_exception_handler.h"
       8                 : 
       9                 : #include <mach/mach.h>
      10                 : #include <mach/mach_vm.h>
      11                 : #include <mach/thread_status.h>
      12                 : #include <pthread.h>
      13                 : #include <stddef.h>
      14                 : #include <stdio.h>
      15                 : #include <stdlib.h>
      16                 : 
      17                 : #include "native_client/src/include/nacl_macros.h"
      18                 : #include "native_client/src/include/portability.h"
      19                 : #include "native_client/src/shared/platform/nacl_check.h"
      20                 : #include "native_client/src/shared/platform/nacl_log.h"
      21                 : #include "native_client/src/trusted/service_runtime/arch/sel_ldr_arch.h"
      22                 : #include "native_client/src/trusted/service_runtime/nacl_app.h"
      23                 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
      24                 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
      25                 : #include "native_client/src/trusted/service_runtime/nacl_exc.h"
      26                 : #include "native_client/src/trusted/service_runtime/nacl_exception.h"
      27                 : #include "native_client/src/trusted/service_runtime/nacl_globals.h"
      28                 : #include "native_client/src/trusted/service_runtime/nacl_switch_to_app.h"
      29                 : #include "native_client/src/trusted/service_runtime/nacl_tls.h"
      30                 : #include "native_client/src/trusted/service_runtime/osx/crash_filter.h"
      31                 : #include "native_client/src/trusted/service_runtime/osx/mach_thread_map.h"
      32                 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
      33                 : #include "native_client/src/trusted/service_runtime/sel_rt.h"
      34                 : 
      35                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
      36                 : 
      37                 : 
      38                 : /*
      39                 :  * MIG generated message pump from /usr/include/mach/exc.defs
      40                 :  * Tweaked to place in an isolated namespace.
      41                 :  */
      42                 : boolean_t nacl_exc_server(
      43                 :     mach_msg_header_t *InHeadP,
      44                 :     mach_msg_header_t *OutHeadP);
      45                 : 
      46                 : 
      47                 : /* Exception types to intercept. */
      48                 : #define NACL_MACH_EXCEPTION_MASK \
      49                 :     (EXC_MASK_BAD_ACCESS | \
      50                 :      EXC_MASK_BAD_INSTRUCTION | \
      51                 :      EXC_MASK_ARITHMETIC | \
      52                 :      EXC_MASK_BREAKPOINT)
      53                 : 
      54                 : /*
      55                 :  * <mach/mach_types.defs> says that the arrays in MachExceptionParameters have
      56                 :  * room for 32 elements.  EXC_TYPES_COUNT, a smaller value, has grown over
      57                 :  * time in major SDK releases.  task_get_exception_ports expects there to be
      58                 :  * room for all 32 and is not bounded by EXC_TYPES_COUNT or by the initial
      59                 :  * value of its handler_count argument, contrary to its documentation.
      60                 :  * Provide a constant matching its limit to avoid potential buffer overflow.
      61                 :  */
      62                 : #define NACL_MAX_EXCEPTION_PORTS 32
      63                 : 
      64                 : struct MachExceptionParameters {
      65                 :   mach_msg_type_number_t count;
      66                 :   exception_mask_t masks[NACL_MAX_EXCEPTION_PORTS];
      67                 :   mach_port_t ports[NACL_MAX_EXCEPTION_PORTS];
      68                 :   exception_behavior_t behaviors[NACL_MAX_EXCEPTION_PORTS];
      69                 :   thread_state_flavor_t flavors[NACL_MAX_EXCEPTION_PORTS];
      70                 : };
      71                 : 
      72                 : struct MachExceptionHandlerData {
      73                 :   struct MachExceptionParameters old_ports;
      74                 :   mach_port_t exception_port;
      75                 : };
      76                 : 
      77                 : /*
      78                 :  * This is global because MIG does not provide a mechanism to associate
      79                 :  * user data with a handler. We could 'sed' the output, but that might be
      80                 :  * brittle. This is moot as the handler is task wide. Revisit if we switch to
      81                 :  * a per-thread handler.
      82                 :  */
      83                 : static struct MachExceptionHandlerData *g_MachExceptionHandlerData = 0;
      84                 : 
      85                 : 
      86              91 : static int ExceptionCodeToNaClSignalNumber(exception_type_t exception) {
      87              91 :   switch (exception) {
      88                 :     case EXC_BREAKPOINT:
      89              41 :       return NACL_ABI_SIGTRAP;
      90                 :     default:
      91              50 :       return NACL_ABI_SIGSEGV;
      92                 :   }
      93              91 : }
      94                 : 
      95              91 : static void FireDebugStubEvent(int pipe_fd) {
      96              91 :   char buf = 0;
      97              91 :   if (write(pipe_fd, &buf, sizeof(buf)) != sizeof(buf)) {
      98               0 :     NaClLog(LOG_FATAL, "FireDebugStubEvent: Can't send debug stub event\n");
      99               0 :   }
     100              91 : }
     101                 : 
     102                 : #if NACL_BUILD_SUBARCH == 32
     103                 : 
     104                 : #define NATIVE_x86_THREAD_STATE x86_THREAD_STATE32
     105                 : #define X86_REG_SP(regs)    ((regs)->uts.ts32.__esp)
     106                 : #define X86_REG_IP(regs)    ((regs)->uts.ts32.__eip)
     107                 : #define X86_REG_FLAGS(regs) ((regs)->uts.ts32.__eflags)
     108                 : 
     109                 : #elif NACL_BUILD_SUBARCH == 64
     110                 : 
     111                 : #define NATIVE_x86_THREAD_STATE x86_THREAD_STATE64
     112                 : #define X86_REG_SP(regs)    ((regs)->uts.ts64.__rsp)
     113                 : #define X86_REG_IP(regs)    ((regs)->uts.ts64.__rip)
     114                 : #define X86_REG_FLAGS(regs) ((regs)->uts.ts64.__rflags)
     115                 : 
     116                 : #endif  /* NACL_BUILD_SUBARCH */
     117                 : 
     118                 : enum HandleExceptionResult {
     119                 :   kHandleExceptionUnhandled = 0,
     120                 :   kHandleExceptionHandled_SetState,
     121                 :   kHandleExceptionHandled_DontSetState
     122                 : };
     123                 : 
     124           15016 : static enum HandleExceptionResult HandleException(mach_port_t thread_port,
     125           15016 :                                                   exception_type_t exception,
     126           15016 :                                                   int *is_untrusted,
     127           15016 :                                                   x86_thread_state_t *regs) {
     128           15016 :   kern_return_t result;
     129           15016 :   uint32_t nacl_thread_index;
     130           15016 :   struct NaClApp *nap;
     131           15016 :   struct NaClAppThread *natp;
     132           15016 :   struct NaClExceptionFrame frame;
     133           15016 :   uint32_t frame_addr_user;
     134           15016 :   uintptr_t frame_addr_sys;
     135           15016 :   struct NaClSignalContext sig_context;
     136                 : #if NACL_BUILD_SUBARCH == 32
     137                 :   uint16_t trusted_cs = NaClGetGlobalCs();
     138                 :   uint16_t trusted_ds = NaClGetGlobalDs();
     139                 : #endif
     140                 : 
     141           45048 :   CHECK(regs->tsh.flavor == NATIVE_x86_THREAD_STATE);
     142                 : 
     143                 :   /* Assume untrusted crash until we know otherwise. */
     144           15016 :   *is_untrusted = TRUE;
     145                 : 
     146                 : #if NACL_BUILD_SUBARCH == 32
     147                 :   /*
     148                 :    * We can get the thread index from the segment selector used for TLS
     149                 :    * from %gs >> 3.
     150                 :    * TODO(bradnelson): Migrate that knowledge to a single shared location.
     151                 :    */
     152                 :   nacl_thread_index = regs->uts.ts32.__gs >> 3;
     153                 : #elif NACL_BUILD_SUBARCH == 64
     154           15016 :   nacl_thread_index = NaClGetThreadIndexForMachThread(thread_port);
     155                 : 
     156           15016 :   if (nacl_thread_index == NACL_TLS_INDEX_INVALID) {
     157               1 :     *is_untrusted = FALSE;
     158               1 :     return kHandleExceptionUnhandled;
     159                 :   }
     160                 : #endif
     161                 : 
     162           15015 :   natp = NaClAppThreadGetFromIndex(nacl_thread_index);
     163           15015 :   if (natp == NULL) {
     164               0 :     *is_untrusted = FALSE;
     165               0 :     return kHandleExceptionUnhandled;
     166                 :   }
     167           15015 :   nap = natp->nap;
     168                 : 
     169                 :   /*
     170                 :    * TODO(bradnelson): For x86_32, this makes the potentially false assumption
     171                 :    *     that cs is the last thing to change when switching into untrusted
     172                 :    *     code. We need tests to vet this.
     173                 :    */
     174           15015 :   *is_untrusted = NaClMachThreadStateIsInUntrusted(regs, nacl_thread_index);
     175                 : 
     176                 :   /*
     177                 :    * If trusted code accidentally jumped to untrusted code, don't let the
     178                 :    * untrusted exception handler take over.
     179                 :    */
     180           30029 :   if (*is_untrusted &&
     181                 :       (natp->suspend_state & NACL_APP_THREAD_UNTRUSTED) == 0) {
     182               0 :     *is_untrusted = 0;
     183               0 :     return kHandleExceptionUnhandled;
     184                 :   }
     185                 : 
     186           15015 :   if (!*is_untrusted) {
     187                 : #if NACL_BUILD_SUBARCH == 32
     188                 :     /*
     189                 :      * If we are single-stepping, allow NaClSwitchRemainingRegsViaECX()
     190                 :      * to continue in order to restore control to untrusted code.
     191                 :      */
     192                 :     if (exception == EXC_BREAKPOINT &&
     193                 :         (X86_REG_FLAGS(regs) & NACL_X86_TRAP_FLAG) != 0 &&
     194                 :         X86_REG_IP(regs) >= (uintptr_t) NaClSwitchRemainingRegsViaECX &&
     195                 :         X86_REG_IP(regs) < (uintptr_t) NaClSwitchRemainingRegsAsmEnd) {
     196                 :       return kHandleExceptionHandled_DontSetState;
     197                 :     }
     198                 : #endif
     199               1 :     return kHandleExceptionUnhandled;
     200                 :   }
     201                 : 
     202           15014 :   if (nap->enable_faulted_thread_queue) {
     203                 : #if NACL_BUILD_SUBARCH == 32
     204                 :     /*
     205                 :      * If we are single-stepping, step through until we reach untrusted code.
     206                 :      */
     207                 :     if (exception == EXC_BREAKPOINT &&
     208                 :         (X86_REG_FLAGS(regs) & NACL_X86_TRAP_FLAG) != 0) {
     209                 :       if (X86_REG_IP(regs) >= nap->all_regs_springboard.start_addr &&
     210                 :           X86_REG_IP(regs) < nap->all_regs_springboard.end_addr) {
     211                 :         return kHandleExceptionHandled_DontSetState;
     212                 :       }
     213                 :       /*
     214                 :        * Step through the instruction we have been asked to restore
     215                 :        * control to.
     216                 :        */
     217                 :       if (X86_REG_IP(regs) == natp->user.gs_segment.new_prog_ctr) {
     218                 :         return kHandleExceptionHandled_DontSetState;
     219                 :       }
     220                 :     }
     221                 : #endif
     222                 : 
     223                 :     /*
     224                 :      * Increment the kernel's thread suspension count so that the
     225                 :      * thread remains suspended after we return.
     226                 :      */
     227              91 :     result = thread_suspend(thread_port);
     228              91 :     if (result != KERN_SUCCESS) {
     229               0 :       NaClLog(LOG_FATAL, "HandleException: thread_suspend() call failed\n");
     230               0 :     }
     231                 :     /*
     232                 :      * Notify the handler running on another thread.  This must happen
     233                 :      * after the thread_suspend() call, otherwise the handler might
     234                 :      * receive the notification and attempt to decrement the thread's
     235                 :      * suspension count before we have incremented it.
     236                 :      */
     237              91 :     natp->fault_signal = ExceptionCodeToNaClSignalNumber(exception);
     238              91 :     AtomicIncrement(&nap->faulted_thread_count, 1);
     239              91 :     FireDebugStubEvent(nap->faulted_thread_fd_write);
     240              91 :     return kHandleExceptionHandled_DontSetState;
     241                 :   }
     242                 : 
     243           14927 :   if (exception != EXC_BAD_ACCESS &&
     244                 :       exception != EXC_BAD_INSTRUCTION &&
     245                 :       exception != EXC_ARITHMETIC) {
     246               0 :     return kHandleExceptionUnhandled;
     247                 :   }
     248                 : 
     249           14923 :   NaClSignalContextFromMacThreadState(&sig_context, regs);
     250           14923 :   if (!NaClSignalCheckSandboxInvariants(&sig_context, natp)) {
     251               0 :     return kHandleExceptionUnhandled;
     252                 :   }
     253                 : 
     254                 :   /* Don't handle if no exception handler is set. */
     255           14923 :   if (nap->exception_handler == 0) {
     256               0 :     return kHandleExceptionUnhandled;
     257                 :   }
     258                 : 
     259                 :   /* Don't handle it if the exception flag is set. */
     260           14923 :   if (natp->exception_flag) {
     261               0 :     return kHandleExceptionUnhandled;
     262                 :   }
     263                 :   /* Set the flag. */
     264           14923 :   natp->exception_flag = 1;
     265                 : 
     266                 :   /* Get location of exception stack frame. */
     267           14923 :   if (natp->exception_stack) {
     268              41 :     frame_addr_user = natp->exception_stack;
     269              41 :   } else {
     270                 :     /* If not set default to user stack. */
     271           14882 :     frame_addr_user = X86_REG_SP(regs) - NACL_STACK_RED_ZONE;
     272                 :   }
     273                 : 
     274                 :   /* Align stack frame properly. */
     275           14923 :   frame_addr_user -=
     276                 :       sizeof(struct NaClExceptionFrame) - NACL_STACK_PAD_BELOW_ALIGN;
     277           14923 :   frame_addr_user &= ~NACL_STACK_ALIGN_MASK;
     278           14923 :   frame_addr_user -= NACL_STACK_PAD_BELOW_ALIGN;
     279                 : 
     280                 :   /* Convert from user to system space. */
     281           14923 :   frame_addr_sys = NaClUserToSysAddrRange(
     282                 :       nap, frame_addr_user, sizeof(struct NaClExceptionFrame));
     283           14923 :   if (frame_addr_sys == kNaClBadAddress) {
     284               0 :     return kHandleExceptionUnhandled;
     285                 :   }
     286                 : 
     287                 :   /* Set up the stack frame for the handler invocation. */
     288           14923 :   NaClSignalSetUpExceptionFrame(
     289                 :       &frame, &sig_context,
     290                 :       frame_addr_user + offsetof(struct NaClExceptionFrame, context));
     291                 : 
     292                 :   /*
     293                 :    * Write the stack frame into untrusted address space.  We do not
     294                 :    * write to the memory directly because that will fault if the
     295                 :    * destination location is not writable.  Faulting is OK for NaCl
     296                 :    * syscalls, but here we do not want to trigger an exception while
     297                 :    * in the exception handler.  The overhead of using a Mach system
     298                 :    * call to write to memory, 2-3 microseconds on a 2.3GHz 4-core i7,
     299                 :    * is acceptable here.
     300                 :    */
     301           14923 :   result = mach_vm_write(mach_task_self(), frame_addr_sys,
     302                 :                          (uintptr_t) &frame, sizeof(frame));
     303           14923 :   if (result != KERN_SUCCESS) {
     304               0 :     return kHandleExceptionUnhandled;
     305                 :   }
     306                 : 
     307                 :   /* Set up thread context to resume at handler. */
     308                 :   /* TODO(bradnelson): put all registers in some default state. */
     309                 : #if NACL_BUILD_SUBARCH == 32
     310                 :   /*
     311                 :    * Put registers in right place to land at NaClSwitchNoSSEViaECX
     312                 :    * This is required because:
     313                 :    *   - For an unknown reason thread_set_state resets %cs to the default
     314                 :    *     value, even when set to something else, in current XNU versions.
     315                 :    *   - An examination of the XNU sources indicates
     316                 :    *     that setting the code which state the thread state resets
     317                 :    *     %cs, %ds, %es, %ss to their default values in some early versions.
     318                 :    *     (For instance: xnu-792.6.22/osfmk/i386/pcb.c:616)
     319                 :    * This precludes going directly to the untrusted handler.
     320                 :    * Instead we call a variant of NaClSwitchNoSSE which takes a pointer
     321                 :    * to the thread user context in %ecx.
     322                 :    */
     323                 :   natp->user.new_prog_ctr = nap->exception_handler;
     324                 :   natp->user.stack_ptr = frame_addr_user;
     325                 :   X86_REG_IP(regs) = (uint32_t) &NaClSwitchNoSSEViaECX;
     326                 :   regs->uts.ts32.__cs = trusted_cs;
     327                 :   regs->uts.ts32.__ecx = (uint32_t) &natp->user;
     328                 :   regs->uts.ts32.__ds = trusted_ds;
     329                 :   regs->uts.ts32.__es = trusted_ds;  /* just for good measure */
     330                 :   regs->uts.ts32.__ss = trusted_ds;  /* just for good measure */
     331                 : #elif NACL_BUILD_SUBARCH == 64
     332           14923 :   X86_REG_IP(regs) = NaClUserToSys(nap, nap->exception_handler);
     333           14923 :   X86_REG_SP(regs) = frame_addr_sys;
     334                 : 
     335                 :   /* Argument 1 */
     336           14923 :   regs->uts.ts64.__rdi = frame_addr_user +
     337                 :                          offsetof(struct NaClExceptionFrame, context);
     338                 : #endif
     339           14923 :   X86_REG_FLAGS(regs) &= ~NACL_X86_DIRECTION_FLAG;
     340                 : 
     341                 :   /* Return success, and resume the thread. */
     342           14923 :   return kHandleExceptionHandled_SetState;
     343           15016 : }
     344                 : 
     345                 : 
     346                 : static kern_return_t ForwardException(
     347               2 :     struct MachExceptionHandlerData *data,
     348               2 :     mach_port_t thread,
     349               2 :     mach_port_t task,
     350               2 :     exception_type_t exception,
     351               2 :     exception_data_t code,
     352               2 :     mach_msg_type_number_t code_count) {
     353               2 :   unsigned int i;
     354               2 :   mach_port_t target_port;
     355               2 :   exception_behavior_t target_behavior;
     356               2 :   kern_return_t kr;
     357                 : 
     358                 :   /* Find a port with a mask matching this exception to pass it on to. */
     359               4 :   for (i = 0; i < data->old_ports.count; ++i) {
     360               2 :     if (data->old_ports.masks[i] & (1 << exception)) {
     361               2 :       break;
     362                 :     }
     363               0 :   }
     364               2 :   if (i == data->old_ports.count) {
     365               0 :     return KERN_FAILURE;
     366                 :   }
     367               2 :   target_port = data->old_ports.ports[i];
     368               2 :   target_behavior = data->old_ports.behaviors[i];
     369                 : 
     370                 :   /*
     371                 :    * By default a null exception port is registered.
     372                 :    * As it is unclear how to forward to this, we should just fail.
     373                 :    */
     374               2 :   if (target_port == MACH_PORT_NULL) {
     375               0 :     return KERN_FAILURE;
     376                 :   }
     377                 : 
     378                 :   /*
     379                 :    * Only support EXCEPTION_DEFAULT, as we only plan to inter-operate with
     380                 :    * Breakpad for now.
     381                 :    */
     382               6 :   CHECK(target_behavior == EXCEPTION_DEFAULT);
     383                 : 
     384                 :   /* Forward the exception. */
     385               0 :   kr = exception_raise(target_port, thread, task, exception, code, code_count);
     386                 : 
     387                 :   /*
     388                 :    * Don't set the thread state. See the comment in
     389                 :    * nacl_catch_exception_raise_state_identity. Transforming KERN_SUCCESS to
     390                 :    * MACH_RCV_PORT_DIED is necessary because the exception was delivered with
     391                 :    * behavior EXCEPTION_STATE_IDENTITY to
     392                 :    * nacl_catch_exception_raise_state_identity, but it was forwarded to a
     393                 :    * handler with behavior EXCEPTION_DEFAULT via exception_raise, and such
     394                 :    * handlers don't provide a new thread state. They can set a new thread
     395                 :    * state on their own by calling thread_set_state. Returning KERN_SUCCESS
     396                 :    * would allow the old state passed to
     397                 :    * nacl_catch_exception_raise_state_identity to overwrite any new state set
     398                 :    * by the handler that the exception was forwarded to via exception_raise.
     399                 :    */
     400               0 :   return kr == KERN_SUCCESS ? MACH_RCV_PORT_DIED : kr;
     401               0 : }
     402                 : 
     403                 : 
     404                 : kern_return_t nacl_catch_exception_raise(
     405               0 :     mach_port_t exception_port,
     406               0 :     mach_port_t thread,
     407               0 :     mach_port_t task,
     408               0 :     exception_type_t exception,
     409               0 :     exception_data_t code,
     410               0 :     mach_msg_type_number_t code_count) {
     411                 :   /* MIG generated code expects this, but should never be called. */
     412               0 :   UNREFERENCED_PARAMETER(exception_port);
     413               0 :   UNREFERENCED_PARAMETER(thread);
     414               0 :   UNREFERENCED_PARAMETER(task);
     415               0 :   UNREFERENCED_PARAMETER(exception);
     416               0 :   UNREFERENCED_PARAMETER(code);
     417               0 :   UNREFERENCED_PARAMETER(code_count);
     418               0 :   NaClLog(LOG_FATAL, "nacl_catch_exception_raise: "
     419                 :                      "Unrequested message received.\n");
     420               0 :   return KERN_FAILURE;
     421                 : }
     422                 : 
     423                 : kern_return_t nacl_catch_exception_raise_state(
     424               0 :     mach_port_t exception_port,
     425               0 :     exception_type_t exception,
     426               0 :     const exception_data_t code,
     427               0 :     mach_msg_type_number_t code_count,
     428               0 :     int *flavor,
     429               0 :     const thread_state_t old_state,
     430               0 :     mach_msg_type_number_t old_state_count,
     431               0 :     thread_state_t new_state,
     432               0 :     mach_msg_type_number_t *new_state_count) {
     433                 :   /* MIG generated code expects this, but should never be called. */
     434               0 :   UNREFERENCED_PARAMETER(exception_port);
     435               0 :   UNREFERENCED_PARAMETER(exception);
     436               0 :   UNREFERENCED_PARAMETER(code);
     437               0 :   UNREFERENCED_PARAMETER(code_count);
     438               0 :   UNREFERENCED_PARAMETER(flavor);
     439               0 :   UNREFERENCED_PARAMETER(old_state);
     440               0 :   UNREFERENCED_PARAMETER(old_state_count);
     441               0 :   UNREFERENCED_PARAMETER(new_state);
     442               0 :   UNREFERENCED_PARAMETER(new_state_count);
     443               0 :   NaClLog(LOG_FATAL, "nacl_catch_exception_raise_state: "
     444                 :                      "Unrequested message received.\n");
     445               0 :   return KERN_FAILURE;
     446                 : }
     447                 : 
     448               2 : static void PrintCrashMessage(int exception_type, int is_untrusted,
     449               2 :                               x86_thread_state_t *regs) {
     450               2 :   char buf[128];
     451               2 :   int len = snprintf(
     452                 :       buf, sizeof(buf),
     453                 :       "\n** Mach exception %d from %s code: pc=%" NACL_PRIx64 "\n",
     454                 :       exception_type,
     455                 :       is_untrusted ? "untrusted" : "trusted",
     456                 :       (uint64_t) X86_REG_IP(regs));
     457               2 :   write(2, buf, len);
     458               2 : }
     459                 : 
     460                 : kern_return_t nacl_catch_exception_raise_state_identity (
     461           15016 :         mach_port_t exception_port,
     462           15016 :         mach_port_t thread,
     463           15016 :         mach_port_t task,
     464           15016 :         exception_type_t exception,
     465           15016 :         exception_data_t code,
     466           15016 :         mach_msg_type_number_t code_count,
     467           15016 :         int *flavor,
     468           15016 :         thread_state_t old_state,
     469           15016 :         mach_msg_type_number_t old_state_count,
     470           15016 :         thread_state_t new_state,
     471           15016 :         mach_msg_type_number_t *new_state_count) {
     472           15016 :   int rv;
     473           15016 :   int is_untrusted;
     474                 : 
     475           60064 :   DCHECK(exception_port == g_MachExceptionHandlerData->exception_port);
     476                 : 
     477           45048 :   CHECK(*flavor == x86_THREAD_STATE);
     478           45048 :   CHECK(old_state_count == x86_THREAD_STATE_COUNT);
     479           45048 :   CHECK(*new_state_count >= x86_THREAD_STATE_COUNT);
     480           15016 :   *new_state_count = x86_THREAD_STATE_COUNT;
     481           45048 :   memcpy(new_state, old_state, x86_THREAD_STATE_COUNT * sizeof(natural_t));
     482                 : 
     483                 :   /* Check if we want to handle this exception. */
     484           15016 :   rv = HandleException(thread,
     485                 :                        exception,
     486                 :                        &is_untrusted,
     487                 :                        (x86_thread_state_t *) new_state);
     488           15016 :   if (rv == kHandleExceptionHandled_SetState) {
     489           14923 :     return KERN_SUCCESS;
     490              93 :   } else if (rv == kHandleExceptionHandled_DontSetState) {
     491                 :     /*
     492                 :      * To avoid setting the thread state, return MACH_RCV_PORT_DIED. In
     493                 :      * exception_deliver, the kernel will only set the thread state if
     494                 :      * exception_raise_state returns MACH_MSG_SUCCESS (== KERN_SUCCESS), so
     495                 :      * another value is needed to avoid having it set the state. KERN_SUCCESS
     496                 :      * and MACH_RCV_PORT_DIED are the only two values that don't result in task
     497                 :      * termination in exception_triage. See 10.8.2
     498                 :      * xnu-2050.18.24/osfmk/kern/exception.c.
     499                 :      *
     500                 :      * This is done instead of letting the kernel set the new thread state to
     501                 :      * be the same as the old state because the kernel resets %cs to the
     502                 :      * default value when setting a thread's state. This behavior is explained
     503                 :      * in more detail in HandleException.
     504                 :      */
     505              91 :     return MACH_RCV_PORT_DIED;
     506                 :   }
     507                 : 
     508               2 :   PrintCrashMessage(exception, is_untrusted, (x86_thread_state_t *) old_state);
     509                 : 
     510                 :   /*
     511                 :    * Don't forward if the crash is untrusted, but unhandled.
     512                 :    * (As we don't want things like Breakpad handling the crash.)
     513                 :    */
     514               2 :   if (is_untrusted) {
     515               0 :     return KERN_FAILURE;
     516                 :   }
     517                 : 
     518                 :   /* Forward on the exception to the old set of ports. */
     519               2 :   return ForwardException(
     520                 :       g_MachExceptionHandlerData, thread, task, exception, code, code_count);
     521           15014 : }
     522                 : 
     523              31 : static void *MachExceptionHandlerThread(void *arg) {
     524              31 :   struct MachExceptionHandlerData *data =
     525                 :       (struct MachExceptionHandlerData *) arg;
     526              31 :   kern_return_t result;
     527                 :   union {
     528                 :     mach_msg_header_t header;
     529                 :     union __RequestUnion__nacl_exc_subsystem nacl_exc_subsystem_request;
     530              31 :   } request;
     531                 :   union {
     532                 :     mach_msg_header_t header;
     533                 :     union __ReplyUnion__nacl_exc_subsystem nacl_exc_subsystem_reply;
     534              31 :   } reply;
     535                 : 
     536              31 :   for (;;) {
     537           15016 :     result = mach_msg(&request.header, MACH_RCV_MSG | MACH_RCV_LARGE, 0,
     538                 :                       sizeof(request), data->exception_port,
     539                 :                       MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
     540           15016 :     if (result != MACH_MSG_SUCCESS) {
     541               0 :       goto failure;
     542                 :     }
     543           15014 :     if (!nacl_exc_server(&request.header, &reply.header)) {
     544               0 :       goto failure;
     545                 :     }
     546           15014 :     result = mach_msg(&reply.header, MACH_SEND_MSG,
     547                 :                       reply.header.msgh_size, 0,
     548                 :                       MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
     549           15014 :     if (result != MACH_MSG_SUCCESS) {
     550               0 :       goto failure;
     551                 :     }
     552           15014 :   }
     553                 : 
     554                 : failure:
     555               0 :   free(data);
     556                 : 
     557               0 :   return 0;
     558                 : }
     559                 : 
     560              31 : static int InstallHandler(struct MachExceptionHandlerData *data) {
     561              31 :   kern_return_t result;
     562              31 :   mach_port_t current_task = mach_task_self();
     563              31 :   unsigned int i;
     564                 : 
     565                 :   /* Capture old handler info. */
     566              31 :   data->old_ports.count = NACL_MAX_EXCEPTION_PORTS;
     567              31 :   result = task_get_exception_ports(current_task, NACL_MACH_EXCEPTION_MASK,
     568                 :                                     data->old_ports.masks,
     569                 :                                     &data->old_ports.count,
     570                 :                                     data->old_ports.ports,
     571                 :                                     data->old_ports.behaviors,
     572                 :                                     data->old_ports.flavors);
     573              31 :   if (result != KERN_SUCCESS) {
     574               0 :     return result;
     575                 :   }
     576                 : 
     577                 :   /*
     578                 :    * We only handle forwarding of the EXCEPTION_DEFAULT behavior (all that
     579                 :    * Breakpad needs). Check that all old handlers are either of this behavior
     580                 :    * type or null.
     581                 :    *
     582                 :    * NOTE: Ideally we might also require a particular behavior for null
     583                 :    * exception ports. Unfortunately, testing indicates that while on
     584                 :    * OSX 10.6 / 10.7 the behavior for such a null port is set to 0,
     585                 :    * on OSX 10.5 it is set to 0x803fe956 (on a given run).
     586                 :    * As tasks inherit exception ports from their parents, this may be
     587                 :    * an uninitialized value carried along from a parent.
     588                 :    * http://opensource.apple.com/source/xnu/xnu-1228.0.2/osfmk/kern/ipc_tt.c
     589                 :    * For now, we will ignore the behavior when the port is null.
     590                 :    */
     591             124 :   for (i = 0; i < data->old_ports.count; ++i) {
     592             122 :     CHECK(data->old_ports.behaviors[i] == EXCEPTION_DEFAULT ||
     593                 :           data->old_ports.ports[i] == MACH_PORT_NULL);
     594              31 :   }
     595                 : 
     596                 :   /* TODO(bradnelson): decide if we should set the exception port per thread. */
     597                 :   /* Direct all task exceptions to new exception port. */
     598              31 :   result = task_set_exception_ports(current_task, NACL_MACH_EXCEPTION_MASK,
     599                 :                                     data->exception_port,
     600                 :                                     EXCEPTION_STATE_IDENTITY,
     601                 :                                     x86_THREAD_STATE);
     602              31 :   if (result != KERN_SUCCESS) {
     603               0 :     return result;
     604                 :   }
     605                 : 
     606              31 :   return KERN_SUCCESS;
     607              31 : }
     608                 : 
     609                 : int NaClInterceptMachExceptions(void) {
     610              31 :   struct MachExceptionHandlerData *data;
     611              31 :   kern_return_t result;
     612              31 :   mach_port_t current_task;
     613              31 :   pthread_attr_t attr;
     614              31 :   int thread_result;
     615              31 :   pthread_t exception_handler_thread;
     616                 : 
     617              31 :   current_task = mach_task_self();
     618                 : 
     619                 :   /* Allocate structure to share with exception handler thread. */
     620              31 :   data = (struct MachExceptionHandlerData *) calloc(1, sizeof(*data));
     621              31 :   if (data == NULL) {
     622               0 :     goto failure;
     623                 :   }
     624              31 :   g_MachExceptionHandlerData = data;
     625              31 :   data->exception_port = MACH_PORT_NULL;
     626                 : 
     627                 :   /* Allocate port to receive exceptions. */
     628              31 :   result = mach_port_allocate(current_task, MACH_PORT_RIGHT_RECEIVE,
     629                 :                               &data->exception_port);
     630              31 :   if (result != KERN_SUCCESS) {
     631               0 :     goto failure;
     632                 :   }
     633                 : 
     634                 :   /* Add the right to send. */
     635              31 :   result = mach_port_insert_right(current_task,
     636                 :                                   data->exception_port, data->exception_port,
     637                 :                                   MACH_MSG_TYPE_MAKE_SEND);
     638              31 :   if (result != KERN_SUCCESS) {
     639               0 :     goto failure;
     640                 :   }
     641                 : 
     642                 :   /* Install handler. */
     643              31 :   result = InstallHandler(data);
     644              31 :   if (result != KERN_SUCCESS) {
     645               0 :     goto failure;
     646                 :   }
     647                 : 
     648                 :   /* Create handler thread. */
     649              31 :   pthread_attr_init(&attr);
     650              31 :   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
     651              31 :   thread_result = pthread_create(&exception_handler_thread, &attr,
     652                 :                                  &MachExceptionHandlerThread, data);
     653              31 :   pthread_attr_destroy(&attr);
     654              31 :   if (thread_result) {
     655               0 :     goto failure;
     656                 :   }
     657                 : 
     658              31 :   return TRUE;
     659                 : 
     660                 : failure:
     661               0 :   if (data) {
     662               0 :     if (MACH_PORT_NULL != data->exception_port) {
     663               0 :       mach_port_deallocate(current_task, data->exception_port);
     664               0 :     }
     665               0 :     free(data);
     666               0 :   }
     667               0 :   return FALSE;
     668              31 : }
     669                 : 
     670                 : #endif  /* NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 */

Generated by: LCOV version 1.7