LCOV - code coverage report
Current view: directory - src/trusted/service_runtime/osx - thread_suspension.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 75 63 84.0 %
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 <mach/mach.h>
       8                 : #include <mach/thread_status.h>
       9                 : 
      10                 : #include "native_client/src/shared/platform/nacl_check.h"
      11                 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
      12                 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
      13                 : #include "native_client/src/trusted/service_runtime/nacl_switch_to_app.h"
      14                 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
      15                 : #include "native_client/src/trusted/service_runtime/thread_suspension.h"
      16                 : 
      17                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
      18                 : # include "native_client/src/trusted/service_runtime/arch/x86_32/nacl_switch_all_regs_32.h"
      19                 : #endif
      20                 : 
      21                 : 
      22                 : struct NaClAppThreadSuspendedRegisters {
      23                 :   x86_thread_state_t context;
      24                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
      25                 :   struct NaClSwitchRemainingRegsState switch_state;
      26                 : #endif
      27                 : };
      28                 : 
      29           12326 : static mach_port_t GetHostThreadPort(struct NaClAppThread *natp) {
      30           36978 :   CHECK(natp->host_thread_is_defined);
      31           12326 :   return pthread_mach_thread_np(natp->host_thread.tid);
      32                 : }
      33                 : 
      34         7008361 : void NaClAppThreadSetSuspendState(struct NaClAppThread *natp,
      35         7008361 :                                   enum NaClSuspendState old_state,
      36         7008361 :                                   enum NaClSuspendState new_state) {
      37                 :   /*
      38                 :    * Claiming suspend_mu here blocks a trusted/untrusted context
      39                 :    * switch while the thread is suspended or a suspension is in
      40                 :    * progress.
      41                 :    */
      42         7008361 :   NaClXMutexLock(&natp->suspend_mu);
      43        28033430 :   DCHECK(natp->suspend_state == (Atomic32) old_state);
      44         7008356 :   natp->suspend_state = new_state;
      45         7008356 :   NaClXMutexUnlock(&natp->suspend_mu);
      46         7008356 : }
      47                 : 
      48           12404 : void NaClUntrustedThreadSuspend(struct NaClAppThread *natp,
      49           12404 :                                 int save_registers) {
      50                 :   /*
      51                 :    * We claim suspend_mu here to block trusted/untrusted context
      52                 :    * switches by blocking NaClAppThreadSetSuspendState().  This blocks
      53                 :    * any untrusted->trusted context switch that might happen before
      54                 :    * SuspendThread() takes effect.  It blocks any trusted->untrusted
      55                 :    * context switch that might happen if the syscall running in the
      56                 :    * target thread returns.
      57                 :    */
      58           12404 :   NaClXMutexLock(&natp->suspend_mu);
      59           12404 :   if (natp->suspend_state == NACL_APP_THREAD_UNTRUSTED) {
      60            6086 :     kern_return_t result;
      61            6086 :     mach_msg_type_number_t size;
      62            6086 :     mach_port_t thread_port = GetHostThreadPort(natp);
      63                 : 
      64            6086 :     result = thread_suspend(thread_port);
      65            6086 :     if (result != KERN_SUCCESS) {
      66               0 :       NaClLog(LOG_FATAL, "NaClUntrustedThreadSuspend: "
      67                 :               "thread_suspend() call failed: error %d\n", (int) result);
      68               0 :     }
      69                 : 
      70            6086 :     if (save_registers) {
      71            5845 :       if (natp->suspended_registers == NULL) {
      72              25 :         natp->suspended_registers = malloc(sizeof(*natp->suspended_registers));
      73              25 :         if (natp->suspended_registers == NULL) {
      74               0 :           NaClLog(LOG_FATAL, "NaClUntrustedThreadSuspend: malloc() failed\n");
      75               0 :         }
      76              25 :       }
      77                 : 
      78            5845 :       size = sizeof(natp->suspended_registers->context) / sizeof(natural_t);
      79            5845 :       result = thread_get_state(thread_port, x86_THREAD_STATE,
      80                 :                                 (void *) &natp->suspended_registers->context,
      81                 :                                 &size);
      82            5845 :       if (result != KERN_SUCCESS) {
      83               0 :         NaClLog(LOG_FATAL, "NaClUntrustedThreadSuspend: "
      84                 :                 "thread_get_state() call failed: error %d\n", (int) result);
      85               0 :       }
      86            5845 :     }
      87            6086 :   }
      88                 :   /*
      89                 :    * We leave suspend_mu held so that NaClAppThreadSetSuspendState()
      90                 :    * will block.
      91                 :    */
      92           12404 : }
      93                 : 
      94           12387 : void NaClUntrustedThreadResume(struct NaClAppThread *natp) {
      95           12387 :   if (natp->suspend_state == NACL_APP_THREAD_UNTRUSTED) {
      96            6071 :     kern_return_t result = thread_resume(GetHostThreadPort(natp));
      97            6071 :     if (result != KERN_SUCCESS) {
      98               0 :       NaClLog(LOG_FATAL, "NaClUntrustedThreadResume: "
      99                 :               "thread_resume() call failed: error %d\n", (int) result);
     100               0 :     }
     101            6071 :   }
     102           12387 :   NaClXMutexUnlock(&natp->suspend_mu);
     103           12387 : }
     104                 : 
     105                 : void NaClAppThreadGetSuspendedRegistersInternal(
     106           11674 :     struct NaClAppThread *natp, struct NaClSignalContext *regs) {
     107                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
     108                 :   /*
     109                 :    * We might have suspended the thread while it is returning to
     110                 :    * untrusted code via NaClSwitchRemainingRegsViaECX() and
     111                 :    * NaCl_springboard_all_regs.  This is particularly likely for a
     112                 :    * faulted thread that has been resumed and suspended again without
     113                 :    * ever being unblocked by NaClAppThreadUnblockIfFaulted().
     114                 :    *
     115                 :    * In this situation, we must undo the register state modifications
     116                 :    * made by NaClAppThreadSetSuspendedRegistersInternal().
     117                 :    */
     118                 :   struct NaClAppThreadSuspendedRegisters *state = natp->suspended_registers;
     119                 :   struct NaClApp *nap = natp->nap;
     120                 :   uint32_t eip = state->context.uts.ts32.__eip;
     121                 :   if ((state->context.uts.ts32.__cs == NaClGetGlobalCs() &&
     122                 :        eip >= (uintptr_t) NaClSwitchRemainingRegsViaECX &&
     123                 :        eip < (uintptr_t) NaClSwitchRemainingRegsAsmEnd) ||
     124                 :       (state->context.uts.ts32.__cs == natp->user.cs &&
     125                 :        eip >= nap->all_regs_springboard.start_addr &&
     126                 :        eip < nap->all_regs_springboard.end_addr)) {
     127                 :     state->context.uts.ts32.__eip = natp->user.gs_segment.new_prog_ctr;
     128                 :     state->context.uts.ts32.__ecx = natp->user.gs_segment.new_ecx;
     129                 :     /*
     130                 :      * It is sometimes necessary to restore the following registers
     131                 :      * too, depending on how far we are through
     132                 :      * NaClSwitchRemainingRegsViaECX().
     133                 :      */
     134                 :     state->context.uts.ts32.__cs = natp->user.cs;
     135                 :     state->context.uts.ts32.__ds = natp->user.ds;
     136                 :     state->context.uts.ts32.__es = natp->user.es;
     137                 :     state->context.uts.ts32.__fs = natp->user.fs;
     138                 :     state->context.uts.ts32.__gs = natp->user.gs;
     139                 :     state->context.uts.ts32.__ss = natp->user.ss;
     140                 :   }
     141                 : #endif
     142                 : 
     143           11674 :   NaClSignalContextFromMacThreadState(regs,
     144                 :                                       &natp->suspended_registers->context);
     145           11674 : }
     146                 : 
     147                 : void NaClAppThreadSetSuspendedRegistersInternal(
     148              78 :     struct NaClAppThread *natp, const struct NaClSignalContext *regs) {
     149              78 :   kern_return_t result;
     150              78 :   mach_msg_type_number_t size;
     151              78 :   struct NaClAppThreadSuspendedRegisters *state = natp->suspended_registers;
     152              78 :   x86_thread_state_t context_copy;
     153                 : 
     154              78 :   NaClSignalContextToMacThreadState(&state->context, regs);
     155              78 :   context_copy = state->context;
     156                 : 
     157                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
     158                 :   /*
     159                 :    * thread_set_state() ignores the %cs value we supply and always
     160                 :    * resets %cs back to the trusted-code value.  This means we must
     161                 :    * set up the new untrusted register state via a trusted code
     162                 :    * routine which returns to untrusted code via a springboard.
     163                 :    *
     164                 :    * We reset %cs here in case the Mac kernel is ever fixed to not
     165                 :    * ignore the supplied %cs value.
     166                 :    */
     167                 :   context_copy.uts.ts32.__cs = NaClGetGlobalCs();
     168                 :   context_copy.uts.ts32.__ds = NaClGetGlobalDs();
     169                 :   /* Reset these too just in case. */
     170                 :   context_copy.uts.ts32.__es = NaClGetGlobalDs();
     171                 :   context_copy.uts.ts32.__ss = NaClGetGlobalDs();
     172                 :   context_copy.uts.ts32.__ecx = (uintptr_t) &state->switch_state;
     173                 :   context_copy.uts.ts32.__eip = (uintptr_t) NaClSwitchRemainingRegsViaECX;
     174                 :   NaClSwitchRemainingRegsSetup(&state->switch_state, natp, regs);
     175                 : #endif
     176                 : 
     177              78 :   size = sizeof(context_copy) / sizeof(natural_t);
     178              78 :   result = thread_set_state(GetHostThreadPort(natp), x86_THREAD_STATE,
     179                 :                             (void *) &context_copy, size);
     180              78 :   if (result != KERN_SUCCESS) {
     181               0 :     NaClLog(LOG_FATAL, "NaClAppThreadSetSuspendedRegistersInternal: "
     182                 :             "thread_set_state() call failed: error %d\n", result);
     183               0 :   }
     184              78 : }
     185                 : 
     186              92 : int NaClAppThreadUnblockIfFaulted(struct NaClAppThread *natp, int *signal) {
     187              92 :   kern_return_t result;
     188              92 :   if (natp->fault_signal == 0) {
     189               1 :     return 0;
     190                 :   }
     191              91 :   *signal = natp->fault_signal;
     192              91 :   natp->fault_signal = 0;
     193              91 :   AtomicIncrement(&natp->nap->faulted_thread_count, -1);
     194                 :   /*
     195                 :    * Decrement the kernel's suspension count for the thread.  This
     196                 :    * undoes the effect of mach_exception_handler.c's thread_suspend()
     197                 :    * call.
     198                 :    */
     199              91 :   result = thread_resume(GetHostThreadPort(natp));
     200              91 :   if (result != KERN_SUCCESS) {
     201               0 :     NaClLog(LOG_FATAL, "NaClAppThreadUnblockIfFaulted: "
     202                 :             "thread_resume() call failed: error %d\n", (int) result);
     203               0 :   }
     204              91 :   return 1;
     205              92 : }

Generated by: LCOV version 1.7