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; } }
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); }
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 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; }
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; }
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; }
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; }