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