Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / native_client / src / trusted / service_runtime / sel_main.c
1 /*
2  * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
6
7 /*
8  * NaCl Simple/secure ELF loader (NaCl SEL).
9  */
10 #include "native_client/src/include/portability.h"
11 #include "native_client/src/include/portability_io.h"
12
13 #if NACL_LINUX
14 #include <getopt.h>
15 #endif
16
17 #if !NACL_WINDOWS
18 #include <signal.h>
19 #endif
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "native_client/src/shared/gio/gio.h"
26 #include "native_client/src/shared/imc/nacl_imc_c.h"
27 #include "native_client/src/shared/platform/nacl_check.h"
28 #include "native_client/src/shared/platform/nacl_exit.h"
29 #include "native_client/src/shared/platform/nacl_log.h"
30 #include "native_client/src/shared/platform/nacl_sync.h"
31 #include "native_client/src/shared/platform/nacl_sync_checked.h"
32 #include "native_client/src/shared/srpc/nacl_srpc.h"
33
34 #include "native_client/src/trusted/desc/nacl_desc_base.h"
35 #include "native_client/src/trusted/desc/nacl_desc_io.h"
36 #include "native_client/src/trusted/fault_injection/fault_injection.h"
37 #include "native_client/src/trusted/fault_injection/test_injection.h"
38 #include "native_client/src/trusted/perf_counter/nacl_perf_counter.h"
39 #include "native_client/src/trusted/service_runtime/env_cleanser.h"
40 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
41 #include "native_client/src/trusted/service_runtime/load_file.h"
42 #include "native_client/src/trusted/service_runtime/nacl_app.h"
43 #include "native_client/src/trusted/service_runtime/nacl_all_modules.h"
44 #include "native_client/src/trusted/service_runtime/nacl_bootstrap_channel_error_reporter.h"
45 #include "native_client/src/trusted/service_runtime/nacl_debug_init.h"
46 #include "native_client/src/trusted/service_runtime/nacl_error_log_hook.h"
47 #include "native_client/src/trusted/service_runtime/nacl_globals.h"
48 #include "native_client/src/trusted/service_runtime/nacl_runtime_host_interface.h"
49 #include "native_client/src/trusted/service_runtime/nacl_signal.h"
50 #include "native_client/src/trusted/service_runtime/nacl_syscall_common.h"
51 #include "native_client/src/trusted/service_runtime/nacl_valgrind_hooks.h"
52 #include "native_client/src/trusted/service_runtime/osx/mach_exception_handler.h"
53 #include "native_client/src/trusted/service_runtime/outer_sandbox.h"
54 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
55 #include "native_client/src/trusted/service_runtime/sel_main_common.h"
56 #include "native_client/src/trusted/service_runtime/sel_qualify.h"
57 #include "native_client/src/trusted/service_runtime/win/exception_patch/ntdll_patch.h"
58 #include "native_client/src/trusted/service_runtime/win/debug_exception_handler.h"
59
60
61 static void (*g_enable_outer_sandbox_func)(void) =
62 #if NACL_OSX
63     NaClEnableOuterSandbox;
64 #else
65     NULL;
66 #endif
67
68 void NaClSetEnableOuterSandboxFunc(void (*func)(void)) {
69   g_enable_outer_sandbox_func = func;
70 }
71
72 static void VmentryPrinter(void           *state,
73                     struct NaClVmmapEntry *vmep) {
74   UNREFERENCED_PARAMETER(state);
75   printf("page num 0x%06x\n", (uint32_t)vmep->page_num);
76   printf("num pages %d\n", (uint32_t)vmep->npages);
77   printf("prot bits %x\n", vmep->prot);
78   fflush(stdout);
79 }
80
81 static void PrintVmmap(struct NaClApp  *nap) {
82   printf("In PrintVmmap\n");
83   fflush(stdout);
84   NaClXMutexLock(&nap->mu);
85   NaClVmmapVisit(&nap->mem_map, VmentryPrinter, (void *) 0);
86
87   NaClXMutexUnlock(&nap->mu);
88 }
89
90
91 struct redir {
92   struct redir  *next;
93   int           nacl_desc;
94   enum {
95     HOST_DESC,
96     IMC_DESC
97   }             tag;
98   union {
99     struct {
100       int d;
101       int mode;
102     }                         host;
103     NaClHandle                handle;
104     struct NaClSocketAddress  addr;
105   } u;
106 };
107
108 int ImportModeMap(char opt) {
109   switch (opt) {
110     case 'h':
111       return O_RDWR;
112     case 'r':
113       return O_RDONLY;
114     case 'w':
115       return O_WRONLY;
116   }
117   fprintf(stderr, ("option %c not understood as a host descriptor"
118                    " import mode\n"),
119           opt);
120   exit(1);
121   /* NOTREACHED */
122 }
123
124 static void PrintUsage(void) {
125   /* NOTE: this is broken up into multiple statements to work around
126            the constant string size limit */
127   fprintf(stderr,
128           "Usage: sel_ldr [-h d:D] [-r d:D] [-w d:D] [-i d:D]\n"
129           "               [-f nacl_file]\n"
130           "               [-l log_file]\n"
131           "               [-X d] [-acFglQRsSQv]\n"
132           "               -- [nacl_file] [args]\n"
133           "\n");
134   fprintf(stderr,
135           " -h\n"
136           " -r\n"
137           " -w associate a host POSIX descriptor D with app desc d\n"
138           "    that was opened in O_RDWR, O_RDONLY, and O_WRONLY modes\n"
139           "    respectively\n"
140           " -i associates an IMC handle D with app desc d\n"
141           " -f file to load; if omitted, 1st arg after \"--\" is loaded\n"
142           " -B additional ELF file to load as a blob library\n"
143           " -v increases verbosity\n"
144           " -X create a bound socket and export the address via an\n"
145           "    IMC message to a corresponding inherited IMC app descriptor\n"
146           "    (use -1 to create the bound socket / address descriptor\n"
147           "    pair, but that no export via IMC should occur)\n");
148   fprintf(stderr,
149           " -R an RPC supplies the NaCl module.\n"
150           "    No nacl_file argument is expected, and the -f flag cannot be\n"
151           "    used with this flag.\n"
152           "\n"
153           " (testing flags)\n"
154           " -a allow file access plus some other syscalls! dangerous!\n"
155           " -c ignore validator! dangerous! Repeating this option twice skips\n"
156           "    validation completely.\n"
157           " -F fuzz testing; quit after loading NaCl app\n"
158           " -g enable gdb debug stub.  Not secure on x86-64 Windows.\n"
159           " -l <file>  write log output to the given file\n"
160           " -q quiet; suppress diagnostic/warning messages at startup\n"
161           " -Q disable platform qualification (dangerous!)\n"
162           " -s safely stub out non-validating instructions\n"
163           " -S enable signal handling.  Not supported on Windows.\n"
164           " -E <name=value>|<name> set an environment variable\n"
165           " -Z use fixed feature x86 CPU mode\n"
166           "\n"
167           " (For full effect, put -l and -q at the beginning.)\n"
168           );  /* easier to add new flags/lines */
169 }
170
171 #if NACL_LINUX
172 static const struct option longopts[] = {
173   { "r_debug", required_argument, NULL, 'D' },
174   { "reserved_at_zero", required_argument, NULL, 'z' },
175   { NULL, 0, NULL, 0 }
176 };
177
178 static int my_getopt(int argc, char *const *argv, const char *shortopts) {
179   return getopt_long(argc, argv, shortopts, longopts, NULL);
180 }
181 #else
182 #define my_getopt getopt
183 #endif
184
185 struct SelLdrOptions {
186   char *nacl_file;
187   char *blob_library_file;
188   int app_argc;
189   char **app_argv;
190
191   int quiet;
192   int verbosity;
193   int fuzzing_quit_after_load;
194   int skip_qualification;
195   int handle_signals;
196   int enable_exception_handling;
197   int enable_debug_stub;
198   int rpc_supplies_nexe;
199   int export_addr_to;
200   int debug_mode_bypass_acl_checks;
201   int debug_mode_ignore_validator;
202   int debug_mode_startup_signal;
203   struct redir *redir_queue;
204   struct redir **redir_qend;
205 };
206
207 static void SelLdrOptionsCtor(struct SelLdrOptions *options) {
208   /* Just to be safe. */
209   memset(options, 0, sizeof(*options));
210
211   options->nacl_file = NULL;
212   options->blob_library_file = NULL;
213   options->app_argc = 0;
214   options->app_argv = NULL;
215
216   options->quiet = 0;
217   options->verbosity = 0;
218   options->fuzzing_quit_after_load = 0;
219   options->skip_qualification = 0;
220   options->handle_signals = 0;
221   options->enable_exception_handling = 0;
222   options->enable_debug_stub = 0;
223   options->rpc_supplies_nexe = 0;
224   options->export_addr_to = -1;
225   options->debug_mode_bypass_acl_checks = 0;
226   options->debug_mode_ignore_validator = 0;
227   options->debug_mode_startup_signal = 0;
228   options->redir_queue = NULL;
229   options->redir_qend = &(options->redir_queue);
230 }
231
232 /* TODO(ncbray): do not directly set fields on NaClApp. */
233 static void NaClSelLdrParseArgs(int argc, char **argv,
234                                 struct SelLdrOptions *options,
235                                 struct DynArray *env_vars,
236                                 struct NaClApp *nap) {
237   int opt;
238   char *rest;
239   struct redir *entry;
240
241   options->verbosity = NaClLogGetVerbosity();
242
243   /*
244    * On platforms with glibc getopt, require POSIXLY_CORRECT behavior,
245    * viz, no reordering of the arglist -- stop argument processing as
246    * soon as an unrecognized argument is encountered, so that, for
247    * example, in the invocation
248    *
249    *   sel_ldr foo.nexe -vvv
250    *
251    * the -vvv flags are made available to the nexe, rather than being
252    * consumed by getopt.  This makes the behavior of the Linux build
253    * of sel_ldr consistent with the Windows and OSX builds.
254    */
255   while ((opt = my_getopt(argc, argv,
256 #if NACL_LINUX
257                        "+D:z:"
258 #endif
259                        "aB:cdeE:f:Fgh:i:l:qQr:RsSvw:X:Z")) != -1) {
260     switch (opt) {
261       case 'a':
262         if (!options->quiet)
263           fprintf(stderr, "DEBUG MODE ENABLED (bypass acl)\n");
264         options->debug_mode_bypass_acl_checks = 1;
265         break;
266       case 'B':
267         options->blob_library_file = optarg;
268         break;
269       case 'c':
270         ++(options->debug_mode_ignore_validator);
271         break;
272       case 'd':
273         options->debug_mode_startup_signal = 1;
274         break;
275 #if NACL_LINUX
276       case 'D':
277         NaClHandleRDebug(optarg, argv[0]);
278         break;
279 #endif
280       case 'e':
281         options->enable_exception_handling = 1;
282         break;
283       case 'E':
284         /*
285          * For simplicity, we treat the environment variables as a
286          * list of strings rather than a key/value mapping.  We do not
287          * try to prevent duplicate keys or require the strings to be
288          * of the form "KEY=VALUE".  This is in line with how execve()
289          * works in Unix.
290          *
291          * We expect that most callers passing "-E" will either pass
292          * in a fixed list or will construct the list using a
293          * high-level language, in which case de-duplicating keys
294          * outside of sel_ldr is easier.  However, we could do
295          * de-duplication here if it proves to be worthwhile.
296          */
297         if (!DynArraySet(env_vars, env_vars->num_entries, optarg)) {
298           NaClLog(LOG_FATAL, "Adding item to env_vars failed\n");
299         }
300         break;
301       case 'f':
302         options->nacl_file = optarg;
303         break;
304       case 'F':
305         options->fuzzing_quit_after_load = 1;
306         break;
307
308       case 'g':
309         options->enable_debug_stub = 1;
310         break;
311
312       case 'h':
313       case 'r':
314       case 'w':
315         /* import host descriptor */
316         entry = malloc(sizeof *entry);
317         if (NULL == entry) {
318           fprintf(stderr, "No memory for redirection queue\n");
319           exit(1);
320         }
321         entry->next = NULL;
322         entry->nacl_desc = strtol(optarg, &rest, 0);
323         entry->tag = HOST_DESC;
324         entry->u.host.d = strtol(rest+1, (char **) 0, 0);
325         entry->u.host.mode = ImportModeMap(opt);
326         *(options->redir_qend) = entry;
327         options->redir_qend = &entry->next;
328         break;
329       case 'i':
330         /* import IMC handle */
331         entry = malloc(sizeof *entry);
332         if (NULL == entry) {
333           fprintf(stderr, "No memory for redirection queue\n");
334           exit(1);
335         }
336         entry->next = NULL;
337         entry->nacl_desc = strtol(optarg, &rest, 0);
338         entry->tag = IMC_DESC;
339         entry->u.handle = (NaClHandle) strtol(rest+1, (char **) 0, 0);
340         *(options->redir_qend) = entry;
341         options->redir_qend = &entry->next;
342         break;
343       case 'l':
344         if (NULL != optarg) {
345           /*
346            * change stdout/stderr to log file now, so that subsequent error
347            * messages will go there.  unfortunately, error messages that
348            * result from getopt processing -- usually out-of-memory, which
349            * shouldn't happen -- won't show up.
350            */
351           NaClLogSetFile(optarg);
352         }
353         break;
354       case 'q':
355         options->quiet = 1;
356         break;
357       case 'Q':
358         if (!options->quiet)
359           fprintf(stderr, "PLATFORM QUALIFICATION DISABLED BY -Q - "
360                   "Native Client's sandbox will be unreliable!\n");
361         options->skip_qualification = 1;
362         break;
363       case 'R':
364         options->rpc_supplies_nexe = 1;
365         break;
366       /* case 'r':  with 'h' and 'w' above */
367       case 's':
368         if (nap->validator->stubout_mode_implemented) {
369           nap->validator_stub_out_mode = 1;
370         } else {
371            NaClLog(LOG_WARNING, "stub_out_mode is not supported, disabled\n");
372         }
373         break;
374       case 'S':
375         options->handle_signals = 1;
376         break;
377       case 'v':
378         ++(options->verbosity);
379         NaClLogIncrVerbosity();
380         break;
381       /* case 'w':  with 'h' and 'r' above */
382       case 'X':
383         options->export_addr_to = strtol(optarg, (char **) 0, 0);
384         break;
385 #if NACL_LINUX
386       case 'z':
387         NaClHandleReservedAtZero(optarg);
388         break;
389 #endif
390       case 'Z':
391         if (nap->validator->readonly_text_implemented) {
392           NaClLog(LOG_WARNING, "Enabling Fixed-Feature CPU Mode\n");
393           nap->fixed_feature_cpu_mode = 1;
394           if (!nap->validator->FixCPUFeatures(nap->cpu_features)) {
395             NaClLog(LOG_ERROR,
396                     "This CPU lacks features required by "
397                     "fixed-function CPU mode.\n");
398             exit(1);
399           }
400         } else {
401            NaClLog(LOG_ERROR, "fixed_feature_cpu_mode is not supported\n");
402            exit(1);
403         }
404         break;
405       default:
406         fprintf(stderr, "ERROR: unknown option: [%c]\n\n", opt);
407         PrintUsage();
408         exit(-1);
409     }
410   }
411
412   /* Post process the options. */
413
414   if (options->debug_mode_ignore_validator == 1) {
415     if (!options->quiet)
416       fprintf(stderr, "DEBUG MODE ENABLED (ignore validator)\n");
417   } else if (options->debug_mode_ignore_validator > 1) {
418     if (!options->quiet)
419       fprintf(stderr, "DEBUG MODE ENABLED (skip validator)\n");
420   }
421
422   if (options->verbosity) {
423     int         ix;
424     char const  *separator = "";
425
426     fprintf(stderr, "sel_ldr argument list:\n");
427     for (ix = 0; ix < argc; ++ix) {
428       fprintf(stderr, "%s%s", separator, argv[ix]);
429       separator = " ";
430     }
431     putc('\n', stderr);
432   }
433
434   if (options->rpc_supplies_nexe) {
435     if (NULL != options->nacl_file) {
436       fprintf(stderr,
437               "sel_ldr: mutually exclusive flags -f and -R both used\n");
438       exit(1);
439     }
440     /* post: NULL == nacl_file */
441     if (options->export_addr_to < 0) {
442       fprintf(stderr,
443               "sel_ldr: -R requires -X to set up secure command channel\n");
444       exit(1);
445     }
446   } else {
447     if (NULL == options->nacl_file && optind < argc) {
448       options->nacl_file = argv[optind];
449       ++optind;
450     }
451     if (NULL == options->nacl_file) {
452       fprintf(stderr, "No nacl file specified\n");
453       exit(1);
454     }
455     /* post: NULL != nacl_file */
456   }
457   /*
458    * post condition established by the above code (in Hoare logic
459    * terminology):
460    *
461    * NULL == nacl_file iff rpc_supplies_nexe
462    *
463    * so hence forth, testing !rpc_supplies_nexe suffices for
464    * establishing NULL != nacl_file.
465    */
466   CHECK((NULL == options->nacl_file) == options->rpc_supplies_nexe);
467
468   /* to be passed to NaClMain, eventually... */
469   if (NULL != options->nacl_file && options->debug_mode_bypass_acl_checks) {
470     argv[--optind] = options->nacl_file;
471   } else {
472     argv[--optind] = (char *) "NaClMain";
473   }
474
475   options->app_argc = argc - optind;
476   options->app_argv = argv + optind;
477
478   /*
479    * NACL_DANGEROUS_SKIP_QUALIFICATION_TEST is used by tsan / memcheck
480    * (see src/third_party/valgrind/).
481    */
482   if (!options->skip_qualification &&
483       getenv("NACL_DANGEROUS_SKIP_QUALIFICATION_TEST") != NULL) {
484     if (!options->quiet)
485       fprintf(stderr, "PLATFORM QUALIFICATION DISABLED BY ENVIRONMENT - "
486               "Native Client's sandbox will be unreliable!\n");
487     options->skip_qualification = 1;
488   }
489
490   if (getenv("NACL_UNTRUSTED_EXCEPTION_HANDLING") != NULL) {
491     options->enable_exception_handling = 1;
492   }
493 }
494
495 static void RedirectIO(struct NaClApp *nap, struct redir *redir_queue){
496   struct redir *entry;
497   /*
498    * Execute additional I/O redirections.  NB: since the NaClApp
499    * takes ownership of host / IMC socket descriptors, all but
500    * the first run will not get access if the NaClApp closes
501    * them.  Currently a normal NaClApp process exit does not
502    * close descriptors, since the underlying host OS will do so
503    * as part of service runtime exit.
504    */
505   NaClLog(4, "Processing I/O redirection/inheritance from command line\n");
506   for (entry = redir_queue; NULL != entry; entry = entry->next) {
507     switch (entry->tag) {
508       case HOST_DESC:
509         NaClAddHostDescriptor(nap, entry->u.host.d,
510                               entry->u.host.mode, entry->nacl_desc);
511         break;
512       case IMC_DESC:
513         NaClAddImcHandle(nap, entry->u.handle, entry->nacl_desc);
514         break;
515     }
516   }
517 }
518
519 int NaClSelLdrMain(int argc, char **argv) {
520   struct NaClApp                *nap = NULL;
521   struct SelLdrOptions          optionsImpl;
522   struct SelLdrOptions          *options = &optionsImpl;
523
524   NaClErrorCode                 errcode = LOAD_INTERNAL;
525   struct NaClDesc               *blob_file = NULL;
526
527   int                           ret_code;
528
529   struct DynArray               env_vars;
530   struct NaClEnvCleanser        env_cleanser;
531   char const *const             *envp;
532
533   struct NaClPerfCounter        time_all_main;
534
535
536   ret_code = 1;
537
538   NaClAllModulesInit();
539
540   /*
541    * If this is a secondary process spun up to assist windows exception
542    * handling, the following function will not return.  If this is a normal
543    * sel_ldr process, the following function does nothing.
544    */
545   NaClDebugExceptionHandlerStandaloneHandleArgs(argc, argv);
546
547   nap = NaClAppCreate();
548   if (nap == NULL) {
549     NaClLog(LOG_FATAL, "NaClAppCreate() failed\n");
550   }
551
552   NaClBootstrapChannelErrorReporterInit();
553   NaClErrorLogHookInit(NaClBootstrapChannelErrorReporter, nap);
554
555   NaClPerfCounterCtor(&time_all_main, "SelMain");
556
557   fflush((FILE *) NULL);
558
559   SelLdrOptionsCtor(options);
560   if (!DynArrayCtor(&env_vars, 0)) {
561     NaClLog(LOG_FATAL, "Failed to allocate env var array\n");
562   }
563   NaClSelLdrParseArgs(argc, argv, options, &env_vars, nap);
564
565   /*
566    * Define the environment variables for untrusted code.
567    */
568   if (!DynArraySet(&env_vars, env_vars.num_entries, NULL)) {
569     NaClLog(LOG_FATAL, "Adding env_vars NULL terminator failed\n");
570   }
571   NaClEnvCleanserCtor(&env_cleanser, 0);
572   if (!NaClEnvCleanserInit(&env_cleanser, NaClGetEnviron(),
573           (char const *const *)env_vars.ptr_array)) {
574     NaClLog(LOG_FATAL, "Failed to initialise env cleanser\n");
575   }
576   envp = NaClEnvCleanserEnvironment(&env_cleanser);
577
578   if (options->debug_mode_startup_signal) {
579 #if NACL_WINDOWS
580     NaClLog(LOG_FATAL, "DEBUG startup signal not supported on Windows\n");
581 #else
582     /*
583      * SIGCONT is ignored by default, so this doesn't actually do anything
584      * by itself.  The purpose of raising the signal is to get a debugger
585      * to stop and inspect the process before it does anything else.  When
586      * sel_ldr is started via nacl_helper_bootstrap, it needs to run as far
587      * as doing its option processing and calling NaClHandleRDebug before
588      * the debugger will understand the association between the address
589      * space and the sel_ldr binary and its dependent shared libraries.
590      * When the debugger stops for the signal, the hacker can run the
591      * "sharedlibrary" command (if the debugger is GDB) and thereafter
592      * it becomes possible to set symbolic breakpoints and so forth.
593      */
594     NaClLog(LOG_ERROR, "DEBUG taking startup signal (SIGCONT) now\n");
595     raise(SIGCONT);
596 #endif
597   }
598
599   if (options->debug_mode_bypass_acl_checks) {
600     NaClInsecurelyBypassAllAclChecks();
601   }
602
603   nap->ignore_validator_result = (options->debug_mode_ignore_validator > 0);
604   nap->skip_validator = (options->debug_mode_ignore_validator > 1);
605   nap->enable_exception_handling = options->enable_exception_handling;
606
607   /*
608    * TODO(mseaborn): Always enable the Mach exception handler on Mac
609    * OS X, and remove handle_signals and sel_ldr's "-S" option.
610    */
611   if (nap->enable_exception_handling || options->enable_debug_stub ||
612       (options->handle_signals && NACL_OSX)) {
613 #if NACL_WINDOWS
614     nap->attach_debug_exception_handler_func =
615         NaClDebugExceptionHandlerStandaloneAttach;
616 #elif NACL_LINUX
617     /* NaCl's signal handler is always enabled on Linux. */
618 #elif NACL_OSX
619     if (!NaClInterceptMachExceptions()) {
620       NaClLog(LOG_ERROR, "ERROR setting up Mach exception interception.\n");
621       return -1;
622     }
623 #else
624 # error Unknown host OS
625 #endif
626   }
627
628   errcode = LOAD_OK;
629
630   /*
631    * in order to report load error to the browser plugin through the
632    * secure command channel, we do not immediate jump to cleanup code
633    * on error.  rather, we continue processing (assuming earlier
634    * errors do not make it inappropriate) until the secure command
635    * channel is set up, and then bail out.
636    */
637
638   /*
639    * Ensure the platform qualification checks pass.
640    */
641   if (!options->skip_qualification) {
642     NaClErrorCode pq_error = NACL_FI_VAL("pq", NaClErrorCode,
643                                          NaClRunSelQualificationTests());
644     if (LOAD_OK != pq_error) {
645       errcode = pq_error;
646       nap->module_load_status = pq_error;
647       if (!options->quiet)
648         NaClLog(LOG_ERROR, "Error while loading \"%s\": %s\n",
649                 NULL != options->nacl_file ? options->nacl_file
650                                   : "(no file, to-be-supplied-via-RPC)",
651                 NaClErrorString(errcode));
652     }
653   }
654
655 #if NACL_LINUX
656   NaClSignalHandlerInit();
657 #endif
658   /*
659    * Patch the Windows exception dispatcher to be safe in the case of
660    * faults inside x86-64 sandboxed code.  The sandbox is not secure
661    * on 64-bit Windows without this.
662    */
663 #if (NACL_WINDOWS && NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && \
664      NACL_BUILD_SUBARCH == 64)
665   NaClPatchWindowsExceptionDispatcher();
666 #endif
667   NaClSignalTestCrashOnStartup();
668
669   /*
670    * Open both files first because (on Mac OS X at least)
671    * NaClAppLoadFile() enables an outer sandbox.
672    */
673   if (NULL != options->blob_library_file) {
674     NaClFileNameForValgrind(options->blob_library_file);
675     blob_file = (struct NaClDesc *) NaClDescIoDescOpen(
676         options->blob_library_file, NACL_ABI_O_RDONLY, 0);
677     if (NULL == blob_file) {
678       perror("sel_main");
679       NaClLog(LOG_FATAL, "Cannot open \"%s\".\n", options->blob_library_file);
680     }
681     NaClPerfCounterMark(&time_all_main, "SnapshotBlob");
682     NaClPerfCounterIntervalLast(&time_all_main);
683   }
684
685   NaClAppInitialDescriptorHookup(nap);
686
687   if (!options->rpc_supplies_nexe) {
688     if (LOAD_OK == errcode) {
689       NaClLog(2, "Loading nacl file %s (non-RPC)\n", options->nacl_file);
690       errcode = NaClAppLoadFileFromFilename(nap, options->nacl_file);
691       if (LOAD_OK != errcode && !options->quiet) {
692         NaClLog(LOG_ERROR, "Error while loading \"%s\": %s\n"
693                 "Using the wrong type of nexe (nacl-x86-32"
694                 " on an x86-64 or vice versa)\n"
695                 "or a corrupt nexe file may be"
696                 " responsible for this error.\n",
697                 options->nacl_file,
698                 NaClErrorString(errcode));
699       }
700       NaClPerfCounterMark(&time_all_main, "AppLoadEnd");
701       NaClPerfCounterIntervalLast(&time_all_main);
702     }
703
704     if (options->fuzzing_quit_after_load) {
705       exit(0);
706     }
707   }
708
709   RedirectIO(nap, options->redir_queue);
710
711   /*
712    * If export_addr_to is set to a non-negative integer, we create a
713    * bound socket and socket address pair and bind the former to
714    * descriptor NACL_SERVICE_PORT_DESCRIPTOR (3 [see sel_ldr.h]) and
715    * the latter to descriptor NACL_SERVICE_ADDRESS_DESCRIPTOR (4).
716    * The socket address is sent to the export_addr_to descriptor.
717    *
718    * The service runtime also accepts a connection on the bound socket
719    * and spawns a secure command channel thread to service it.
720    */
721   if (0 <= options->export_addr_to) {
722     NaClCreateServiceSocket(nap);
723     /*
724      * LOG_FATAL errors that occur before NaClSetUpBootstrapChannel will
725      * not be reported via the crash log mechanism (for Chromium
726      * embedding of NaCl, shown in the JavaScript console).
727      *
728      * Some errors, such as due to NaClRunSelQualificationTests, do not
729      * trigger a LOG_FATAL but instead set module_load_status to be sent
730      * in the start_module RPC reply.  Log messages associated with such
731      * errors would be seen, since NaClSetUpBootstrapChannel will get
732      * called.
733      */
734     NaClSetUpBootstrapChannel(nap, (NaClHandle) options->export_addr_to);
735     /*
736      * NB: spawns a thread that uses the command channel.  we do
737      * this after NaClAppLoadFile so that NaClApp object is more
738      * fully populated.  Hereafter any changes to nap should be done
739      * while holding locks.
740      */
741     NaClSecureCommandChannel(nap);
742   }
743
744   /*
745    * May have created a thread, so need to synchronize uses of nap
746    * contents henceforth.
747    */
748
749   if (options->rpc_supplies_nexe) {
750     NaClErrorCode load_error = NaClWaitForLoadModuleCommand(nap);
751     if (load_error != LOAD_OK) {
752       errcode = load_error;
753     }
754     NaClPerfCounterMark(&time_all_main, "WaitForLoad");
755     NaClPerfCounterIntervalLast(&time_all_main);
756   }
757
758   /*
759    * Tell the debug stub to bind a TCP port before enabling the outer
760    * sandbox.  This is only needed on Mac OS X since that is the only
761    * platform where we have an outer sandbox in standalone sel_ldr.
762    * In principle this call should work on all platforms, but Windows
763    * XP seems to have some problems when we do bind()/listen() on a
764    * separate thread from accept().
765    */
766   if (options->enable_debug_stub && NACL_OSX) {
767     if (!NaClDebugBindSocket()) {
768       exit(1);
769     }
770   }
771
772   /*
773    * Enable the outer sandbox, if one is defined.  Do this as soon as
774    * possible.
775    *
776    * This must come after NaClWaitForLoadModuleCommand(), which waits
777    * for another thread to have called NaClAppLoadFile().
778    * NaClAppLoadFile() does not work inside the Mac outer sandbox in
779    * standalone sel_ldr when using a dynamic code area because it uses
780    * NaClCreateMemoryObject() which opens a file in /tmp.
781    *
782    * We cannot enable the sandbox if file access is enabled.
783    */
784   if (!NaClAclBypassChecks && g_enable_outer_sandbox_func != NULL) {
785     g_enable_outer_sandbox_func();
786   }
787
788   if (NULL != options->blob_library_file) {
789     if (LOAD_OK == errcode) {
790       errcode = NaClMainLoadIrt(nap, blob_file, NULL);
791       if (LOAD_OK != errcode) {
792         NaClLog(LOG_ERROR, "Error while loading \"%s\": %s\n",
793                 options->blob_library_file,
794                 NaClErrorString(errcode));
795       }
796       NaClPerfCounterMark(&time_all_main, "BlobLoaded");
797       NaClPerfCounterIntervalLast(&time_all_main);
798     }
799
800     NaClDescUnref(blob_file);
801   }
802
803   /*
804    * Print out a marker for scripts to use to mark the start of app
805    * output.
806    */
807   NaClLog(1, "NACL: Application output follows\n");
808
809   /*
810    * Make sure all the file buffers are flushed before entering
811    * the application code.
812    */
813   fflush((FILE *) NULL);
814
815   if (NULL != nap->secure_service) {
816     NaClErrorCode start_result;
817     /*
818      * wait for start_module RPC call on secure channel thread.
819      */
820     start_result = NaClWaitForStartModuleCommand(nap);
821     NaClPerfCounterMark(&time_all_main, "WaitedForStartModuleCommand");
822     NaClPerfCounterIntervalLast(&time_all_main);
823     if (LOAD_OK == errcode) {
824       errcode = start_result;
825     }
826   } else {
827     NaClAppStartModule(nap, NULL, NULL);
828   }
829
830   /*
831    * error reporting done; can quit now if there was an error earlier.
832    */
833   if (LOAD_OK != errcode) {
834     NaClLog(4,
835             "Not running app code since errcode is %s (%d)\n",
836             NaClErrorString(errcode),
837             errcode);
838     goto done;
839   }
840
841   if (!NaClAppLaunchServiceThreads(nap)) {
842     goto done;
843   }
844   if (options->enable_debug_stub) {
845     if (!NaClDebugInit(nap)) {
846       goto done;
847     }
848   }
849   NACL_TEST_INJECTION(BeforeMainThreadLaunches, ());
850   if (!NaClCreateMainThread(nap,
851                             options->app_argc,
852                             options->app_argv,
853                             envp)) {
854     NaClLog(LOG_FATAL, "creating main thread failed\n");
855   }
856
857   /*
858    * Clean up temp storage for env vars.
859    */
860   NaClEnvCleanserDtor(&env_cleanser);
861   DynArrayDtor(&env_vars);
862
863   NaClPerfCounterMark(&time_all_main, "CreateMainThread");
864   NaClPerfCounterIntervalLast(&time_all_main);
865
866   ret_code = NaClWaitForMainThreadToExit(nap);
867   NaClPerfCounterMark(&time_all_main, "WaitForMainThread");
868   NaClPerfCounterIntervalLast(&time_all_main);
869
870   NaClPerfCounterMark(&time_all_main, "SelMainEnd");
871   NaClPerfCounterIntervalTotal(&time_all_main);
872
873   /*
874    * exit_group or equiv kills any still running threads while module
875    * addr space is still valid.  otherwise we'd have to kill threads
876    * before we clean up the address space.
877    */
878   NaClExit(ret_code);
879
880  done:
881   fflush(stdout);
882
883   if (options->verbosity) {
884     printf("Dumping vmmap.\n"); fflush(stdout);
885     PrintVmmap(nap);
886     fflush(stdout);
887   }
888   /*
889    * If there is a secure command channel, we sent an RPC reply with
890    * the reason that the nexe was rejected.  If we exit now, that
891    * reply may still be in-flight and the various channel closure (esp
892    * reverse channel) may be detected first.  This would result in a
893    * crash being reported, rather than the error in the RPC reply.
894    * Instead, we wait for the hard-shutdown on the command channel.
895    */
896   if (LOAD_OK != errcode) {
897     NaClBlockIfCommandChannelExists(nap);
898   }
899
900   if (options->verbosity > 0) {
901     printf("Done.\n");
902   }
903   fflush(stdout);
904
905 #if NACL_LINUX
906   NaClSignalHandlerFini();
907 #endif
908   NaClAllModulesFini();
909
910   NaClExit(ret_code);
911
912   /* Unreachable, but having the return prevents a compiler error. */
913   return ret_code;
914 }