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 : #include <errno.h>
22 : #include <limits.h>
23 : #include <stdio.h>
24 : #include <stdlib.h>
25 : #include <string.h>
26 :
27 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm || NACL_SANDBOX_FIXED_AT_ZERO == 1
28 : /* Required for our use of mallopt -- see below. */
29 : #include <malloc.h>
30 : #endif
31 :
32 : #include "native_client/src/shared/gio/gio.h"
33 : #include "native_client/src/shared/imc/nacl_imc_c.h"
34 : #include "native_client/src/shared/platform/nacl_check.h"
35 : #include "native_client/src/shared/platform/nacl_exit.h"
36 : #include "native_client/src/shared/platform/nacl_log.h"
37 : #include "native_client/src/shared/platform/nacl_sync.h"
38 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
39 : #include "native_client/src/shared/srpc/nacl_srpc.h"
40 :
41 : #include "native_client/src/trusted/fault_injection/fault_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/nacl_app.h"
46 : #include "native_client/src/trusted/service_runtime/nacl_all_modules.h"
47 : #include "native_client/src/trusted/service_runtime/nacl_config_dangerous.h"
48 : #include "native_client/src/trusted/service_runtime/nacl_globals.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/outer_sandbox.h"
53 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
54 : #include "native_client/src/trusted/service_runtime/sel_qualify.h"
55 : #include "native_client/src/trusted/service_runtime/win/exception_patch/ntdll_patch.h"
56 : #if NACL_WINDOWS
57 : #include "native_client/src/trusted/service_runtime/win/debug_exception_handler.h"
58 : #elif NACL_OSX
59 : #include "native_client/src/trusted/service_runtime/osx/mach_exception_handler.h"
60 : #endif
61 :
62 : static void VmentryPrinter(void *state,
63 0 : struct NaClVmmapEntry *vmep) {
64 : UNREFERENCED_PARAMETER(state);
65 0 : printf("page num 0x%06x\n", (uint32_t)vmep->page_num);
66 0 : printf("num pages %d\n", (uint32_t)vmep->npages);
67 0 : printf("prot bits %x\n", vmep->prot);
68 0 : fflush(stdout);
69 0 : }
70 :
71 0 : static void PrintVmmap(struct NaClApp *nap) {
72 0 : printf("In PrintVmmap\n");
73 0 : fflush(stdout);
74 0 : NaClXMutexLock(&nap->mu);
75 0 : NaClVmmapVisit(&nap->mem_map, VmentryPrinter, (void *) 0);
76 :
77 0 : NaClXMutexUnlock(&nap->mu);
78 0 : }
79 :
80 :
81 : struct redir {
82 : struct redir *next;
83 : int nacl_desc;
84 : enum {
85 : HOST_DESC,
86 : IMC_DESC
87 : } tag;
88 : union {
89 : struct {
90 : int d;
91 : int mode;
92 : } host;
93 : NaClHandle handle;
94 : struct NaClSocketAddress addr;
95 : } u;
96 : };
97 :
98 0 : int ImportModeMap(char opt) {
99 0 : switch (opt) {
100 : case 'h':
101 0 : return O_RDWR;
102 : case 'r':
103 0 : return O_RDONLY;
104 : case 'w':
105 0 : return O_WRONLY;
106 : }
107 0 : fprintf(stderr, ("option %c not understood as a host descriptor"
108 : " import mode\n"),
109 : opt);
110 0 : exit(1);
111 : /* NOTREACHED */
112 : }
113 :
114 :
115 0 : static void PrintUsage() {
116 : /* NOTE: this is broken up into multiple statements to work around
117 : the constant string size limit */
118 0 : fprintf(stderr,
119 : "Usage: sel_ldr [-h d:D] [-r d:D] [-w d:D] [-i d:D]\n"
120 : " [-f nacl_file]\n"
121 : " [-l log_file]\n"
122 : " [-X d] [-acFgImMRsQv] -- [nacl_file] [args]\n"
123 : "\n");
124 0 : fprintf(stderr,
125 : " -h\n"
126 : " -r\n"
127 : " -w associate a host POSIX descriptor D with app desc d\n"
128 : " that was opened in O_RDWR, O_RDONLY, and O_WRONLY modes\n"
129 : " respectively\n"
130 : " -i associates an IMC handle D with app desc d\n"
131 : " -f file to load; if omitted, 1st arg after \"--\" is loaded\n"
132 : " -B additional ELF file to load as a blob library\n"
133 : " -v increases verbosity\n"
134 : " -X create a bound socket and export the address via an\n"
135 : " IMC message to a corresponding NaCl app descriptor\n"
136 : " (use -1 to create the bound socket / address descriptor\n"
137 : " pair, but that no export via IMC should occur)\n");
138 0 : fprintf(stderr,
139 : " -R an RPC supplies the NaCl module.\n"
140 : " No nacl_file argument is expected, and the -f flag cannot be\n"
141 : " used with this flag.\n"
142 : "\n"
143 : " (testing flags)\n"
144 : " -a allow file access! dangerous!\n"
145 : " -c ignore validator! dangerous! Repeating this option twice skips\n"
146 : " validation completely.\n"
147 : " -F fuzz testing; quit after loading NaCl app\n"
148 : " -S enable signal handling. Not secure on x86-64 Windows.\n"
149 : " -g enable gdb debug stub. Not secure on x86-64 Windows.\n"
150 : " -l <file> write log output to the given file\n"
151 : " -s safely stub out non-validating instructions\n"
152 : " -Q disable platform qualification (dangerous!)\n"
153 : " -E <name=value>|<name> set an environment variable\n"
154 : ); /* easier to add new flags/lines */
155 0 : }
156 :
157 : #if NACL_LINUX
158 : static const struct option longopts[] = {
159 : { "r_debug", required_argument, NULL, 'D' },
160 : { NULL, 0, NULL, 0 }
161 : };
162 :
163 : static int my_getopt(int argc, char *const *argv, const char *shortopts) {
164 : return getopt_long(argc, argv, shortopts, longopts, NULL);
165 : }
166 : #else
167 : #define my_getopt getopt
168 : #endif
169 :
170 : int main(int argc,
171 13 : char **argv) {
172 : int opt;
173 : char *rest;
174 : struct redir *entry;
175 : struct redir *redir_queue;
176 : struct redir **redir_qend;
177 :
178 :
179 : struct NaClApp state;
180 13 : char *nacl_file = NULL;
181 13 : char *blob_library_file = NULL;
182 13 : int rpc_supplies_nexe = 0;
183 13 : int export_addr_to = -2;
184 :
185 : struct NaClApp *nap;
186 :
187 : struct GioFile gout;
188 13 : NaClErrorCode errcode = LOAD_INTERNAL;
189 : struct GioMemoryFileSnapshot blob_file;
190 :
191 : int ret_code;
192 : struct DynArray env_vars;
193 :
194 13 : char *log_file = NULL;
195 13 : int verbosity = 0;
196 13 : int fuzzing_quit_after_load = 0;
197 13 : int debug_mode_bypass_acl_checks = 0;
198 13 : int debug_mode_ignore_validator = 0;
199 13 : int stub_out_mode = 0;
200 13 : int skip_qualification = 0;
201 13 : int enable_debug_stub = 0;
202 13 : int handle_signals = 0;
203 13 : int exception_handling_requested = 0;
204 13 : int enable_exception_handling = 0;
205 : struct NaClPerfCounter time_all_main;
206 : const char **envp;
207 : struct NaClEnvCleanser env_cleanser;
208 :
209 :
210 : const char* sandbox_fd_string;
211 :
212 : #if NACL_OSX
213 : /* Mac dynamic libraries cannot access the environ variable directly. */
214 13 : envp = (const char **) *_NSGetEnviron();
215 : #else
216 : /* Overzealous code style check is overzealous. */
217 : /* @IGNORE_LINES_FOR_CODE_HYGIENE[1] */
218 : extern char **environ;
219 : envp = (const char **) environ;
220 : #endif
221 :
222 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm || NACL_SANDBOX_FIXED_AT_ZERO == 1
223 : /*
224 : * Set malloc not to use mmap even for large allocations. This is currently
225 : * necessary when we must use a specific area of RAM for the sandbox.
226 : *
227 : * During startup, before the sandbox is set up, the sel_ldr allocates a chunk
228 : * of memory to store the untrusted code. Normally such an allocation would
229 : * go into the sel_ldr's heap area, but the allocation is typically large --
230 : * at least hundreds of KiB. The default malloc configuration on Linux (at
231 : * least) switches to mmap for such allocations, and mmap will select
232 : * essentially any unoccupied section of the address space. The result: the
233 : * nexe is allocated in the region we use for the sandbox, we protect the
234 : * address space, and then the memcpy into the sandbox (of course) fails.
235 : *
236 : * This is at best a temporary fix. The proper fix is to reserve the
237 : * sandbox region early enough that this isn't a problem. Possible methods
238 : * are discussed in this bug:
239 : * http://code.google.com/p/nativeclient/issues/detail?id=232
240 : */
241 : mallopt(M_MMAP_MAX, 0);
242 : #endif
243 :
244 13 : ret_code = 1;
245 13 : redir_queue = NULL;
246 13 : redir_qend = &redir_queue;
247 :
248 13 : NaClAllModulesInit();
249 :
250 13 : verbosity = NaClLogGetVerbosity();
251 :
252 13 : NaClPerfCounterCtor(&time_all_main, "SelMain");
253 :
254 13 : fflush((FILE *) NULL);
255 :
256 13 : if (!GioFileRefCtor(&gout, stdout)) {
257 0 : fprintf(stderr, "Could not create general standard output channel\n");
258 0 : exit(1);
259 : }
260 :
261 13 : if (!DynArrayCtor(&env_vars, 0)) {
262 0 : NaClLog(LOG_FATAL, "Failed to allocate env var array\n");
263 : }
264 : /*
265 : * On platforms with glibc getopt, require POSIXLY_CORRECT behavior,
266 : * viz, no reordering of the arglist -- stop argument processing as
267 : * soon as an unrecognized argument is encountered, so that, for
268 : * example, in the invocation
269 : *
270 : * sel_ldr foo.nexe -vvv
271 : *
272 : * the -vvv flags are made available to the nexe, rather than being
273 : * consumed by getopt. This makes the behavior of the Linux build
274 : * of sel_ldr consistent with the Windows and OSX builds.
275 : */
276 35 : while ((opt = my_getopt(argc, argv,
277 : #if NACL_LINUX
278 : "+D:"
279 : #endif
280 : "aB:ceE:f:Fgh:i:Il:Qr:RsSvw:X:")) != -1) {
281 9 : switch (opt) {
282 : case 'e':
283 0 : exception_handling_requested = 1;
284 0 : break;
285 : #if NACL_LINUX
286 : case 'D':
287 : handle_r_debug(optarg, argv[0]);
288 : break;
289 : #endif
290 : case 'c':
291 0 : ++debug_mode_ignore_validator;
292 0 : break;
293 : case 'a':
294 3 : fprintf(stderr, "DEBUG MODE ENABLED (bypass acl)\n");
295 3 : debug_mode_bypass_acl_checks = 1;
296 3 : break;
297 : case 'f':
298 0 : nacl_file = optarg;
299 0 : break;
300 : case 'B':
301 1 : blob_library_file = optarg;
302 1 : break;
303 : case 'F':
304 0 : fuzzing_quit_after_load = 1;
305 0 : break;
306 :
307 : case 'g':
308 0 : handle_signals = 1;
309 0 : enable_debug_stub = 1;
310 0 : break;
311 :
312 : case 'S':
313 0 : handle_signals = 1;
314 0 : break;
315 :
316 : case 'h':
317 : case 'r':
318 : case 'w':
319 : /* import host descriptor */
320 0 : entry = malloc(sizeof *entry);
321 0 : if (NULL == entry) {
322 0 : fprintf(stderr, "No memory for redirection queue\n");
323 0 : exit(1);
324 : }
325 0 : entry->next = NULL;
326 0 : entry->nacl_desc = strtol(optarg, &rest, 0);
327 0 : entry->tag = HOST_DESC;
328 0 : entry->u.host.d = strtol(rest+1, (char **) 0, 0);
329 0 : entry->u.host.mode = ImportModeMap(opt);
330 0 : *redir_qend = entry;
331 0 : redir_qend = &entry->next;
332 0 : break;
333 : case 'i':
334 : /* import IMC handle */
335 3 : entry = malloc(sizeof *entry);
336 3 : if (NULL == entry) {
337 0 : fprintf(stderr, "No memory for redirection queue\n");
338 0 : exit(1);
339 : }
340 3 : entry->next = NULL;
341 3 : entry->nacl_desc = strtol(optarg, &rest, 0);
342 3 : entry->tag = IMC_DESC;
343 3 : entry->u.handle = (NaClHandle) strtol(rest+1, (char **) 0, 0);
344 3 : *redir_qend = entry;
345 3 : redir_qend = &entry->next;
346 3 : break;
347 : case 'l':
348 0 : log_file = optarg;
349 0 : break;
350 : case 'R':
351 1 : rpc_supplies_nexe = 1;
352 1 : break;
353 : /* case 'r': with 'h' and 'w' above */
354 : case 'v':
355 0 : ++verbosity;
356 0 : NaClLogIncrVerbosity();
357 0 : break;
358 : /* case 'w': with 'h' and 'r' above */
359 : case 'X':
360 1 : export_addr_to = strtol(optarg, (char **) 0, 0);
361 1 : break;
362 : case 'Q':
363 0 : fprintf(stderr, "PLATFORM QUALIFICATION DISABLED BY -Q - "
364 : "Native Client's sandbox will be unreliable!\n");
365 0 : skip_qualification = 1;
366 0 : break;
367 : case 's':
368 0 : stub_out_mode = 1;
369 0 : break;
370 : case 'E':
371 : /*
372 : * For simplicity, we treat the environment variables as a
373 : * list of strings rather than a key/value mapping. We do not
374 : * try to prevent duplicate keys or require the strings to be
375 : * of the form "KEY=VALUE". This is in line with how execve()
376 : * works in Unix.
377 : *
378 : * We expect that most callers passing "-E" will either pass
379 : * in a fixed list or will construct the list using a
380 : * high-level language, in which case de-duplicating keys
381 : * outside of sel_ldr is easier. However, we could do
382 : * de-duplication here if it proves to be worthwhile.
383 : */
384 0 : if (!DynArraySet(&env_vars, env_vars.num_entries, optarg)) {
385 0 : NaClLog(LOG_FATAL, "Adding item to env_vars failed\n");
386 : }
387 0 : break;
388 : default:
389 0 : fprintf(stderr, "ERROR: unknown option: [%c]\n\n", opt);
390 0 : PrintUsage();
391 0 : exit(-1);
392 : }
393 : }
394 :
395 13 : if (exception_handling_requested) {
396 : #if NACL_WINDOWS
397 : int status;
398 : DWORD exit_code;
399 : enable_exception_handling = 1;
400 : status = NaClLaunchAndDebugItself(argv[0], &exit_code);
401 : if (status == DEBUG_EXCEPTION_HANDLER_NOT_SUPPORTED) {
402 : enable_exception_handling = 0;
403 : } else if (status == DEBUG_EXCEPTION_HANDLER_SUCCESS) {
404 : return exit_code;
405 : } else if (status == DEBUG_EXCEPTION_HANDLER_ERROR) {
406 : fprintf(stderr, "ERROR in debug exception handling %d\n",
407 : GetLastError());
408 : return -1;
409 : }
410 : #elif NACL_LINUX
411 : handle_signals = 1;
412 : enable_exception_handling = 1;
413 : #elif NACL_OSX
414 : int status;
415 0 : enable_exception_handling = 1;
416 0 : status = NaClInterceptMachExceptions();
417 0 : if (!status) {
418 0 : fprintf(stderr, "ERROR setting up Mach exception interception.\n");
419 0 : return -1;
420 : }
421 : #else
422 : # error Unknown host OS
423 : #endif
424 : }
425 13 : if (exception_handling_requested && !enable_exception_handling) {
426 0 : fprintf(stderr, "WARNING: exception handling is not supported\n");
427 : }
428 :
429 13 : if (debug_mode_ignore_validator == 1)
430 0 : fprintf(stderr, "DEBUG MODE ENABLED (ignore validator)\n");
431 13 : else if (debug_mode_ignore_validator > 1)
432 0 : fprintf(stderr, "DEBUG MODE ENABLED (skip validator)\n");
433 :
434 13 : if (verbosity) {
435 : int ix;
436 0 : char const *separator = "";
437 :
438 0 : fprintf(stderr, "sel_ldr argument list:\n");
439 0 : for (ix = 0; ix < argc; ++ix) {
440 0 : fprintf(stderr, "%s%s", separator, argv[ix]);
441 0 : separator = " ";
442 : }
443 0 : putc('\n', stderr);
444 : }
445 :
446 : if (NACL_DANGEROUS_STUFF_ENABLED) {
447 : fprintf(stderr,
448 : "WARNING WARNING WARNING WARNING"
449 : " WARNING WARNING WARNING WARNING\n");
450 : fprintf(stderr,
451 : "WARNING\n");
452 : fprintf(stderr,
453 : "WARNING Using a dangerous/debug configuration.\n");
454 : fprintf(stderr,
455 : "WARNING\n");
456 : fprintf(stderr,
457 : "WARNING WARNING WARNING WARNING"
458 : " WARNING WARNING WARNING WARNING\n");
459 : }
460 :
461 13 : if (debug_mode_bypass_acl_checks) {
462 3 : NaClInsecurelyBypassAllAclChecks();
463 : }
464 :
465 : #if NACL_DANGEROUS_DEBUG_MODE_DISABLE_INNER_SANDBOX
466 : if (debug_mode_bypass_acl_checks == 0 &&
467 : debug_mode_ignore_validator == 0) {
468 : fprintf(stderr,
469 : "ERROR: dangerous debug version of sel_ldr can only "
470 : "be invoked with -a/-c options");
471 : exit(-1);
472 : }
473 : #endif
474 : /*
475 : * change stdout/stderr to log file now, so that subsequent error
476 : * messages will go there. unfortunately, error messages that
477 : * result from getopt processing -- usually out-of-memory, which
478 : * shouldn't happen -- won't show up.
479 : */
480 13 : if (NULL != log_file) {
481 0 : NaClLogSetFile(log_file);
482 : }
483 :
484 13 : if (rpc_supplies_nexe) {
485 1 : if (NULL != nacl_file) {
486 0 : fprintf(stderr,
487 : "sel_ldr: mutually exclusive flags -f and -R both used\n");
488 0 : exit(1);
489 : }
490 : /* post: NULL == nacl_file */
491 1 : if (export_addr_to < 0) {
492 0 : fprintf(stderr,
493 : "sel_ldr: -R requires -X to set up secure command channel\n");
494 0 : exit(1);
495 : }
496 : } else {
497 12 : if (NULL == nacl_file && optind < argc) {
498 12 : nacl_file = argv[optind];
499 12 : ++optind;
500 : }
501 12 : if (NULL == nacl_file) {
502 0 : fprintf(stderr, "No nacl file specified\n");
503 0 : exit(1);
504 : }
505 : /* post: NULL != nacl_file */
506 : }
507 : /*
508 : * post condition established by the above code (in Hoare logic
509 : * terminology):
510 : *
511 : * NULL == nacl_file iff rpc_supplies_nexe
512 : *
513 : * so hence forth, testing !rpc_supplies_nexe suffices for
514 : * establishing NULL != nacl_file.
515 : */
516 13 : CHECK((NULL == nacl_file) == rpc_supplies_nexe);
517 :
518 : /* to be passed to NaClMain, eventually... */
519 13 : argv[--optind] = (char *) "NaClMain";
520 :
521 13 : if (!NaClAppCtor(&state)) {
522 0 : fprintf(stderr, "Error while constructing app state\n");
523 0 : goto done_file_dtor;
524 : }
525 :
526 13 : state.ignore_validator_result = (debug_mode_ignore_validator > 0);
527 13 : state.skip_validator = (debug_mode_ignore_validator > 1);
528 13 : state.validator_stub_out_mode = stub_out_mode;
529 13 : state.enable_debug_stub = enable_debug_stub;
530 13 : state.enable_exception_handling = enable_exception_handling;
531 :
532 13 : nap = &state;
533 13 : errcode = LOAD_OK;
534 :
535 : /*
536 : * in order to report load error to the browser plugin through the
537 : * secure command channel, we do not immediate jump to cleanup code
538 : * on error. rather, we continue processing (assuming earlier
539 : * errors do not make it inappropriate) until the secure command
540 : * channel is set up, and then bail out.
541 : */
542 :
543 : /*
544 : * Ensure the platform qualification checks pass.
545 : *
546 : * NACL_DANGEROUS_SKIP_QUALIFICATION_TEST is used by tsan / memcheck
547 : * (see src/third_party/valgrind/).
548 : */
549 13 : if (!skip_qualification &&
550 : getenv("NACL_DANGEROUS_SKIP_QUALIFICATION_TEST") != NULL) {
551 0 : fprintf(stderr, "PLATFORM QUALIFICATION DISABLED BY ENVIRONMENT - "
552 : "Native Client's sandbox will be unreliable!\n");
553 0 : skip_qualification = 1;
554 : }
555 :
556 : /* We use the signal handler to verify a signal took place. */
557 13 : NaClSignalHandlerInit();
558 12 : if (!skip_qualification) {
559 12 : NaClErrorCode pq_error = NACL_FI_VAL("pq", NaClErrorCode,
560 : NaClRunSelQualificationTests());
561 12 : if (LOAD_OK != pq_error) {
562 0 : errcode = pq_error;
563 0 : nap->module_load_status = pq_error;
564 0 : fprintf(stderr, "Error while loading \"%s\": %s\n",
565 : NULL != nacl_file ? nacl_file
566 : : "(no file, to-be-supplied-via-RPC)",
567 : NaClErrorString(errcode));
568 : }
569 : }
570 :
571 : /* Remove the signal handler if we are not using it. */
572 12 : if (!handle_signals) {
573 12 : NaClSignalHandlerFini();
574 : /* Sanity check. */
575 12 : NaClSignalAssertNoHandlers();
576 :
577 : /*
578 : * Patch the Windows exception dispatcher to be safe in the case
579 : * of faults inside x86-64 sandboxed code. The sandbox is not
580 : * secure on 64-bit Windows without this.
581 : */
582 : #if (NACL_WINDOWS && NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && \
583 : NACL_BUILD_SUBARCH == 64)
584 : NaClPatchWindowsExceptionDispatcher();
585 : #endif
586 : }
587 :
588 : /*
589 : * Open both files first because (on Mac OS X at least)
590 : * NaClAppLoadFile() enables an outer sandbox.
591 : */
592 12 : if (NULL != blob_library_file) {
593 0 : NaClFileNameForValgrind(blob_library_file);
594 0 : if (0 == GioMemoryFileSnapshotCtor(&blob_file, blob_library_file)) {
595 0 : perror("sel_main");
596 0 : fprintf(stderr, "Cannot open \"%s\".\n", blob_library_file);
597 0 : exit(1);
598 : }
599 0 : NaClPerfCounterMark(&time_all_main, "SnapshotBlob");
600 0 : NaClPerfCounterIntervalLast(&time_all_main);
601 : }
602 :
603 12 : NaClAppInitialDescriptorHookup(nap);
604 :
605 12 : if (!rpc_supplies_nexe) {
606 : struct GioMemoryFileSnapshot main_file;
607 :
608 12 : NaClFileNameForValgrind(nacl_file);
609 12 : if (0 == GioMemoryFileSnapshotCtor(&main_file, nacl_file)) {
610 1 : perror("sel_main");
611 1 : fprintf(stderr, "Cannot open \"%s\".\n", nacl_file);
612 1 : exit(1);
613 : }
614 11 : NaClPerfCounterMark(&time_all_main, "SnapshotNaclFile");
615 11 : NaClPerfCounterIntervalLast(&time_all_main);
616 :
617 11 : if (LOAD_OK == errcode) {
618 11 : NaClLog(2, "Loading nacl file %s (non-RPC)\n", nacl_file);
619 11 : errcode = NaClAppLoadFile((struct Gio *) &main_file, nap);
620 11 : if (LOAD_OK != errcode) {
621 8 : fprintf(stderr, "Error while loading \"%s\": %s\n",
622 : nacl_file,
623 : NaClErrorString(errcode));
624 8 : fprintf(stderr,
625 : ("Using the wrong type of nexe (nacl-x86-32"
626 : " on an x86-64 or vice versa)\n"
627 : "or a corrupt nexe file may be"
628 : " responsible for this error.\n"));
629 : }
630 11 : NaClPerfCounterMark(&time_all_main, "AppLoadEnd");
631 11 : NaClPerfCounterIntervalLast(&time_all_main);
632 :
633 11 : NaClXMutexLock(&nap->mu);
634 11 : nap->module_load_status = errcode;
635 11 : NaClXCondVarBroadcast(&nap->cv);
636 11 : NaClXMutexUnlock(&nap->mu);
637 : }
638 :
639 11 : if (-1 == (*((struct Gio *) &main_file)->vtbl->Close)((struct Gio *)
640 : &main_file)) {
641 0 : fprintf(stderr, "Error while closing \"%s\".\n", nacl_file);
642 : }
643 11 : (*((struct Gio *) &main_file)->vtbl->Dtor)((struct Gio *) &main_file);
644 :
645 11 : if (fuzzing_quit_after_load) {
646 0 : exit(0);
647 : }
648 : }
649 :
650 : /*
651 : * Execute additional I/O redirections. NB: since the NaClApp
652 : * takes ownership of host / IMC socket descriptors, all but
653 : * the first run will not get access if the NaClApp closes
654 : * them. Currently a normal NaClApp process exit does not
655 : * close descriptors, since the underlying host OS will do so
656 : * as part of service runtime exit.
657 : */
658 11 : NaClLog(4, "Processing I/O redirection/inheritance from command line\n");
659 11 : for (entry = redir_queue; NULL != entry; entry = entry->next) {
660 0 : switch (entry->tag) {
661 : case HOST_DESC:
662 0 : NaClAddHostDescriptor(nap, entry->u.host.d,
663 : entry->u.host.mode, entry->nacl_desc);
664 0 : break;
665 : case IMC_DESC:
666 0 : NaClAddImcHandle(nap, entry->u.handle, entry->nacl_desc);
667 : break;
668 : }
669 : }
670 :
671 : /*
672 : * If export_addr_to is set to a non-negative integer, we create a
673 : * bound socket and socket address pair and bind the former to
674 : * descriptor 3 and the latter to descriptor 4. The socket address
675 : * is written out to the export_addr_to descriptor.
676 : *
677 : * The service runtime also accepts a connection on the bound socket
678 : * and spawns a secure command channel thread to service it.
679 : *
680 : * If export_addr_to is -1, we only create the bound socket and
681 : * socket address pair, and we do not export to an IMC socket. This
682 : * use case is typically only used in testing, where we only "dump"
683 : * the socket address to stdout or similar channel.
684 : */
685 11 : if (-2 < export_addr_to) {
686 0 : NaClCreateServiceSocket(nap);
687 0 : if (0 <= export_addr_to) {
688 0 : NaClSendServiceAddressTo(nap, 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 0 : NaClSecureCommandChannel(nap);
696 : }
697 : }
698 :
699 : /*
700 : * May have created a thread, so need to synchronize uses of nap
701 : * contents henceforth.
702 : */
703 :
704 11 : if (rpc_supplies_nexe) {
705 0 : errcode = NaClWaitForLoadModuleStatus(nap);
706 0 : NaClPerfCounterMark(&time_all_main, "WaitForLoad");
707 0 : NaClPerfCounterIntervalLast(&time_all_main);
708 : } else {
709 : /**************************************************************************
710 : * TODO(bsy): This else block should be made unconditional and
711 : * invoked after the LoadModule RPC completes, eliminating the
712 : * essentially dulicated code in latter part of NaClLoadModuleRpc.
713 : * This cannot be done until we have full saucer separation
714 : * technology, since Chrome currently uses sel_main_chrome.c and
715 : * relies on the functionality of the duplicated code.
716 : *************************************************************************/
717 11 : if (LOAD_OK == errcode) {
718 3 : if (verbosity) {
719 0 : gprintf((struct Gio *) &gout, "printing NaClApp details\n");
720 0 : NaClAppPrintDetails(nap, (struct Gio *) &gout);
721 : }
722 :
723 : /*
724 : * Finish setting up the NaCl App. On x86-32, this means
725 : * allocating segment selectors. On x86-64 and ARM, this is
726 : * (currently) a no-op.
727 : */
728 3 : errcode = NaClAppPrepareToLaunch(nap);
729 3 : if (LOAD_OK != errcode) {
730 0 : nap->module_load_status = errcode;
731 0 : fprintf(stderr, "NaClAppPrepareToLaunch returned %d", errcode);
732 : }
733 3 : NaClPerfCounterMark(&time_all_main, "AppPrepLaunch");
734 3 : NaClPerfCounterIntervalLast(&time_all_main);
735 : }
736 :
737 : /* Give debuggers a well known point at which xlate_base is known. */
738 11 : NaClGdbHook(&state);
739 : }
740 :
741 :
742 11 : if (NULL != blob_library_file) {
743 0 : if (LOAD_OK == errcode) {
744 0 : NaClLog(2, "Loading blob file %s\n", blob_library_file);
745 0 : errcode = NaClAppLoadFileDynamically(nap, (struct Gio *) &blob_file);
746 0 : if (LOAD_OK != errcode) {
747 0 : fprintf(stderr, "Error while loading \"%s\": %s\n",
748 : blob_library_file,
749 : NaClErrorString(errcode));
750 : }
751 0 : NaClPerfCounterMark(&time_all_main, "BlobLoaded");
752 0 : NaClPerfCounterIntervalLast(&time_all_main);
753 : }
754 :
755 0 : if (-1 == (*((struct Gio *) &blob_file)->vtbl->Close)((struct Gio *)
756 : &blob_file)) {
757 0 : fprintf(stderr, "Error while closing \"%s\".\n", blob_library_file);
758 : }
759 0 : (*((struct Gio *) &blob_file)->vtbl->Dtor)((struct Gio *) &blob_file);
760 0 : 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 11 : NaClLog(1, "NACL: Application output follows\n");
771 :
772 : /*
773 : * Chroot() ourselves. Based on agl's chrome implementation.
774 : *
775 : * TODO(mseaborn): This enables a SUID-based Linux outer sandbox,
776 : * but it is not used now. When we do have an outer sandbox for
777 : * standalone sel_ldr, we should enable it earlier on, and merge it
778 : * with NaClEnableOuterSandbox().
779 : */
780 11 : sandbox_fd_string = getenv(NACL_SANDBOX_CHROOT_FD);
781 11 : if (NULL != sandbox_fd_string) {
782 : static const char kChrootMe = 'C';
783 : static const char kChrootSuccess = 'O';
784 :
785 : char* endptr;
786 : char reply;
787 : int fd;
788 : long fd_long;
789 0 : errno = 0; /* To distinguish between success/failure after call */
790 0 : fd_long = strtol(sandbox_fd_string, &endptr, 10);
791 :
792 0 : NaClLog(1, "Chrooting the NaCl module\n");
793 0 : if ((ERANGE == errno && (LONG_MAX == fd_long || LONG_MIN == fd_long)) ||
794 : (0 != errno && 0 == fd_long)) {
795 0 : perror("strtol");
796 0 : exit(1);
797 : }
798 0 : if (endptr == sandbox_fd_string) {
799 0 : fprintf(stderr, "Could not initialize sandbox fd: No digits found\n");
800 0 : exit(1);
801 : }
802 0 : if (*endptr) {
803 0 : fprintf(stderr, "Could not initialize sandbox fd: Extra digits\n");
804 0 : exit(1);
805 : }
806 0 : fd = fd_long;
807 :
808 : /*
809 : * TODO(neha): When we're more merged with chrome, use HANDLE_EINTR()
810 : */
811 0 : if (write(fd, &kChrootMe, 1) != 1) {
812 0 : fprintf(stderr, "Cound not signal sandbox to chroot()\n");
813 0 : exit(1);
814 : }
815 :
816 : /*
817 : * TODO(neha): When we're more merged with chrome, use HANDLE_EINTR()
818 : */
819 0 : if (read(fd, &reply, 1) != 1) {
820 0 : fprintf(stderr, "Could not get response to chroot() from sandbox\n");
821 0 : exit(1);
822 : }
823 0 : if (kChrootSuccess != reply) {
824 0 : fprintf(stderr, "%s\n", &reply);
825 0 : fprintf(stderr, "Reply not correct\n");
826 0 : exit(1);
827 : }
828 : }
829 :
830 : /*
831 : * Make sure all the file buffers are flushed before entering
832 : * the application code.
833 : */
834 11 : fflush((FILE *) NULL);
835 :
836 11 : if (NULL != nap->secure_service) {
837 : NaClErrorCode start_result;
838 : /*
839 : * wait for start_module RPC call on secure channel thread.
840 : */
841 0 : start_result = NaClWaitForStartModuleCommand(nap);
842 0 : NaClPerfCounterMark(&time_all_main, "WaitedForStartModuleCommand");
843 0 : NaClPerfCounterIntervalLast(&time_all_main);
844 0 : if (LOAD_OK == errcode) {
845 0 : errcode = start_result;
846 : }
847 : }
848 :
849 : /*
850 : * error reporting done; can quit now if there was an error earlier.
851 : */
852 11 : if (LOAD_OK != errcode) {
853 8 : NaClLog(4,
854 : "Not running app code since errcode is %s (%d)\n",
855 : NaClErrorString(errcode),
856 : errcode);
857 8 : goto done;
858 : }
859 :
860 3 : if (!DynArraySet(&env_vars, env_vars.num_entries, NULL)) {
861 0 : NaClLog(LOG_FATAL, "Adding env_vars NULL terminator failed\n");
862 : }
863 :
864 3 : NaClEnvCleanserCtor(&env_cleanser, 0);
865 3 : if (!NaClEnvCleanserInit(&env_cleanser, envp,
866 : (char const *const *)env_vars.ptr_array)) {
867 0 : NaClLog(LOG_FATAL, "Failed to initialise env cleanser\n");
868 : }
869 :
870 3 : if (!NaClAppLaunchServiceThreads(nap)) {
871 0 : fprintf(stderr, "Launch service threads failed\n");
872 0 : goto done;
873 : }
874 3 : if (!NaClCreateMainThread(nap,
875 : argc - optind,
876 : argv + optind,
877 : NaClEnvCleanserEnvironment(&env_cleanser))) {
878 0 : fprintf(stderr, "creating main thread failed\n");
879 0 : goto done;
880 : }
881 :
882 3 : NaClEnvCleanserDtor(&env_cleanser);
883 :
884 3 : NaClPerfCounterMark(&time_all_main, "CreateMainThread");
885 3 : NaClPerfCounterIntervalLast(&time_all_main);
886 3 : DynArrayDtor(&env_vars);
887 :
888 3 : ret_code = NaClWaitForMainThreadToExit(nap);
889 3 : NaClPerfCounterMark(&time_all_main, "WaitForMainThread");
890 3 : NaClPerfCounterIntervalLast(&time_all_main);
891 :
892 3 : NaClPerfCounterMark(&time_all_main, "SelMainEnd");
893 3 : NaClPerfCounterIntervalTotal(&time_all_main);
894 :
895 : /*
896 : * exit_group or equiv kills any still running threads while module
897 : * addr space is still valid. otherwise we'd have to kill threads
898 : * before we clean up the address space.
899 : */
900 3 : NaClExit(ret_code);
901 :
902 8 : done:
903 8 : fflush(stdout);
904 :
905 8 : if (verbosity) {
906 0 : gprintf((struct Gio *) &gout, "exiting -- printing NaClApp details\n");
907 0 : NaClAppPrintDetails(nap, (struct Gio *) &gout);
908 :
909 0 : printf("Dumping vmmap.\n"); fflush(stdout);
910 0 : PrintVmmap(nap);
911 0 : fflush(stdout);
912 : }
913 : /*
914 : * If there is a secure command channel, we sent an RPC reply with
915 : * the reason that the nexe was rejected. If we exit now, that
916 : * reply may still be in-flight and the various channel closure (esp
917 : * reverse channel) may be detected first. This would result in a
918 : * crash being reported, rather than the error in the RPC reply.
919 : * Instead, we wait for the hard-shutdown on the command channel.
920 : */
921 8 : if (LOAD_OK != errcode) {
922 8 : NaClBlockIfCommandChannelExists(nap);
923 : }
924 :
925 8 : done_file_dtor:
926 8 : if (verbosity > 0) {
927 0 : printf("Done.\n");
928 : }
929 8 : fflush(stdout);
930 :
931 8 : if (handle_signals) NaClSignalHandlerFini();
932 8 : NaClAllModulesFini();
933 :
934 8 : NaClExit(ret_code);
935 :
936 : /* Unreachable, but having the return prevents a compiler error. */
937 0 : return ret_code;
938 : }
|