static int StartApp(struct NaClApp *nap, struct NaClChromeMainArgs *args) { int ret_code; struct NaClEnvCleanser env_cleanser; char const *const *envp; NACL_FI_FATAL("BeforeEnvCleanserCtor"); NaClEnvCleanserCtor(&env_cleanser, 1, 0); if (!NaClEnvCleanserInit(&env_cleanser, NaClGetEnviron(), NULL)) { NaClLog(LOG_FATAL, "Failed to initialise env cleanser\n"); } envp = NaClEnvCleanserEnvironment(&env_cleanser); if (NACL_FI_ERROR_COND( "CreateMainThread", !NaClCreateMainThread(nap, args->argc, args->argv, envp))) { NaClLog(LOG_FATAL, "creating main thread failed\n"); } NACL_FI_FATAL("BeforeEnvCleanserDtor"); NaClEnvCleanserDtor(&env_cleanser); ret_code = NaClWaitForMainThreadToExit(nap); if (NACL_ABI_WIFEXITED(nap->exit_status)) { /* * Under Chrome, a call to _exit() often indicates that something * has gone awry, so we report it here to aid debugging. * * This conditional does not run if the NaCl process was * terminated forcibly, which is the normal case under Chrome. * This forcible exit is triggered by the renderer closing the * trusted SRPC channel, which we record as NACL_ABI_SIGKILL * internally. */ NaClLog(LOG_INFO, "NaCl untrusted code called _exit(0x%x)\n", ret_code); } return ret_code; }
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; int export_addr_to = kSrpcFd; /* Used to be set by -X. */ struct NaClApp *nap; 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; /* import IMC handle - used to be "-i" */ CHECK(handle_count == 3); NaClAddImcHandle(nap, handles[0], export_addr_to); NaClAddImcHandle(nap, handles[1], 6); /* async_receive_desc */ NaClAddImcHandle(nap, handles[2], 7); /* async_send_desc */ /* * 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 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 3 and the latter to descriptor 4. The socket address * is written out 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 export_addr_to is -1, we only create the bound socket and * socket address pair, and we do not export to an IMC socket. This * use case is typically only used in testing, where we only "dump" * the socket address to stdout or similar channel. */ if (-2 < export_addr_to) { NaClCreateServiceSocket(nap); if (0 <= export_addr_to) { NaClSendServiceAddressTo(nap, 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); } } if (NULL != nap->secure_channel && LOAD_OK == errcode) { /* * wait for start_module RPC call on secure channel thread. */ errcode = NaClWaitForStartModuleCommand(nap); } /* * error reporting done; can quit now if there was an error earlier. */ if (LOAD_OK != errcode) { goto done; } /* * 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; }
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; }
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; }