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