コード例 #1
0
int main(int argc, char **argv) {
  struct NaClApp app;
  struct GioMemoryFileSnapshot gio_file;

  /* Register file-local handler first (so it ends up second in the chain). */
  if (strcmp(argv[1], "unforwarded_trusted") != 0) {
    RegisterExceptionHandler();
  }

  /* Install untrusted exception catcher after local handler. */
  CHECK(NaClInterceptMachExceptions());

  if (argc == 2 && strcmp(argv[1], "early_trusted") == 0) {
    g_expect_crash = 1;
    /* Cause a crash. */
    *(volatile int *) 0 = 0;
  }

  if (argc != 3) {
    NaClLog(LOG_FATAL, "Expected 1 or 2 arguments\n");
  }

  if (strcmp(argv[1], "trusted") == 0) {
    g_expect_crash = 1;
  } else if (strcmp(argv[1], "unforwarded_trusted") == 0) {
    fprintf(stderr, "** intended_exit_status=-10\n");
    g_expect_crash = 0;
  } else if (strcmp(argv[1], "untrusted") == 0) {
    /* Expect the test to crash; untrusted crashes shouldn't be propagated. */
    fprintf(stderr, "** intended_exit_status=-10\n");
    g_expect_crash = 0;
  } else if (strcmp(argv[1], "untrusted_caught") == 0) {
    g_expect_crash = 0;
  } else {
    NaClLog(LOG_FATAL, "1st argument (%s) not recognised\n", argv[1]);
  }

  NaClAllModulesInit();

  NaClFileNameForValgrind(argv[1]);
  CHECK(GioMemoryFileSnapshotCtor(&gio_file, argv[2]));
  CHECK(NaClAppCtor(&app));
  app.enable_exception_handling = 1;
  CHECK(NaClAppLoadFile((struct Gio *) &gio_file, &app) == LOAD_OK);
  CHECK(NaClAppPrepareToLaunch(&app) == LOAD_OK);
  CHECK(NaClCreateMainThread(&app, 0, NULL, NULL));
  CHECK(NaClWaitForMainThreadToExit(&app) == 0);

  if (g_expect_crash) {
    NaClLog(LOG_FATAL, "Did not expect the guest code to exit\n");
    return 1;
  } else {
    fprintf(stderr, "No crashes, as intended.\n");
    fprintf(stderr, "** intended_exit_status=0\n");
    return 0;
  }
}
コード例 #2
0
int main(int argc, char **argv) {
  struct NaClApp app;
  struct NaClApp *nap = &app;
  struct GioMemoryFileSnapshot gio_file;

  NaClDebugExceptionHandlerStandaloneHandleArgs(argc, argv);
  NaClHandleBootstrapArgs(&argc, &argv);

  /* Turn off buffering to aid debugging. */
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);

  NaClAllModulesInit();

  if (argc != 2) {
    NaClLog(LOG_FATAL, "Expected 1 argument: executable filename\n");
  }

  NaClFileNameForValgrind(argv[1]);
  CHECK(GioMemoryFileSnapshotCtor(&gio_file, argv[1]));
  CHECK(NaClAppCtor(nap));
  CHECK(NaClAppLoadFile((struct Gio *) &gio_file, nap) == LOAD_OK);
  NaClAppInitialDescriptorHookup(nap);
  CHECK(NaClAppPrepareToLaunch(nap) == LOAD_OK);

#if NACL_LINUX
  NaClSignalHandlerInit();
#elif NACL_OSX
  CHECK(NaClInterceptMachExceptions());
#elif NACL_WINDOWS
  nap->attach_debug_exception_handler_func =
      NaClDebugExceptionHandlerStandaloneAttach;
#else
# error Unknown host OS
#endif
  CHECK(NaClFaultedThreadQueueEnable(nap));

  printf("Running TestReceivingFault...\n");
  TestReceivingFault(nap);

  printf("Running TestGettingRegistersInMacSwitchRemainingRegs...\n");
  TestGettingRegistersInMacSwitchRemainingRegs(nap);

  /*
   * Avoid calling exit() because it runs process-global destructors
   * which might break code that is running in our unjoined threads.
   */
  NaClExit(0);
}
コード例 #3
0
int NaClSelLdrMain(int argc, char **argv) {
  int                           opt;
  char                          *rest;
  struct redir                  *entry;
  struct redir                  *redir_queue;
  struct redir                  **redir_qend;


  struct NaClApp                state;
  char                          *nacl_file = NULL;
  char                          *blob_library_file = NULL;
  int                           rpc_supplies_nexe = 0;
  int                           export_addr_to = -1;

  struct NaClApp                *nap = &state;

  struct GioFile                gout;
  NaClErrorCode                 errcode = LOAD_INTERNAL;
  struct GioMemoryFileSnapshot  blob_file;

  int                           ret_code;
  struct DynArray               env_vars;

  char                          *log_file = NULL;
  int                           verbosity = 0;
  int                           fuzzing_quit_after_load = 0;
  int                           debug_mode_bypass_acl_checks = 0;
  int                           debug_mode_ignore_validator = 0;
  int                           skip_qualification = 0;
  int                           handle_signals = 0;
  int                           enable_debug_stub = 0;
  struct NaClPerfCounter        time_all_main;
  const char                    **envp;
  struct NaClEnvCleanser        env_cleanser;

#if NACL_OSX
  /* Mac dynamic libraries cannot access the environ variable directly. */
  envp = (const char **) *_NSGetEnviron();
#else
  /* Overzealous code style check is overzealous. */
  /* @IGNORE_LINES_FOR_CODE_HYGIENE[1] */
  extern char **environ;
  envp = (const char **) environ;
#endif

  ret_code = 1;
  redir_queue = NULL;
  redir_qend = &redir_queue;

  memset(&state, 0, sizeof state);
  NaClAllModulesInit();
  NaClBootstrapChannelErrorReporterInit();
  NaClErrorLogHookInit(NaClBootstrapChannelErrorReporter, &state);

  verbosity = NaClLogGetVerbosity();

  NaClPerfCounterCtor(&time_all_main, "SelMain");

  fflush((FILE *) NULL);

  NaClDebugExceptionHandlerStandaloneHandleArgs(argc, argv);

  if (!GioFileRefCtor(&gout, stdout)) {
    fprintf(stderr, "Could not create general standard output channel\n");
    exit(1);
  }
  if (!NaClAppCtor(&state)) {
    NaClLog(LOG_FATAL, "NaClAppCtor() failed\n");
  }
  if (!DynArrayCtor(&env_vars, 0)) {
    NaClLog(LOG_FATAL, "Failed to allocate env var array\n");
  }
  /*
   * On platforms with glibc getopt, require POSIXLY_CORRECT behavior,
   * viz, no reordering of the arglist -- stop argument processing as
   * soon as an unrecognized argument is encountered, so that, for
   * example, in the invocation
   *
   *   sel_ldr foo.nexe -vvv
   *
   * the -vvv flags are made available to the nexe, rather than being
   * consumed by getopt.  This makes the behavior of the Linux build
   * of sel_ldr consistent with the Windows and OSX builds.
   */
  while ((opt = my_getopt(argc, argv,
#if NACL_LINUX
                       "+D:z:"
#endif
                       "aB:ceE:f:Fgh:i:l:Qr:RsSvw:X:Z")) != -1) {
    switch (opt) {
      case 'a':
        fprintf(stderr, "DEBUG MODE ENABLED (bypass acl)\n");
        debug_mode_bypass_acl_checks = 1;
        break;
      case 'B':
        blob_library_file = optarg;
        break;
      case 'c':
        ++debug_mode_ignore_validator;
        break;
#if NACL_LINUX
      case 'D':
        NaClHandleRDebug(optarg, argv[0]);
        break;
#endif
      case 'e':
        nap->enable_exception_handling = 1;
        break;
      case 'E':
        /*
         * For simplicity, we treat the environment variables as a
         * list of strings rather than a key/value mapping.  We do not
         * try to prevent duplicate keys or require the strings to be
         * of the form "KEY=VALUE".  This is in line with how execve()
         * works in Unix.
         *
         * We expect that most callers passing "-E" will either pass
         * in a fixed list or will construct the list using a
         * high-level language, in which case de-duplicating keys
         * outside of sel_ldr is easier.  However, we could do
         * de-duplication here if it proves to be worthwhile.
         */
        if (!DynArraySet(&env_vars, env_vars.num_entries, optarg)) {
          NaClLog(LOG_FATAL, "Adding item to env_vars failed\n");
        }
        break;
      case 'f':
        nacl_file = optarg;
        break;
      case 'F':
        fuzzing_quit_after_load = 1;
        break;

      case 'g':
        enable_debug_stub = 1;
        break;

      case 'h':
      case 'r':
      case 'w':
        /* import host descriptor */
        entry = malloc(sizeof *entry);
        if (NULL == entry) {
          fprintf(stderr, "No memory for redirection queue\n");
          exit(1);
        }
        entry->next = NULL;
        entry->nacl_desc = strtol(optarg, &rest, 0);
        entry->tag = HOST_DESC;
        entry->u.host.d = strtol(rest+1, (char **) 0, 0);
        entry->u.host.mode = ImportModeMap(opt);
        *redir_qend = entry;
        redir_qend = &entry->next;
        break;
      case 'i':
        /* import IMC handle */
        entry = malloc(sizeof *entry);
        if (NULL == entry) {
          fprintf(stderr, "No memory for redirection queue\n");
          exit(1);
        }
        entry->next = NULL;
        entry->nacl_desc = strtol(optarg, &rest, 0);
        entry->tag = IMC_DESC;
        entry->u.handle = (NaClHandle) strtol(rest+1, (char **) 0, 0);
        *redir_qend = entry;
        redir_qend = &entry->next;
        break;
      case 'l':
        log_file = optarg;
        break;
      case 'Q':
        fprintf(stderr, "PLATFORM QUALIFICATION DISABLED BY -Q - "
                "Native Client's sandbox will be unreliable!\n");
        skip_qualification = 1;
        break;
      case 'R':
        rpc_supplies_nexe = 1;
        break;
      /* case 'r':  with 'h' and 'w' above */
      case 's':
        nap->validator_stub_out_mode = 1;
        break;
      case 'S':
        handle_signals = 1;
        break;
      case 'v':
        ++verbosity;
        NaClLogIncrVerbosity();
        break;
      /* case 'w':  with 'h' and 'r' above */
      case 'X':
        export_addr_to = strtol(optarg, (char **) 0, 0);
        break;
#if NACL_LINUX
      case 'z':
        NaClHandleReservedAtZero(optarg);
        break;
#endif
      case 'Z':
        NaClLog(LOG_WARNING, "Enabling Fixed-Feature CPU Mode\n");
        nap->fixed_feature_cpu_mode = 1;
        if (!nap->validator->FixCPUFeatures(nap->cpu_features)) {
          NaClLog(LOG_ERROR,
                  "This CPU lacks features required by "
                  "fixed-function CPU mode.\n");
          exit(1);
        }
        break;
      default:
        fprintf(stderr, "ERROR: unknown option: [%c]\n\n", opt);
        PrintUsage();
        exit(-1);
    }
  }

  if (debug_mode_ignore_validator == 1)
    fprintf(stderr, "DEBUG MODE ENABLED (ignore validator)\n");
  else if (debug_mode_ignore_validator > 1)
    fprintf(stderr, "DEBUG MODE ENABLED (skip validator)\n");

  if (verbosity) {
    int         ix;
    char const  *separator = "";

    fprintf(stderr, "sel_ldr argument list:\n");
    for (ix = 0; ix < argc; ++ix) {
      fprintf(stderr, "%s%s", separator, argv[ix]);
      separator = " ";
    }
    putc('\n', stderr);
  }

  if (debug_mode_bypass_acl_checks) {
    NaClInsecurelyBypassAllAclChecks();
  }

  /*
   * change stdout/stderr to log file now, so that subsequent error
   * messages will go there.  unfortunately, error messages that
   * result from getopt processing -- usually out-of-memory, which
   * shouldn't happen -- won't show up.
   */
  if (NULL != log_file) {
    NaClLogSetFile(log_file);
  }

  if (rpc_supplies_nexe) {
    if (NULL != nacl_file) {
      fprintf(stderr,
              "sel_ldr: mutually exclusive flags -f and -R both used\n");
      exit(1);
    }
    /* post: NULL == nacl_file */
    if (export_addr_to < 0) {
      fprintf(stderr,
              "sel_ldr: -R requires -X to set up secure command channel\n");
      exit(1);
    }
  } else {
    if (NULL == nacl_file && optind < argc) {
      nacl_file = argv[optind];
      ++optind;
    }
    if (NULL == nacl_file) {
      fprintf(stderr, "No nacl file specified\n");
      exit(1);
    }
    /* post: NULL != nacl_file */
  }
  /*
   * post condition established by the above code (in Hoare logic
   * terminology):
   *
   * NULL == nacl_file iff rpc_supplies_nexe
   *
   * so hence forth, testing !rpc_supplies_nexe suffices for
   * establishing NULL != nacl_file.
   */
  CHECK((NULL == nacl_file) == rpc_supplies_nexe);

  /* to be passed to NaClMain, eventually... */
  argv[--optind] = (char *) "NaClMain";

  state.ignore_validator_result = (debug_mode_ignore_validator > 0);
  state.skip_validator = (debug_mode_ignore_validator > 1);

  if (getenv("NACL_UNTRUSTED_EXCEPTION_HANDLING") != NULL) {
    state.enable_exception_handling = 1;
  }
  if (state.enable_exception_handling || enable_debug_stub) {
#if NACL_WINDOWS
    state.attach_debug_exception_handler_func =
        NaClDebugExceptionHandlerStandaloneAttach;
#elif NACL_LINUX
    /* NaCl's signal handler is always enabled on Linux. */
#elif NACL_OSX
    if (!NaClInterceptMachExceptions()) {
      fprintf(stderr, "ERROR setting up Mach exception interception.\n");
      return -1;
    }
#else
# error Unknown host OS
#endif
  }
  if (NACL_LINUX) {
    handle_signals = 1;
  }

  errcode = LOAD_OK;

  /*
   * 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 the platform qualification checks pass.
   *
   * NACL_DANGEROUS_SKIP_QUALIFICATION_TEST is used by tsan / memcheck
   * (see src/third_party/valgrind/).
   */
  if (!skip_qualification &&
      getenv("NACL_DANGEROUS_SKIP_QUALIFICATION_TEST") != NULL) {
    fprintf(stderr, "PLATFORM QUALIFICATION DISABLED BY ENVIRONMENT - "
            "Native Client's sandbox will be unreliable!\n");
    skip_qualification = 1;
  }

  if (!skip_qualification) {
    NaClErrorCode pq_error = NACL_FI_VAL("pq", NaClErrorCode,
                                         NaClRunSelQualificationTests());
    if (LOAD_OK != pq_error) {
      errcode = pq_error;
      nap->module_load_status = pq_error;
      fprintf(stderr, "Error while loading \"%s\": %s\n",
              NULL != nacl_file ? nacl_file
                                : "(no file, to-be-supplied-via-RPC)",
              NaClErrorString(errcode));
    }
  }

  if (handle_signals) {
    NaClSignalHandlerInit();
  } else {
    /*
     * 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();

  /*
   * Open both files first because (on Mac OS X at least)
   * NaClAppLoadFile() enables an outer sandbox.
   */
  if (NULL != blob_library_file) {
    NaClFileNameForValgrind(blob_library_file);
    if (0 == GioMemoryFileSnapshotCtor(&blob_file, blob_library_file)) {
      perror("sel_main");
      fprintf(stderr, "Cannot open \"%s\".\n", blob_library_file);
      exit(1);
    }
    NaClPerfCounterMark(&time_all_main, "SnapshotBlob");
    NaClPerfCounterIntervalLast(&time_all_main);
  }

  NaClAppInitialDescriptorHookup(nap);

  if (!rpc_supplies_nexe) {
    struct GioMemoryFileSnapshot main_file;

    NaClFileNameForValgrind(nacl_file);
    if (0 == GioMemoryFileSnapshotCtor(&main_file, nacl_file)) {
      perror("sel_main");
      fprintf(stderr, "Cannot open \"%s\".\n", nacl_file);
      exit(1);
    }
    NaClPerfCounterMark(&time_all_main, "SnapshotNaclFile");
    NaClPerfCounterIntervalLast(&time_all_main);

    if (LOAD_OK == errcode) {
      NaClLog(2, "Loading nacl file %s (non-RPC)\n", nacl_file);
      errcode = NaClAppLoadFile((struct Gio *) &main_file, nap);
      if (LOAD_OK != errcode) {
        fprintf(stderr, "Error while loading \"%s\": %s\n",
                nacl_file,
                NaClErrorString(errcode));
        fprintf(stderr,
                ("Using the wrong type of nexe (nacl-x86-32"
                 " on an x86-64 or vice versa)\n"
                 "or a corrupt nexe file may be"
                 " responsible for this error.\n"));
      }
      NaClPerfCounterMark(&time_all_main, "AppLoadEnd");
      NaClPerfCounterIntervalLast(&time_all_main);

      NaClXMutexLock(&nap->mu);
      nap->module_load_status = errcode;
      NaClXCondVarBroadcast(&nap->cv);
      NaClXMutexUnlock(&nap->mu);
    }

    if (-1 == (*((struct Gio *) &main_file)->vtbl->Close)((struct Gio *)
                                                          &main_file)) {
      fprintf(stderr, "Error while closing \"%s\".\n", nacl_file);
    }
    (*((struct Gio *) &main_file)->vtbl->Dtor)((struct Gio *) &main_file);

    if (fuzzing_quit_after_load) {
      exit(0);
    }
  }

  /*
   * Execute additional I/O redirections.  NB: since the NaClApp
   * takes ownership of host / IMC socket descriptors, all but
   * the first run will not get access if the NaClApp closes
   * them.  Currently a normal NaClApp process exit does not
   * close descriptors, since the underlying host OS will do so
   * as part of service runtime exit.
   */
  NaClLog(4, "Processing I/O redirection/inheritance from command line\n");
  for (entry = redir_queue; NULL != entry; entry = entry->next) {
    switch (entry->tag) {
      case HOST_DESC:
        NaClAddHostDescriptor(nap, entry->u.host.d,
                              entry->u.host.mode, entry->nacl_desc);
        break;
      case IMC_DESC:
        NaClAddImcHandle(nap, entry->u.handle, entry->nacl_desc);
        break;
    }
  }

  /*
   * If export_addr_to is set to a non-negative integer, we create a
   * bound socket and socket address pair and bind the former to
   * descriptor NACL_SERVICE_PORT_DESCRIPTOR (3 [see sel_ldr.h]) and
   * the latter to descriptor NACL_SERVICE_ADDRESS_DESCRIPTOR (4).
   * The socket address is sent to the export_addr_to descriptor.
   *
   * The service runtime also accepts a connection on the bound socket
   * and spawns a secure command channel thread to service it.
   */
  if (0 <= export_addr_to) {
    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, (NaClHandle) export_addr_to);
    /*
     * NB: spawns a thread that uses the command channel.  we do
     * this after NaClAppLoadFile so that NaClApp object is more
     * fully populated.  Hereafter any changes to nap should be done
     * while holding locks.
     */
    NaClSecureCommandChannel(nap);
  }

  /*
   * May have created a thread, so need to synchronize uses of nap
   * contents henceforth.
   */

  if (rpc_supplies_nexe) {
    errcode = NaClWaitForLoadModuleStatus(nap);
    NaClPerfCounterMark(&time_all_main, "WaitForLoad");
    NaClPerfCounterIntervalLast(&time_all_main);
  } else {
    /**************************************************************************
     * TODO(bsy): This else block should be made unconditional and
     * invoked after the LoadModule RPC completes, eliminating the
     * essentially dulicated code in latter part of NaClLoadModuleRpc.
     * This cannot be done until we have full saucer separation
     * technology, since Chrome currently uses sel_main_chrome.c and
     * relies on the functionality of the duplicated code.
     *************************************************************************/
    if (LOAD_OK == errcode) {
      if (verbosity) {
        gprintf((struct Gio *) &gout, "printing NaClApp details\n");
        NaClAppPrintDetails(nap, (struct Gio *) &gout);
      }

      /*
       * Finish setting up the NaCl App.  On x86-32, this means
       * allocating segment selectors.  On x86-64 and ARM, this is
       * (currently) a no-op.
       */
      errcode = NaClAppPrepareToLaunch(nap);
      if (LOAD_OK != errcode) {
        nap->module_load_status = errcode;
        fprintf(stderr, "NaClAppPrepareToLaunch returned %d", errcode);
      }
      NaClPerfCounterMark(&time_all_main, "AppPrepLaunch");
      NaClPerfCounterIntervalLast(&time_all_main);
    }

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

  /*
   * Tell the debug stub to bind a TCP port before enabling the outer
   * sandbox.  This is only needed on Mac OS X since that is the only
   * platform where we have an outer sandbox in standalone sel_ldr.
   * In principle this call should work on all platforms, but Windows
   * XP seems to have some problems when we do bind()/listen() on a
   * separate thread from accept().
   */
  if (enable_debug_stub && NACL_OSX) {
    if (!NaClDebugBindSocket()) {
      exit(1);
    }
  }

  /*
   * Enable the outer sandbox, if one is defined.  Do this as soon as
   * possible.
   *
   * This must come after NaClWaitForLoadModuleStatus(), which waits
   * for another thread to have called NaClAppLoadFile().
   * NaClAppLoadFile() does not work inside the Mac outer sandbox in
   * standalone sel_ldr when using a dynamic code area because it uses
   * NaClCreateMemoryObject() which opens a file in /tmp.
   *
   * We cannot enable the sandbox if file access is enabled.
   */
  if (!NaClAclBypassChecks && g_enable_outer_sandbox_func != NULL) {
    g_enable_outer_sandbox_func();
  }

  if (NULL != blob_library_file) {
    if (LOAD_OK == errcode) {
      NaClLog(2, "Loading blob file %s\n", blob_library_file);
      errcode = NaClAppLoadFileDynamically(nap, (struct Gio *) &blob_file);
      if (LOAD_OK != errcode) {
        fprintf(stderr, "Error while loading \"%s\": %s\n",
                blob_library_file,
                NaClErrorString(errcode));
      }
      NaClPerfCounterMark(&time_all_main, "BlobLoaded");
      NaClPerfCounterIntervalLast(&time_all_main);
    }

    if (-1 == (*((struct Gio *) &blob_file)->vtbl->Close)((struct Gio *)
                                                          &blob_file)) {
      fprintf(stderr, "Error while closing \"%s\".\n", blob_library_file);
    }
    (*((struct Gio *) &blob_file)->vtbl->Dtor)((struct Gio *) &blob_file);
    if (verbosity) {
      gprintf((struct Gio *) &gout, "printing post-IRT NaClApp details\n");
      NaClAppPrintDetails(nap, (struct Gio *) &gout);
    }
  }

  /*
   * Print out a marker for scripts to use to mark the start of app
   * output.
   */
  NaClLog(1, "NACL: Application output follows\n");

  /*
   * Make sure all the file buffers are flushed before entering
   * the application code.
   */
  fflush((FILE *) NULL);

  if (NULL != nap->secure_service) {
    NaClErrorCode start_result;
    /*
     * wait for start_module RPC call on secure channel thread.
     */
    start_result = NaClWaitForStartModuleCommand(nap);
    NaClPerfCounterMark(&time_all_main, "WaitedForStartModuleCommand");
    NaClPerfCounterIntervalLast(&time_all_main);
    if (LOAD_OK == errcode) {
      errcode = start_result;
    }
  }

  /*
   * error reporting done; can quit now if there was an error earlier.
   */
  if (LOAD_OK != errcode) {
    NaClLog(4,
            "Not running app code since errcode is %s (%d)\n",
            NaClErrorString(errcode),
            errcode);
    goto done;
  }

  if (!DynArraySet(&env_vars, env_vars.num_entries, NULL)) {
    NaClLog(LOG_FATAL, "Adding env_vars NULL terminator failed\n");
  }

  NaClEnvCleanserCtor(&env_cleanser, 0);
  if (!NaClEnvCleanserInit(&env_cleanser, envp,
          (char const *const *)env_vars.ptr_array)) {
    NaClLog(LOG_FATAL, "Failed to initialise env cleanser\n");
  }

  if (!NaClAppLaunchServiceThreads(nap)) {
    fprintf(stderr, "Launch service threads failed\n");
    goto done;
  }
  if (enable_debug_stub) {
    if (!NaClDebugInit(nap)) {
      goto done;
    }
  }
  NACL_TEST_INJECTION(BeforeMainThreadLaunches, ());
  if (!NaClCreateMainThread(nap,
                            argc - optind,
                            argv + optind,
                            NaClEnvCleanserEnvironment(&env_cleanser))) {
    fprintf(stderr, "creating main thread failed\n");
    goto done;
  }

  NaClEnvCleanserDtor(&env_cleanser);

  NaClPerfCounterMark(&time_all_main, "CreateMainThread");
  NaClPerfCounterIntervalLast(&time_all_main);
  DynArrayDtor(&env_vars);

  ret_code = NaClWaitForMainThreadToExit(nap);
  NaClPerfCounterMark(&time_all_main, "WaitForMainThread");
  NaClPerfCounterIntervalLast(&time_all_main);

  NaClPerfCounterMark(&time_all_main, "SelMainEnd");
  NaClPerfCounterIntervalTotal(&time_all_main);

  /*
   * exit_group or equiv kills any still running threads while module
   * addr space is still valid.  otherwise we'd have to kill threads
   * before we clean up the address space.
   */
  NaClExit(ret_code);

 done:
  fflush(stdout);

  if (verbosity) {
    gprintf((struct Gio *) &gout, "exiting -- printing NaClApp details\n");
    NaClAppPrintDetails(nap, (struct Gio *) &gout);

    printf("Dumping vmmap.\n"); fflush(stdout);
    PrintVmmap(nap);
    fflush(stdout);
  }
  /*
   * 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);
  }

  if (verbosity > 0) {
    printf("Done.\n");
  }
  fflush(stdout);

  if (handle_signals) NaClSignalHandlerFini();
  NaClAllModulesFini();

  NaClExit(ret_code);

  /* Unreachable, but having the return prevents a compiler error. */
  return ret_code;
}
コード例 #4
0
ファイル: sel_main.c プロジェクト: alepharchives/zerovm
int main(int argc, char **argv)
{
  struct NaClApp state, *nap = &state;
  struct SystemManifest sys_mft;
  struct GioMemoryFileSnapshot main_file;
  GTimer *timer;

  /* zerovm initialization */
  memset(nap, 0, sizeof *nap);
  nap->system_manifest = &sys_mft;
  memset(nap->system_manifest, 0, sizeof *nap->system_manifest);
  gnap = nap;

  ParseCommandLine(nap, argc, argv);
  NaClSignalHandlerInit();
  NaClSyscallTableInit();

  /* initialize mem_map and set nap fields to default values */
  ZLOGFAIL(NaClAppCtor(nap) == 0, EFAULT, "Error while constructing app state");

  /* We use the signal handler to verify a signal took place. */
  if(nap->skip_qualification == 0) NaClRunSelQualificationTests();

  /* Remove the signal handler if we are not using it. */
  if(nap->handle_signals == 0)
  {
    NaClSignalHandlerFini();
    NaClSignalAssertNoHandlers(); /* Sanity check. */
  }

  /* read nexe into memory */
  timer = g_timer_new();
  ZLOGFAIL(0 == GioMemoryFileSnapshotCtor(&main_file, nap->system_manifest->nexe),
      ENOENT, "Cannot open '%s'. %s", nap->system_manifest->nexe, strerror(errno));

#define TIMER_REPORT(msg) \
  do {\
    ZLOGS(LOG_DEBUG, msg " took %.3f milliseconds",\
        g_timer_elapsed(timer, NULL) * NACL_MICROS_PER_MILLI);\
    g_timer_start(timer);\
  } while(0)

  TIMER_REPORT("GioMemoryFileSnapshotCtor()");

  /* validate given nexe (ensure that text segment is safe) */
  ValidateNexe(nap);
  TIMER_REPORT("ValidateNexe()");

  /* validate nexe structure (check elf header and segments) */
  ZLOGS(LOG_DEBUG, "Loading nacl file %s", nap->system_manifest->nexe);
  NaClAppLoadFile((struct Gio *) &main_file, nap);
  TIMER_REPORT("NaClAppLoadFile()");

  if(-1 == (*((struct Gio *)&main_file)->vtbl->Close)((struct Gio *)&main_file))
    ZLOG(LOG_ERROR, "Error while closing '%s'", nap->system_manifest->nexe);
  (*((struct Gio *) &main_file)->vtbl->Dtor)((struct Gio *) &main_file);

  /* quit if fuzz testing specified */
  if(nap->quit_after_load)
  {
    SetExitState(OK_STATE);
    NaClExit(0);
  }

  /* setup zerovm from manifest */
  SystemManifestCtor(nap);

  /* "defence in depth" call */
  LastDefenseLine(nap);

  /* start accounting */
  AccountingCtor(nap);

  /* Make sure all the file buffers are flushed before entering the nexe */
  fflush((FILE*) NULL);
  TIMER_REPORT("nexe start preparation");

  /* set user code trap() exit location and switch to the user code */
  if(setjmp(user_exit) == 0)
    ZLOGFAIL(!NaClCreateMainThread(nap), EFAULT, "switching to nexe failed");
  SetExitState(OK_STATE);
  TIMER_REPORT("NaClCreateMainThread()");

  /* zerovm exit with finalization, report and stuff */
  NaClExit(0);

  /* Unreachable, but having the return prevents a compiler error. */
  return -1;
}
コード例 #5
0
ファイル: sel_main.c プロジェクト: vi4m/zerovm
int main(int argc, char **argv)
{
  struct NaClApp state, *nap = &state;
  struct SystemManifest sys_mft;
  struct GioMemoryFileSnapshot main_file;
  struct NaClPerfCounter time_all_main;

  /* zerovm initialization */
  memset(nap, 0, sizeof *nap);
  nap->system_manifest = &sys_mft;
  memset(nap->system_manifest, 0, sizeof *nap->system_manifest);
  gnap = nap;

  ParseCommandLine(nap, argc, argv);
  NaClSignalHandlerInit();
  NaClTimeInit();
  NaClSyscallTableInit();
  NaClPerfCounterCtor(&time_all_main, "SelMain");

  /* initialize mem_map and set nap fields to default values */
  ZLOGFAIL(NaClAppCtor(nap) == 0, EFAULT, "Error while constructing app state");

  /* We use the signal handler to verify a signal took place. */
  if(nap->skip_qualification == 0) NaClRunSelQualificationTests();

  /* Remove the signal handler if we are not using it. */
  if(nap->handle_signals == 0)
  {
    NaClSignalHandlerFini();
    NaClSignalAssertNoHandlers(); /* Sanity check. */
  }

#define PERF_CNT(str) \
  NaClPerfCounterMark(&time_all_main, str);\
  NaClPerfCounterIntervalLast(&time_all_main);

  /* read nexe into memory */
  ZLOGFAIL(0 == GioMemoryFileSnapshotCtor(&main_file, nap->system_manifest->nexe),
      ENOENT, "Cannot open '%s'. %s", nap->system_manifest->nexe, strerror(errno));

  PERF_CNT("SnapshotNaclFile");

  /* validate given nexe (ensure that text segment is safe) */
  ValidateNexe(nap);

  /* validate nexe structure (check elf header and segments) */
  ZLOGS(LOG_DEBUG, "Loading nacl file %s", nap->system_manifest->nexe);
  NaClAppLoadFile((struct Gio *) &main_file, nap);
  PERF_CNT("AppLoadEnd");

  if(-1 == (*((struct Gio *)&main_file)->vtbl->Close)((struct Gio *)&main_file))
    ZLOG(LOG_ERROR, "Error while closing '%s'", nap->system_manifest->nexe);

  (*((struct Gio *) &main_file)->vtbl->Dtor)((struct Gio *) &main_file);
  if(nap->quit_after_load) NaClExit(0);

  /* setup zerovm from manifest */
  SystemManifestCtor(nap); /* needs dyn_array initialized */

  /* "defence in depth" call */
  LastDefenseLine(nap);

  /* start accounting */
  AccountingCtor(nap);

  /* Make sure all the file buffers are flushed before entering the nexe */
  fflush((FILE*) NULL);

  /* set user code trap() exit location and switch to the user code */
  PERF_CNT("CreateMainThread");
  if(setjmp(user_exit) == 0)
    ZLOGFAIL(!NaClCreateMainThread(nap), EFAULT, "switching to nexe failed");
  SetExitState(OK_STATE);
  PERF_CNT("WaitForMainThread");
  PERF_CNT("SelMainEnd");

  /* zerovm exit with finalization, report and stuff */
  NaClExit(0);

  /* Unreachable, but having the return prevents a compiler error. */
  return -1;
}
コード例 #6
0
int main(int argc, char **argv) {
  char *nacl_file;
  struct GioMemoryFileSnapshot gf;
  struct NaClApp state;
  struct NaClAppThread nat, *natp = &nat;
  int errcode;
  uint32_t initial_addr;
  uint32_t addr;
  struct NaClVmmap *mem_map;
  char *nacl_verbosity = getenv("NACLVERBOSITY");

  if (argc < 2) {
    printf("No nexe file!\n\nFAIL\n");
  }
  nacl_file = argv[1];

  NaClLogModuleInit();

  NaClLogSetVerbosity((NULL == nacl_verbosity)
                      ? 0
                      : strtol(nacl_verbosity, (char **) 0, 0));

  errcode = GioMemoryFileSnapshotCtor(&gf, nacl_file);
  ASSERT_NE(errcode, 0);
  errcode = NaClAppCtor(&state);
  ASSERT_NE(errcode, 0);
  errcode = NaClAppLoadFile((struct Gio *) &gf,
                            &state,
                            NACL_ABI_CHECK_OPTION_CHECK);
  ASSERT_EQ(errcode, 0);

  InitThread(&state, natp);

  /*
   * Initial mappings:
   * 0. --  Zero page
   * 1. rx  Static code segment
   * 2. r   Read-only data segment
   * 3. rw  Writable data segment
   * 4. rw  Stack
   * There is no dynamic code area in this case.
   */
  /* Check the initial mappings. */
  mem_map = &state.mem_map;
  ASSERT_EQ(mem_map->nvalid, 5);
  CheckLowerMappings(mem_map);

  /* Allocate range */
  addr = NaClSysMmap(natp, 0, 3 * NACL_MAP_PAGESIZE,
                     NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
                     NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE,
                     -1, 0);
  printf("addr=0x%"NACL_PRIx32"\n", addr);
  initial_addr = addr;
  /*
   * The mappings have changed to become:
   * 0. --  Zero page
   * 1. rx  Static code segment
   * 2. r   Read-only data segment
   * 3. rw  Writable data segment
   * 4. rw  mmap()'d anonymous, 3 pages (new)
   * 5. rw  Stack
   */

  /* Map to overwrite the start of the previously allocated range */
  addr = NaClSysMmap(natp, (void *) (uintptr_t) initial_addr,
                     2 * NACL_MAP_PAGESIZE,
                     NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
                     NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE
                     | NACL_ABI_MAP_FIXED,
                     -1, 0);
  printf("addr=0x%"NACL_PRIx32"\n", addr);
  ASSERT_EQ(addr, initial_addr);
  /*
   * The mappings have changed to become:
   * 0. --  Zero page
   * 1. rx  Static code segment
   * 2. r   Read-only data segment
   * 3. rw  Writable data segment
   * 4. rw  mmap()'d anonymous, 2 pages (new)
   * 5. rw  mmap()'d anonymous, 1 pages (previous)
   * 6. rw  Stack
   */

  /* Allocate new page */
  addr = NaClSysMmap(natp, 0, NACL_MAP_PAGESIZE,
                     NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
                     NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE,
                     -1, 0);
  printf("addr=0x%"NACL_PRIx32"\n", addr);
  /*
   * Our allocation strategy is to scan down from stack.  This is an
   * implementation detail and not part of the guaranteed semantics,
   * but it is good to test that what we expect of our implementation
   * didn't change.
   */
  ASSERT_EQ_MSG(addr, initial_addr - NACL_MAP_PAGESIZE,
                "Allocation strategy changed!");
  /*
   * The mappings have changed to become:
   * 0. --  Zero page
   * 1. rx  Static code segment
   * 2. r   Read-only data segment
   * 3. rw  Writable data segment
   * 4. rw  mmap()'d anonymous, 1 pages (new)
   * 5. rw  mmap()'d anonymous, 2 pages
   * 6. rw  mmap()'d anonymous, 1 pages
   * 7. rw  Stack
   */

  NaClVmmapMakeSorted(mem_map);
  ASSERT_EQ(mem_map->nvalid, 8);
  CheckLowerMappings(mem_map);
  NaClVmmapDebug(mem_map, "After allocations");
  /* Skip mappings 0, 1, 2 and 3. */
  ASSERT_EQ(mem_map->vmentry[4]->page_num,
            (initial_addr - NACL_MAP_PAGESIZE) >> NACL_PAGESHIFT);
  ASSERT_EQ(mem_map->vmentry[4]->npages,
            NACL_PAGES_PER_MAP);

  ASSERT_EQ(mem_map->vmentry[5]->page_num,
            initial_addr >> NACL_PAGESHIFT);
  ASSERT_EQ(mem_map->vmentry[5]->npages,
            2 * NACL_PAGES_PER_MAP);

  ASSERT_EQ(mem_map->vmentry[6]->page_num,
            (initial_addr +  2 * NACL_MAP_PAGESIZE) >> NACL_PAGESHIFT);
  ASSERT_EQ(mem_map->vmentry[6]->npages,
            NACL_PAGES_PER_MAP);

  /*
   * Undo effects of previous mmaps
   */
  errcode = NaClSysMunmap(natp,
                          (void *) (uintptr_t) (initial_addr
                                                - NACL_MAP_PAGESIZE),
                          NACL_MAP_PAGESIZE * 4);
  ASSERT_EQ(errcode, 0);
  /*
   * Mappings return to being:
   * 0. --  Zero page
   * 1. rx  Static code segment
   * 2. r   Read-only data segment
   * 3. rw  Writable data segment
   * 4. rw  Stack
   */
  ASSERT_EQ(mem_map->nvalid, 5);
  CheckLowerMappings(mem_map);


  /* Allocate range */
  addr = NaClSysMmap(natp, 0, 9 * NACL_MAP_PAGESIZE,
                     NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
                     NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE, -1, 0);
  printf("addr=0x%"NACL_PRIx32"\n", addr);
  initial_addr = addr;
  /*
   * The mappings have changed to become:
   * 0. --  Zero page
   * 1. rx  Static code segment
   * 2. r   Read-only data segment
   * 3. rw  Writable data segment
   * 4. rw  mmap()'d anonymous, 9 pages (new)
   * 5. rw  Stack
   */

  /* Map into middle of previously allocated range */
  addr = NaClSysMmap(natp,
                     (void *) (uintptr_t) (initial_addr
                                           + 2 * NACL_MAP_PAGESIZE),
                     3 * NACL_MAP_PAGESIZE,
                     NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
                     NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE
                     | NACL_ABI_MAP_FIXED,
                     -1, 0);
  printf("addr=0x%"NACL_PRIx32"\n", addr);
  ASSERT_EQ(addr, initial_addr + NACL_MAP_PAGESIZE * 2);
  /*
   * The mappings have changed to become:
   * 0. --  Zero page
   * 1. rx  Static code segment
   * 2. r   Read-only data segment
   * 3. rw  Writable data segment
   * 4. rw  mmap()'d anonymous, 2 pages (previous)
   * 5. rw  mmap()'d anonymous, 3 pages (new)
   * 6. rw  mmap()'d anonymous, 4 pages (previous)
   * 7. rw  Stack
   */

  NaClVmmapMakeSorted(mem_map);
  ASSERT_EQ(mem_map->nvalid, 8);
  CheckLowerMappings(mem_map);

  ASSERT_EQ(mem_map->vmentry[4]->page_num,
            initial_addr >> NACL_PAGESHIFT);
  ASSERT_EQ(mem_map->vmentry[4]->npages,
            2 * NACL_PAGES_PER_MAP);

  ASSERT_EQ(mem_map->vmentry[5]->page_num,
            (initial_addr + 2 * NACL_MAP_PAGESIZE) >> NACL_PAGESHIFT);
  ASSERT_EQ(mem_map->vmentry[5]->npages,
            3 * NACL_PAGES_PER_MAP);

  ASSERT_EQ(mem_map->vmentry[6]->page_num,
            (initial_addr + 5 * NACL_MAP_PAGESIZE) >> NACL_PAGESHIFT);
  ASSERT_EQ(mem_map->vmentry[6]->npages,
            4 * NACL_PAGES_PER_MAP);

  /*
   * Undo effects of previous mmaps
   */
  errcode = NaClSysMunmap(natp, (void *) (uintptr_t) initial_addr,
                          9 * NACL_MAP_PAGESIZE);
  ASSERT_EQ(errcode, 0);
  ASSERT_EQ(mem_map->nvalid, 5);
  CheckLowerMappings(mem_map);
  /*
   * Mappings return to being:
   * 0. --  Zero page
   * 1. rx  Static code segment
   * 2. r   Read-only data segment
   * 3. rw  Writable data segment
   * 4. rw  Stack
   */

  /*
   * Check use of hint.
   */
  addr = NaClSysMmap(natp, (void *) (uintptr_t) initial_addr,
                     NACL_MAP_PAGESIZE,
                     NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
                     NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE,
                     -1, 0);
  ASSERT_LE(addr, 0xffff0000u);
  printf("addr=0x%"NACL_PRIx32"\n", addr);
  ASSERT_LE_MSG(initial_addr, addr, "returned address not at or above hint");
  errcode = NaClSysMunmap(natp, (void *) (uintptr_t) addr, NACL_MAP_PAGESIZE);
  ASSERT_EQ(errcode, 0);

  /* Check handling of zero-sized mappings. */
  addr = NaClSysMmap(natp, 0, 0,
                     NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
                     NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE, -1, 0);
  ASSERT_EQ((int) addr, -NACL_ABI_EINVAL);

  errcode = NaClSysMunmap(natp, (void *) (uintptr_t) initial_addr, 0);
  ASSERT_EQ(errcode, -NACL_ABI_EINVAL);

  printf("PASS\n");
  return 0;
}
コード例 #7
0
int NaClMainForChromium(int handle_count, const NaClHandle *handles,
                        int debug) {
  char *av[1];
  int ac = 1;
  const char **envp;
  struct NaClApp state;
  int main_thread_only = 1;
  char                          *nacl_file = "test_nexe/hello_ppapi.nexe";
  struct NaClApp *nap;
  enum NaClAbiCheckOption       check_abi = NACL_ABI_CHECK_OPTION_CHECK;
  struct GioFile                gout;
  struct GioMemoryFileSnapshot  gf;
  NaClErrorCode errcode;
  int ret_code = 1;
  struct NaClEnvCleanser env_cleanser;

#if NACL_OSX
  /* Mac dynamic libraries cannot access the environ variable directly. */
  envp = (const char **) *_NSGetEnviron();
#else
  /* Overzealous code style check is overzealous. */
  /* @IGNORE_LINES_FOR_CODE_HYGIENE[1] */
  extern char **environ;
  envp = (const char **) environ;
#endif

  NaClAllModulesInit();

  /* Add a handler to catch untrusted errors only */
  NaClSignalHandlerAdd(NaClSignalHandleUntrusted);

  /* to be passed to NaClMain, eventually... */
  av[0] = "NaClMain";

  if (!NaClAppCtor(&state)) {
    fprintf(stderr, "Error while constructing app state\n");
    goto done;
  }

  state.restrict_to_main_thread = main_thread_only;

  nap = &state;
  errcode = LOAD_OK;
  /*
   * 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.
   */
  errcode = NaClRunSelQualificationTests();
  if (LOAD_OK != errcode) {
    nap->module_load_status = errcode;
    fprintf(stderr, "Error while loading in SelMain: %s\n",
            NaClErrorString(errcode));
  }

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

  if (0 == GioMemoryFileSnapshotCtor(&gf, nacl_file)) {
    perror("sel_main");
    fprintf(stderr, "Cannot open \"%s\".\n", nacl_file);
    errcode = LOAD_OPEN_ERROR;
  }

  if (LOAD_OK == errcode) {
    errcode = NaClAppLoadFile((struct Gio *) &gf, nap, check_abi);
    if (LOAD_OK != errcode) {
      fprintf(stderr, "Error while loading \"%s\": %s\n",
              nacl_file,
              NaClErrorString(errcode));
      fprintf(stderr,
              ("Using the wrong type of nexe (nacl-x86-32"
               " on an x86-64 or vice versa)\n"
               "or a corrupt nexe file may be"
               " responsible for this error.\n"));
    }

    NaClXMutexLock(&nap->mu);
    nap->module_load_status = errcode;
    NaClXCondVarBroadcast(&nap->cv);
    NaClXMutexUnlock(&nap->mu);
  }

   if (LOAD_OK == errcode) {
     if (verbosity) {
       gprintf((struct Gio *) &gout, "printing NaClApp details\n");
       NaClAppPrintDetails(nap, (struct Gio *) &gout);
     }

     /*
      * Finish setting up the NaCl App.  This includes dup'ing
      * descriptors 0-2 and making them available to the NaCl App.
      */
     errcode = NaClAppPrepareToLaunch(nap,
                                      0,
                                      1,
                                      2);
     if (LOAD_OK != errcode) {
       nap->module_load_status = errcode;
       fprintf(stderr, "NaClAppPrepareToLaunch returned %d", errcode);
     }
   }

   if (-1 == (*((struct Gio *) &gf)->vtbl->Close)((struct Gio *) &gf)) {
     fprintf(stderr, "Error while closing \"%s\".\n", nacl_file);
   }
   (*((struct Gio *) &gf)->vtbl->Dtor)((struct Gio *) &gf);

 /*
  * Print out a marker for scripts to use to mark the start of app
  * output.
  */
 NaClLog(1, "NACL: Application output follows\n");

 /*
  * Make sure all the file buffers are flushed before entering
  * the application code.
  */
 fflush((FILE *) NULL);

  /*
   * Enable debugging if requested.
   */
  if (debug) NaClDebugSetAllow(1);

  NaClEnvCleanserCtor(&env_cleanser);
  if (!NaClEnvCleanserInit(&env_cleanser, envp)) {
    NaClLog(LOG_FATAL, "Failed to initialise env cleanser\n");
  }

  /*
   * only nap->ehdrs.e_entry is usable, no symbol table is
   * available.
   */
  if (!NaClCreateMainThread(nap, ac, av,
                            NaClEnvCleanserEnvironment(&env_cleanser))) {
    fprintf(stderr, "creating main thread failed\n");
    goto done;
  }

  NaClEnvCleanserDtor(&env_cleanser);

  ret_code = NaClWaitForMainThreadToExit(nap);

  /*
   * exit_group or equiv kills any still running threads while module
   * addr space is still valid.  otherwise we'd have to kill threads
   * before we clean up the address space.
   */
  return ret_code;

 done:
  fflush(stdout);

  NaClAllModulesFini();

  return ret_code;
}