Пример #1
0
void WINAPI NaClAppThreadLauncher(void *state) {
  struct NaClAppThread *natp = (struct NaClAppThread *) state;
  uint32_t thread_idx;
  NaClLog(4, "NaClAppThreadLauncher: entered\n");

  NaClSignalStackRegister(natp->signal_stack);

  NaClLog(4, "      natp = 0x%016"NACL_PRIxPTR"\n", (uintptr_t) natp);
  NaClLog(4, " prog_ctr  = 0x%016"NACL_PRIxNACL_REG"\n", natp->user.prog_ctr);
  NaClLog(4, "stack_ptr  = 0x%016"NACL_PRIxPTR"\n",
          NaClGetThreadCtxSp(&natp->user));

  thread_idx = NaClGetThreadIdx(natp);
  CHECK(0 < thread_idx);
  CHECK(thread_idx < NACL_THREAD_MAX);
  NaClTlsSetCurrentThread(natp);
  nacl_user[thread_idx] = &natp->user;
#if NACL_WINDOWS
  nacl_thread_ids[thread_idx] = GetCurrentThreadId();
#elif NACL_OSX
  NaClSetCurrentMachThreadForThreadIndex(thread_idx);
#endif

  /*
   * We have to hold the threads_mu lock until after thread_num field
   * in this thread has been initialized.  All other threads can only
   * find and examine this natp through the threads table, so the fact
   * that natp is not consistent (no thread_num) will not be visible.
   */
  NaClXMutexLock(&natp->nap->threads_mu);
  natp->thread_num = NaClAddThreadMu(natp->nap, natp);
  NaClXMutexUnlock(&natp->nap->threads_mu);

  NaClVmHoleThreadStackIsSafe(natp->nap);

  NaClStackSafetyNowOnUntrustedStack();

  /*
   * Notify the debug stub, that a new thread is availible.
   */
  if (NULL != natp->nap->debug_stub_callbacks) {
    natp->nap->debug_stub_callbacks->thread_create_hook(natp);
  }

  /*
   * After this NaClAppThreadSetSuspendState() call, we should not
   * claim any mutexes, otherwise we risk deadlock.
   */
  NaClAppThreadSetSuspendState(natp, NACL_APP_THREAD_TRUSTED,
                               NACL_APP_THREAD_UNTRUSTED);

  NaClStartThreadInApp(natp, natp->user.prog_ctr);
}
NORETURN void NaClStartThreadInApp(struct NaClAppThread *natp,
                                   nacl_reg_t           new_prog_ctr) {
  struct NaClApp            *nap;
  struct NaClThreadContext  *context;

#if !NACL_WINDOWS
  /*
   * Ensure stack alignment.  Stack pointer must be -8 mod 16 when no
   * __m256 objects are passed (8 mod 32 if __m256), after the call.
   * Note the current doc (as of 2009-12-09) at
   *
   *   http://www.x86-64.org/documentation/abi.pdf
   *
   * is wrong since it claims (%rsp-8) should be 0 mod 16 or mod 32
   * after the call, and it should be (%rsp+8) == 0 mod 16 or 32.
   * Clearly it makes no difference since -8 and 8 are the same mod
   * 16, but there is a difference when mod 32.
   *
   * This is not suitable for Windows because we do not reserve 32
   * bytes for the shadow space.
   */
  nacl_reg_t  secure_stack_ptr = NaClGetStackPtr();

  NaClLog(6,
          "NaClStartThreadInApp: secure stack:   0x%"NACL_PRIxNACL_REG"\n",
          secure_stack_ptr);
  secure_stack_ptr = secure_stack_ptr & ~0x1f;
  NaClLog(6,
          "NaClStartThreadInApp: adjusted stack: 0x%"NACL_PRIxNACL_REG"\n",
          secure_stack_ptr);

  natp->user.trusted_stack_ptr = secure_stack_ptr;
#endif

  nap = natp->nap;
  context = &natp->user;
  context->new_prog_ctr = new_prog_ctr;
  context->sysret = 0;
  context->r15 = nap->mem_start;

  NaClLog(6,
          "NaClStackThreadInApp: user stack: 0x%"NACL_PRIxPTR"\n",
          NaClGetThreadCtxSp(context));
  NaClLog(6,
          "NaClStartThreadInApp: switching to untrusted code\n");

#if NACL_WINDOWS
  /* This sets up a stack containing a return address that has unwind info. */
  NaClSwitchSavingStackPtr(context, &context->trusted_stack_ptr);
#else
  NaClSwitch(context);
#endif
}
/*
 * HandleStackContext() fetches some of the inputs to the NaCl syscall
 * from the untrusted stack.  It updates NaClThreadContext so that the
 * saved state will be complete in case this state is read via the
 * thread suspension API.
 *
 * This is called while natp->suspend_state is set to
 * NACL_APP_THREAD_UNTRUSTED, which has two consequences:
 *
 *  1) We may read untrusted address space without calling
 *     NaClCopyInTakeLock() first, because this function's execution
 *     will be suspended while any mmap hole is opened up on Windows.
 *
 *  2) We may not claim any locks.  This means we may not call
 *     NaClLog().  (An exception is that LOG_FATAL calls to NaClLog()
 *     should be okay for internal errors.)
 */
static void HandleStackContext(struct NaClAppThread *natp,
                               uintptr_t            *tramp_ret_out,
                               uintptr_t            *sp_user_out) {
  struct NaClApp *nap = natp->nap;
  uintptr_t      sp_user;
  uintptr_t      sp_sys;
  uintptr_t      tramp_ret;
  nacl_reg_t     user_ret;

  /*
   * sp_sys points to the top of the user stack where return addresses
   * and syscall arguments are stored.
   *
   * Note that on x86-64, NaClUserToSysStackAddr() and
   * NaClSysToUserStackAddr() do no range check.  sp_user must be okay
   * for control to have reached here, because nacl_syscall*.S writes
   * to the stack.
   */
  sp_user = NaClGetThreadCtxSp(&natp->user);
  sp_sys = NaClUserToSysStackAddr(nap, sp_user);
  /*
   * Get the trampoline return address.  This just tells us which
   * trampoline was called (and hence the syscall number); we never
   * return to the trampoline.
   */
  tramp_ret = *(uintptr_t *) (sp_sys + NACL_TRAMPRET_FIX);
  tramp_ret = NaClUserToSysStackAddr(nap, tramp_ret);
  /*
   * Get the user return address (where we return to after the system
   * call).  We must ensure the address is properly sandboxed before
   * switching back to untrusted code.
   */
  user_ret = *(uintptr_t *) (sp_sys + NACL_USERRET_FIX);
  user_ret = (nacl_reg_t) NaClSandboxCodeAddr(nap, (uintptr_t) user_ret);
  natp->user.new_prog_ctr = user_ret;

  *tramp_ret_out = tramp_ret;
  *sp_user_out = sp_user;
}
void WINAPI NaClThreadLauncher(void *state) {
  struct NaClAppThread *natp = (struct NaClAppThread *) state;
  NaClLog(4, "NaClThreadLauncher: entered\n");

  NaClSignalStackRegister(natp->signal_stack);

  NaClLog(4, "      natp = 0x%016"NACL_PRIxPTR"\n", (uintptr_t) natp);
  NaClLog(4, " prog_ctr  = 0x%016"NACL_PRIxNACL_REG"\n", natp->user.prog_ctr);
  NaClLog(4, "stack_ptr  = 0x%016"NACL_PRIxPTR"\n",
          NaClGetThreadCtxSp(&natp->user));

  NaClTlsSetIdx(NaClGetThreadIdx(natp));

  /*
   * We have to hold the threads_mu lock until after thread_num field
   * in this thread has been initialized.  All other threads can only
   * find and examine this natp through the threads table, so the fact
   * that natp is not consistent (no thread_num) will not be visible.
   */
  NaClXMutexLock(&natp->nap->threads_mu);
  natp->thread_num = NaClAddThreadMu(natp->nap, natp);
  NaClXMutexUnlock(&natp->nap->threads_mu);

  /*
   * Notify the debug stub, that a new thread is availible.
   */
   NaClDebugThreadPrepDebugging(natp);

  /*
   * We need to set an exception handler in every thread we start,
   * otherwise the system's default handler is called and a message box is
   * shown.
   */
  WINDOWS_EXCEPTION_TRY;
  NaClStartThreadInApp(natp, natp->user.prog_ctr);
  WINDOWS_EXCEPTION_CATCH;
}
Пример #5
0
/*
 * d'b: make syscall invoked from the untrusted code
 */
NORETURN void NaClSyscallCSegHook()
{
  struct NaClApp            *nap;
  struct NaClThreadContext  *user;
  uintptr_t                 tramp_ret;
  nacl_reg_t                user_ret;
  size_t                    sysnum;
  uintptr_t                 sp_user;
  uintptr_t                 sp_sys;

  /*
   * d'b: nexe just invoked some syscall. stop cpu time counting
   * increase syscalls counter (correction for setup call will be
   * corrected later). small mallocs and other calls which are
   * not really "system" will be accounted anyway!
   */
  nap = gnap; /* restore NaClApp object */
  nap->user_side_flag = 1; /* set "user side call" mark */
  PauseCpuClock(nap);
  AccountingSyscallsInc(nap);
  user = nacl_user; /* restore from global */
  sp_user = NaClGetThreadCtxSp(user);

  sp_sys = NaClUserToSysStackAddr(nap, sp_user);

  /*
   * sp_sys points to the top of user stack where there is a retaddr to
   * trampoline slot
   */
  tramp_ret = *(uintptr_t *)sp_sys;
  tramp_ret = NaClUserToSysStackAddr(nap, tramp_ret);

  sysnum = (tramp_ret - (nap->mem_start + NACL_SYSCALL_START_ADDR))
      >> NACL_SYSCALL_BLOCK_SHIFT;

  /*
   * getting user return address (the address where we need to return after
   * system call) from the user stack. (see stack layout above)
   */
  user_ret = *(uintptr_t *) (sp_sys + NACL_USERRET_FIX);

  /*
   * Fix the user stack, throw away return addresses from the top of the stack.
   * After this fix, the first argument to a system call must be on the top of
   * the user stack (see user stack layout above)
   */
  sp_sys += NACL_SYSARGS_FIX;
  sp_user += NACL_SYSCALLRET_FIX;
  NaClSetThreadCtxSp(user, sp_user);

  /* debug print to log */
  NaClLog(4, "system call number %"NACL_PRIdS"\n", sysnum);

  if (sysnum >= NACL_MAX_SYSCALLS) {
    NaClLog(2, "INVALID system call %"NACL_PRIdS"\n", sysnum);
    nap->sysret = -NACL_ABI_EINVAL;
  } else {
    /*
     * syscall_args is used by Decoder functions in
     * nacl_syscall_handlers.c which is automatically generated file
     * and placed in the
     * scons-out/.../gen/src/service_runtime/
     * directory.  syscall_args must point to the first argument of a
     * system call. System call arguments are placed on the untrusted
     * user stack.
     */
    nap->syscall_args = (uintptr_t *) sp_sys;
    nap->sysret = (*(nap->syscall_table[sysnum].handler))(nap);
  }

  /*
   * before switching back to user module, we need to make sure that the
   * user_ret is properly sandboxed.
   */
  user_ret = (nacl_reg_t) NaClSandboxCodeAddr(nap, (uintptr_t)user_ret);

  /* d'b: give control to the nexe. start cpu time counting */
  ResumeCpuClock(nap);
  nap->user_side_flag = 0; /* remove "user side call" mark */
  NaClSwitchToApp(nap, user_ret);

  /* NOTREACHED */
  fprintf(stderr, "NORETURN NaClSwitchToApp returned!?!\n");
  NaClAbort();
}