Example #1
0
static int LoadApp(struct NaClApp *nap, struct NaClChromeMainArgs *args) {
  NaClErrorCode errcode = LOAD_OK;

  CHECK(g_initialized);

  /* Allow or disallow dyncode API based on args. */
  nap->enable_dyncode_syscalls = args->enable_dyncode_syscalls;
  nap->initial_nexe_max_code_bytes = args->initial_nexe_max_code_bytes;
  nap->pnacl_mode = args->pnacl_mode;

#if NACL_LINUX
  g_prereserved_sandbox_size = args->prereserved_sandbox_size;
#endif
#if NACL_LINUX || NACL_OSX
  /*
   * Overwrite value of sc_nprocessors_onln set in NaClAppCtor.  In
   * the Chrome embedding, the outer sandbox was already enabled when
   * the NaClApp Ctor was invoked, so a bogus value was written in
   * sc_nprocessors_onln.
   */
  if (-1 != args->number_of_cores) {
    nap->sc_nprocessors_onln = args->number_of_cores;
  }
#endif

  if (args->create_memory_object_func != NULL)
    NaClSetCreateMemoryObjectFunc(args->create_memory_object_func);

  /* Inject the validation caching interface, if it exists. */
  nap->validation_cache = args->validation_cache;

  NaClAppInitialDescriptorHookup(nap);

  /*
   * in order to report load error to the browser plugin through the
   * secure command channel, we do not immediate jump to cleanup code
   * on error.  rather, we continue processing (assuming earlier
   * errors do not make it inappropriate) until the secure command
   * channel is set up, and then bail out.
   */

  /*
   * Ensure this operating system platform is supported.
   */
  if (args->skip_qualification) {
    fprintf(stderr, "PLATFORM QUALIFICATION DISABLED - "
        "Native Client's sandbox will be unreliable!\n");
  } else {
    errcode = NACL_FI_VAL("pq", NaClErrorCode,
                          NaClRunSelQualificationTests());
    if (LOAD_OK != errcode) {
      nap->module_load_status = errcode;
      fprintf(stderr, "Error while loading in SelMain: %s\n",
              NaClErrorString(errcode));
      goto error;
    }
  }

  /*
   * Patch the Windows exception dispatcher to be safe in the case
   * of faults inside x86-64 sandboxed code.  The sandbox is not
   * secure on 64-bit Windows without this.
   */
#if (NACL_WINDOWS && NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && \
     NACL_BUILD_SUBARCH == 64)
  NaClPatchWindowsExceptionDispatcher();
#endif
  NaClSignalTestCrashOnStartup();

  nap->enable_exception_handling = args->enable_exception_handling;

  if (args->enable_exception_handling || args->enable_debug_stub) {
#if NACL_LINUX
    /* NaCl's signal handler is always enabled on Linux. */
#elif NACL_OSX
    if (!NaClInterceptMachExceptions()) {
      NaClLog(LOG_FATAL, "LoadApp: Failed to set up Mach exception handler\n");
    }
#elif NACL_WINDOWS
    nap->attach_debug_exception_handler_func =
        args->attach_debug_exception_handler_func;
#else
# error Unknown host OS
#endif
  }
#if NACL_LINUX
  NaClSignalHandlerInit();
#endif

  /* Give debuggers a well known point at which xlate_base is known.  */
  NaClGdbHook(nap);

  CHECK(args->nexe_desc != NULL);
  NaClAppLoadModule(nap, args->nexe_desc);
  NaClDescUnref(args->nexe_desc);
  args->nexe_desc = NULL;

  NACL_FI_FATAL("BeforeLoadIrt");

  /*
   * error reporting done; can quit now if there was an error earlier.
   */
  errcode = NaClGetLoadStatus(nap);
  if (LOAD_OK != errcode) {
    goto error;
  }

  /*
   * Load the integrated runtime (IRT) library.
   * Skip if irt_load_optional and the nexe doesn't have the usual 256MB
   * segment gap. PNaCl's disabling of the segment gap doesn't actually
   * disable the segment gap. It only only reduces it drastically.
   */
  if (args->irt_load_optional && nap->dynamic_text_end < 0x10000000) {
    NaClLog(1,
            "Skipped NaClLoadIrt, irt_load_optional with dynamic_text_end: %"
            NACL_PRIxPTR"\n", nap->dynamic_text_end);
  } else {
    if (args->irt_fd != -1) {
      CHECK(args->irt_desc == NULL);
      args->irt_desc = IrtDescFromFd(args->irt_fd);
      args->irt_fd = -1;
    }
    if (args->irt_desc != NULL) {
      NaClLoadIrt(nap, args->irt_desc);
      NaClDescUnref(args->irt_desc);
      args->irt_desc = NULL;
    }
  }

  if (args->enable_debug_stub) {
#if NACL_LINUX || NACL_OSX
    if (args->debug_stub_pipe_fd != NACL_INVALID_HANDLE) {
      NaClDebugStubSetPipe(args->debug_stub_pipe_fd);
    } else if (args->debug_stub_server_bound_socket_fd != NACL_INVALID_SOCKET) {
      NaClDebugSetBoundSocket(args->debug_stub_server_bound_socket_fd);
    }
#endif
    if (!NaClDebugInit(nap)) {
      goto error;
    }
#if NACL_WINDOWS
    if (NULL != args->debug_stub_server_port_selected_handler_func) {
      args->debug_stub_server_port_selected_handler_func(
          NaClDebugGetBoundPort());
    }
#endif
  }

  if (args->load_status_handler_func != NULL) {
    args->load_status_handler_func(LOAD_OK);
  }
  return LOAD_OK;

error:
  fflush(stdout);

  /* Don't return LOAD_OK if we had some failure loading. */
  if (LOAD_OK == errcode) {
    errcode = LOAD_INTERNAL;
  }
  /*
   * If there is a load status callback, call that now and transfer logs
   * in preparation for process exit.
   */
  if (args->load_status_handler_func != NULL) {
    args->load_status_handler_func(errcode);
    NaClLog(LOG_ERROR, "NaCl LoadApp failed. Transferring logs before exit.\n");
    NaClLogRunAbortBehavior();
  }
  return errcode;
}
static int LoadApp(struct NaClApp *nap, struct NaClChromeMainArgs *args) {
  NaClErrorCode errcode = LOAD_OK;
  int has_bootstrap_channel = args->imc_bootstrap_handle != NACL_INVALID_HANDLE;

  CHECK(g_initialized);

  /* Allow or disallow dyncode API based on args. */
  nap->enable_dyncode_syscalls = args->enable_dyncode_syscalls;
  nap->initial_nexe_max_code_bytes = args->initial_nexe_max_code_bytes;
  nap->pnacl_mode = args->pnacl_mode;

#if NACL_LINUX
  g_prereserved_sandbox_size = args->prereserved_sandbox_size;
#endif
#if NACL_LINUX || NACL_OSX
  /*
   * Overwrite value of sc_nprocessors_onln set in NaClAppCtor.  In
   * the Chrome embedding, the outer sandbox was already enabled when
   * the NaClApp Ctor was invoked, so a bogus value was written in
   * sc_nprocessors_onln.
   */
  if (-1 != args->number_of_cores) {
    nap->sc_nprocessors_onln = args->number_of_cores;
  }
#endif

  if (args->create_memory_object_func != NULL)
    NaClSetCreateMemoryObjectFunc(args->create_memory_object_func);

  /* Inject the validation caching interface, if it exists. */
  nap->validation_cache = args->validation_cache;

#if NACL_WINDOWS
  if (args->broker_duplicate_handle_func != NULL)
    NaClSetBrokerDuplicateHandleFunc(args->broker_duplicate_handle_func);
#endif

  NaClAppInitialDescriptorHookup(nap);

  /*
   * NACL_SERVICE_PORT_DESCRIPTOR and NACL_SERVICE_ADDRESS_DESCRIPTOR
   * are 3 and 4.
   */

  /*
   * in order to report load error to the browser plugin through the
   * secure command channel, we do not immediate jump to cleanup code
   * on error.  rather, we continue processing (assuming earlier
   * errors do not make it inappropriate) until the secure command
   * channel is set up, and then bail out.
   */

  /*
   * Ensure this operating system platform is supported.
   */
  if (args->skip_qualification) {
    fprintf(stderr, "PLATFORM QUALIFICATION DISABLED - "
        "Native Client's sandbox will be unreliable!\n");
  } else {
    errcode = NACL_FI_VAL("pq", NaClErrorCode,
                          NaClRunSelQualificationTests());
    if (LOAD_OK != errcode) {
      nap->module_load_status = errcode;
      fprintf(stderr, "Error while loading in SelMain: %s\n",
              NaClErrorString(errcode));
    }
  }

  /*
   * Patch the Windows exception dispatcher to be safe in the case
   * of faults inside x86-64 sandboxed code.  The sandbox is not
   * secure on 64-bit Windows without this.
   */
#if (NACL_WINDOWS && NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && \
     NACL_BUILD_SUBARCH == 64)
  NaClPatchWindowsExceptionDispatcher();
#endif
  NaClSignalTestCrashOnStartup();

  nap->enable_exception_handling = args->enable_exception_handling;

  if (args->enable_exception_handling || args->enable_debug_stub) {
#if NACL_LINUX
    /* NaCl's signal handler is always enabled on Linux. */
#elif NACL_OSX
    if (!NaClInterceptMachExceptions()) {
      NaClLog(LOG_FATAL, "LoadApp: Failed to set up Mach exception handler\n");
    }
#elif NACL_WINDOWS
    nap->attach_debug_exception_handler_func =
        args->attach_debug_exception_handler_func;
#else
# error Unknown host OS
#endif
  }
#if NACL_LINUX
  NaClSignalHandlerInit();
#endif

  /* Give debuggers a well known point at which xlate_base is known.  */
  NaClGdbHook(nap);

  if (has_bootstrap_channel) {
    NaClCreateServiceSocket(nap);
    /*
     * LOG_FATAL errors that occur before NaClSetUpBootstrapChannel will
     * not be reported via the crash log mechanism (for Chromium
     * embedding of NaCl, shown in the JavaScript console).
     *
     * Some errors, such as due to NaClRunSelQualificationTests, do not
     * trigger a LOG_FATAL but instead set module_load_status to be sent
     * in the start_module RPC reply.  Log messages associated with such
     * errors would be seen, since NaClSetUpBootstrapChannel will get
     * called.
     */
    NaClSetUpBootstrapChannel(nap, args->imc_bootstrap_handle);
  }

  CHECK(args->nexe_desc != NULL);
  NaClAppLoadModule(nap, args->nexe_desc, NULL, NULL);
  NaClDescUnref(args->nexe_desc);
  args->nexe_desc = NULL;

  if (has_bootstrap_channel) {
    NACL_FI_FATAL("BeforeSecureCommandChannel");
    /*
     * Spawns a thread that uses the command channel.
     * Hereafter any changes to nap should be done while holding locks.
     */
    NaClSecureCommandChannel(nap);

    NaClLog(4, "NaClSecureCommandChannel has spawned channel\n");

    NaClLog(4, "secure service = %"NACL_PRIxPTR"\n",
            (uintptr_t) nap->secure_service);

  }

  NACL_FI_FATAL("BeforeLoadIrt");

  /*
   * error reporting done; can quit now if there was an error earlier.
   */
  if (LOAD_OK == errcode) {
    errcode = NaClGetLoadStatus(nap);
  }
  if (LOAD_OK != errcode) {
    goto done;
  }

  /*
   * Load the integrated runtime (IRT) library.
   * Skip if irt_load_optional and the nexe doesn't have the usual 256MB
   * segment gap. PNaCl's disabling of the segment gap doesn't actually
   * disable the segment gap. It only only reduces it drastically.
   */
  if (args->irt_load_optional && nap->dynamic_text_end < 0x10000000) {
    NaClLog(1,
            "Skipped NaClLoadIrt, irt_load_optional with dynamic_text_end: %"
            NACL_PRIxPTR"\n", nap->dynamic_text_end);
  } else {
    if (args->irt_fd != -1) {
      CHECK(args->irt_desc == NULL);
      args->irt_desc = IrtDescFromFd(args->irt_fd);
      args->irt_fd = -1;
    }
    if (args->irt_desc != NULL) {
      NaClLoadIrt(nap, args->irt_desc);
      NaClDescUnref(args->irt_desc);
      args->irt_desc = NULL;
    }
  }

  if (args->enable_debug_stub) {
#if NACL_LINUX || NACL_OSX
    if (args->debug_stub_pipe_fd != NACL_INVALID_HANDLE) {
      NaClDebugStubSetPipe(args->debug_stub_pipe_fd);
    } else if (args->debug_stub_server_bound_socket_fd != NACL_INVALID_SOCKET) {
      NaClDebugSetBoundSocket(args->debug_stub_server_bound_socket_fd);
    }
#endif
    if (!NaClDebugInit(nap)) {
      goto done;
    }
#if NACL_WINDOWS
    if (NULL != args->debug_stub_server_port_selected_handler_func) {
      args->debug_stub_server_port_selected_handler_func(
          NaClDebugGetBoundPort());
    }
#endif
  }

  if (args->load_status_handler_func != NULL) {
    args->load_status_handler_func(LOAD_OK);
  }
  return LOAD_OK;

done:
  fflush(stdout);

  /*
   * If there is a load status callback, call that now and transfer logs
   * in preparation for process exit.
   */
  if (args->load_status_handler_func != NULL) {
    /* Don't return LOAD_OK if we had some failure loading. */
    if (LOAD_OK == errcode) {
      errcode = LOAD_INTERNAL;
    }
    args->load_status_handler_func(errcode);
    NaClLog(LOG_ERROR, "NaCl LoadApp failed. Transferring logs before exit.\n");
    NaClLogRunAbortBehavior();
    /*
     * Fall through and run NaClBlockIfCommandChannelExists.
     * TODO(jvoung): remove NaClBlockIfCommandChannelExists() and use the
     * callback to indicate the load_status after Chromium no longer calls
     * start_module. We also need to change Chromium so that it does not
     * attempt to set up the command channel if there is a known load error.
     * Otherwise there is a race between this process's exit / load error
     * reporting, and the command channel setup on the Chromium side (plus
     * the associated reporting). Thus this could end up with two different
     * load errors being reported (1) the real load error from here, and
     * (2) the command channel setup failure because the process exited in
     * the middle of setting up the command channel.
     */
  }
  /*
   * If there is a secure command channel, we sent an RPC reply with
   * the reason that the nexe was rejected.  If we exit now, that
   * reply may still be in-flight and the various channel closure (esp
   * reverse channel) may be detected first.  This would result in a
   * crash being reported, rather than the error in the RPC reply.
   * Instead, we wait for the hard-shutdown on the command channel.
   */
  if (LOAD_OK != errcode) {
    NaClBlockIfCommandChannelExists(nap);
  } else {
    /*
     * Don't return LOAD_OK if we had some failure loading.
     */
    errcode = LOAD_INTERNAL;
  }
  return errcode;
}