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.
7 #include "native_client/src/public/chrome_main.h"
9 #include "native_client/src/include/portability.h"
10 #include "native_client/src/include/portability_io.h"
11 #include "native_client/src/include/portability_sockets.h"
16 #include "native_client/src/include/nacl_macros.h"
17 #include "native_client/src/public/nacl_app.h"
18 #include "native_client/src/shared/platform/nacl_check.h"
19 #include "native_client/src/shared/platform/nacl_exit.h"
20 #include "native_client/src/shared/platform/nacl_log.h"
21 #include "native_client/src/shared/platform/nacl_secure_random.h"
22 #include "native_client/src/shared/platform/nacl_sync.h"
23 #include "native_client/src/shared/platform/nacl_sync_checked.h"
24 #include "native_client/src/trusted/desc/nacl_desc_io.h"
25 #include "native_client/src/trusted/fault_injection/fault_injection.h"
26 #include "native_client/src/trusted/service_runtime/env_cleanser.h"
27 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
28 #include "native_client/src/trusted/service_runtime/nacl_all_modules.h"
29 #include "native_client/src/trusted/service_runtime/nacl_app.h"
30 #include "native_client/src/trusted/service_runtime/nacl_bootstrap_channel_error_reporter.h"
31 #include "native_client/src/trusted/service_runtime/nacl_error_log_hook.h"
32 #include "native_client/src/trusted/service_runtime/nacl_globals.h"
33 #include "native_client/src/trusted/service_runtime/nacl_debug_init.h"
34 #include "native_client/src/trusted/service_runtime/nacl_signal.h"
35 #include "native_client/src/trusted/service_runtime/osx/mach_exception_handler.h"
36 #include "native_client/src/trusted/service_runtime/sel_addrspace.h"
37 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
38 #include "native_client/src/trusted/service_runtime/sel_main_common.h"
39 #include "native_client/src/trusted/service_runtime/sel_qualify.h"
40 #include "native_client/src/trusted/service_runtime/win/exception_patch/ntdll_patch.h"
41 #include "native_client/src/trusted/validator/rich_file_info.h"
42 #include "native_client/src/trusted/validator/validation_metadata.h"
44 static int g_initialized = 0;
46 static void (*g_fatal_error_handler)(const char *data, size_t bytes) = NULL;
48 static const int default_argc = 1;
49 static const char *const default_argv[1] = {"NaClMain"};
51 #if NACL_LINUX || NACL_OSX
52 void NaClChromeMainSetUrandomFd(int urandom_fd) {
53 CHECK(!g_initialized);
54 NaClSecureRngModuleSetUrandomFd(urandom_fd);
58 void NaClChromeMainInit(void) {
59 CHECK(!g_initialized);
64 static void NaClFatalErrorHandlerCallback(void *state,
68 g_fatal_error_handler(buf, buf_bytes);
71 void NaClSetFatalErrorCallback(void (*func)(const char *data, size_t bytes)) {
73 if (g_fatal_error_handler != NULL)
74 NaClLog(LOG_FATAL, "NaClSetFatalErrorCallback called twice.\n");
75 g_fatal_error_handler = func;
76 NaClErrorLogHookInit(NaClFatalErrorHandlerCallback, NULL);
79 struct NaClChromeMainArgs *NaClChromeMainArgsCreate(void) {
80 struct NaClChromeMainArgs *args;
83 args = malloc(sizeof(*args));
86 args->imc_bootstrap_handle = NACL_INVALID_HANDLE;
88 args->irt_desc = NULL;
89 args->irt_load_optional = 0;
90 args->enable_exception_handling = 0;
91 args->enable_debug_stub = 0;
92 args->enable_dyncode_syscalls = 1;
94 /* TODO(ncbray): default to 0. */
95 args->skip_qualification =
96 getenv("NACL_DANGEROUS_SKIP_QUALIFICATION_TEST") != NULL;
97 args->initial_nexe_max_code_bytes = 0; /* No limit */
98 #if NACL_LINUX || NACL_OSX
99 args->debug_stub_server_bound_socket_fd = NACL_INVALID_SOCKET;
102 args->debug_stub_server_port_selected_handler_func = NULL;
104 args->create_memory_object_func = NULL;
105 args->validation_cache = NULL;
107 args->broker_duplicate_handle_func = NULL;
108 args->attach_debug_exception_handler_func = NULL;
110 #if NACL_LINUX || NACL_OSX
111 args->number_of_cores = -1; /* unknown */
114 args->prereserved_sandbox_size = 0;
116 args->nexe_desc = NULL;
118 args->argc = default_argc;
119 args->argv = (char **) default_argv;
123 static struct NaClDesc *IrtDescFromFd(int irt_fd) {
124 struct NaClDesc *irt_desc;
128 "IrtDescFromFd: Integrated runtime (IRT) not present.\n");
131 /* Takes ownership of the FD. */
132 irt_desc = NaClDescIoDescFromDescAllocCtor(irt_fd, NACL_ABI_O_RDONLY);
133 if (NULL == irt_desc) {
135 "IrtDescFromFd: failed to construct NaClDesc object from"
142 static char kFakeIrtName[] = "\0IRT";
144 static void NaClLoadIrt(struct NaClApp *nap, struct NaClDesc *irt_desc) {
145 struct NaClRichFileInfo info;
146 struct NaClValidationMetadata metadata;
147 NaClErrorCode errcode;
149 /* Attach file origin info to the IRT's NaClDesc. */
150 NaClRichFileInfoCtor(&info);
152 * For the IRT use a fake file name with null characters at the begining and
153 * the end of the name.
154 * TODO(ncbray): plumb the real filename in from Chrome.
155 * Note that when the file info is attached to the NaClDesc, information from
156 * stat is incorporated into the metadata. There is no functional reason to
157 * attach the info to the NaClDesc other than to create the final metadata.
158 * TODO(ncbray): API for deriving metadata from a NaClDesc without attaching.
161 info.file_path = kFakeIrtName;
162 info.file_path_length = sizeof(kFakeIrtName);
163 NaClSetFileOriginInfo(irt_desc, &info);
164 /* Don't free the info struct because we don't own the file path string. */
166 NaClMetadataFromNaClDescCtor(&metadata, irt_desc);
167 errcode = NaClMainLoadIrt(nap, irt_desc, &metadata);
168 if (errcode != LOAD_OK) {
170 "NaClLoadIrt: Failed to load the integrated runtime (IRT): %s\n",
171 NaClErrorString(errcode));
174 NaClMetadataDtor(&metadata);
177 static int LoadApp(struct NaClApp *nap, struct NaClChromeMainArgs *args) {
178 NaClErrorCode errcode = LOAD_OK;
179 int has_bootstrap_channel = args->imc_bootstrap_handle != NACL_INVALID_HANDLE;
181 CHECK(g_initialized);
184 * TODO(teravest): Remove this once Chromium uses NaClSetFatalErrorCallback.
186 if (has_bootstrap_channel && g_fatal_error_handler == NULL) {
187 NaClBootstrapChannelErrorReporterInit();
188 NaClErrorLogHookInit(NaClBootstrapChannelErrorReporter, nap);
191 /* Allow or disallow dyncode API based on args. */
192 nap->enable_dyncode_syscalls = args->enable_dyncode_syscalls;
193 nap->initial_nexe_max_code_bytes = args->initial_nexe_max_code_bytes;
194 nap->pnacl_mode = args->pnacl_mode;
197 g_prereserved_sandbox_size = args->prereserved_sandbox_size;
199 #if NACL_LINUX || NACL_OSX
201 * Overwrite value of sc_nprocessors_onln set in NaClAppCtor. In
202 * the Chrome embedding, the outer sandbox was already enabled when
203 * the NaClApp Ctor was invoked, so a bogus value was written in
204 * sc_nprocessors_onln.
206 if (-1 != args->number_of_cores) {
207 nap->sc_nprocessors_onln = args->number_of_cores;
211 if (args->create_memory_object_func != NULL)
212 NaClSetCreateMemoryObjectFunc(args->create_memory_object_func);
214 /* Inject the validation caching interface, if it exists. */
215 nap->validation_cache = args->validation_cache;
218 if (args->broker_duplicate_handle_func != NULL)
219 NaClSetBrokerDuplicateHandleFunc(args->broker_duplicate_handle_func);
222 NaClAppInitialDescriptorHookup(nap);
225 * NACL_SERVICE_PORT_DESCRIPTOR and NACL_SERVICE_ADDRESS_DESCRIPTOR
230 * in order to report load error to the browser plugin through the
231 * secure command channel, we do not immediate jump to cleanup code
232 * on error. rather, we continue processing (assuming earlier
233 * errors do not make it inappropriate) until the secure command
234 * channel is set up, and then bail out.
238 * Ensure this operating system platform is supported.
240 if (args->skip_qualification) {
241 fprintf(stderr, "PLATFORM QUALIFICATION DISABLED - "
242 "Native Client's sandbox will be unreliable!\n");
244 errcode = NACL_FI_VAL("pq", NaClErrorCode,
245 NaClRunSelQualificationTests());
246 if (LOAD_OK != errcode) {
247 nap->module_load_status = errcode;
248 fprintf(stderr, "Error while loading in SelMain: %s\n",
249 NaClErrorString(errcode));
254 * Patch the Windows exception dispatcher to be safe in the case
255 * of faults inside x86-64 sandboxed code. The sandbox is not
256 * secure on 64-bit Windows without this.
258 #if (NACL_WINDOWS && NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && \
259 NACL_BUILD_SUBARCH == 64)
260 NaClPatchWindowsExceptionDispatcher();
262 NaClSignalTestCrashOnStartup();
264 nap->enable_exception_handling = args->enable_exception_handling;
266 if (args->enable_exception_handling || args->enable_debug_stub) {
268 /* NaCl's signal handler is always enabled on Linux. */
270 if (!NaClInterceptMachExceptions()) {
271 NaClLog(LOG_FATAL, "LoadApp: Failed to set up Mach exception handler\n");
274 nap->attach_debug_exception_handler_func =
275 args->attach_debug_exception_handler_func;
277 # error Unknown host OS
281 NaClSignalHandlerInit();
284 /* Give debuggers a well known point at which xlate_base is known. */
287 if (has_bootstrap_channel) {
288 NaClCreateServiceSocket(nap);
290 * LOG_FATAL errors that occur before NaClSetUpBootstrapChannel will
291 * not be reported via the crash log mechanism (for Chromium
292 * embedding of NaCl, shown in the JavaScript console).
294 * Some errors, such as due to NaClRunSelQualificationTests, do not
295 * trigger a LOG_FATAL but instead set module_load_status to be sent
296 * in the start_module RPC reply. Log messages associated with such
297 * errors would be seen, since NaClSetUpBootstrapChannel will get
300 NaClSetUpBootstrapChannel(nap, args->imc_bootstrap_handle);
303 if (args->nexe_desc) {
304 NaClAppLoadModule(nap, args->nexe_desc, NULL, NULL);
305 NaClDescUnref(args->nexe_desc);
306 args->nexe_desc = NULL;
309 if (has_bootstrap_channel) {
310 NACL_FI_FATAL("BeforeSecureCommandChannel");
312 * Spawns a thread that uses the command channel.
313 * Hereafter any changes to nap should be done while holding locks.
315 NaClSecureCommandChannel(nap);
317 NaClLog(4, "NaClSecureCommandChannel has spawned channel\n");
319 NaClLog(4, "secure service = %"NACL_PRIxPTR"\n",
320 (uintptr_t) nap->secure_service);
321 NACL_FI_FATAL("BeforeWaitForStartModule");
323 if (NULL != nap->secure_service) {
324 NaClErrorCode start_result;
326 * wait for start_module RPC call on secure channel thread.
328 start_result = NaClWaitForStartModuleCommand(nap);
329 if (LOAD_OK == errcode) {
330 errcode = start_result;
335 NACL_FI_FATAL("BeforeLoadIrt");
338 * error reporting done; can quit now if there was an error earlier.
340 if (LOAD_OK != errcode) {
345 * Load the integrated runtime (IRT) library.
346 * Skip if irt_load_optional and the nexe doesn't have the usual 256MB
347 * segment gap. PNaCl's disabling of the segment gap doesn't actually
348 * disable the segment gap. It only only reduces it drastically.
350 if (args->irt_load_optional && nap->dynamic_text_end < 0x10000000) {
352 "Skipped NaClLoadIrt, irt_load_optional with dynamic_text_end: %"
353 NACL_PRIxPTR"\n", nap->dynamic_text_end);
355 if (args->irt_fd != -1) {
356 CHECK(args->irt_desc == NULL);
357 args->irt_desc = IrtDescFromFd(args->irt_fd);
360 if (args->irt_desc != NULL) {
361 NaClLoadIrt(nap, args->irt_desc);
362 NaClDescUnref(args->irt_desc);
363 args->irt_desc = NULL;
367 if (has_bootstrap_channel) {
368 if (NACL_FI_ERROR_COND("LaunchServiceThreads",
369 !NaClAppLaunchServiceThreads(nap))) {
370 NaClLog(LOG_FATAL, "Launch service threads failed\n");
374 if (args->enable_debug_stub) {
375 #if NACL_LINUX || NACL_OSX
376 if (args->debug_stub_server_bound_socket_fd != NACL_INVALID_SOCKET) {
377 NaClDebugSetBoundSocket(args->debug_stub_server_bound_socket_fd);
380 if (!NaClDebugInit(nap)) {
384 if (NULL != args->debug_stub_server_port_selected_handler_func) {
385 args->debug_stub_server_port_selected_handler_func(nap->debug_stub_port);
396 * If there is a secure command channel, we sent an RPC reply with
397 * the reason that the nexe was rejected. If we exit now, that
398 * reply may still be in-flight and the various channel closure (esp
399 * reverse channel) may be detected first. This would result in a
400 * crash being reported, rather than the error in the RPC reply.
401 * Instead, we wait for the hard-shutdown on the command channel.
403 if (LOAD_OK != errcode) {
404 NaClBlockIfCommandChannelExists(nap);
407 * Don't return LOAD_OK if we had some failure loading.
409 errcode = LOAD_INTERNAL;
414 static int StartApp(struct NaClApp *nap, struct NaClChromeMainArgs *args) {
416 struct NaClEnvCleanser env_cleanser;
417 char const *const *envp;
419 NACL_FI_FATAL("BeforeEnvCleanserCtor");
421 NaClEnvCleanserCtor(&env_cleanser, 1);
422 if (!NaClEnvCleanserInit(&env_cleanser, NaClGetEnviron(), NULL)) {
423 NaClLog(LOG_FATAL, "Failed to initialise env cleanser\n");
425 envp = NaClEnvCleanserEnvironment(&env_cleanser);
427 if (NACL_FI_ERROR_COND(
429 !NaClCreateMainThread(nap, args->argc, args->argv, envp))) {
430 NaClLog(LOG_FATAL, "creating main thread failed\n");
432 NACL_FI_FATAL("BeforeEnvCleanserDtor");
434 NaClEnvCleanserDtor(&env_cleanser);
436 ret_code = NaClWaitForMainThreadToExit(nap);
438 if (NACL_ABI_WIFEXITED(nap->exit_status)) {
440 * Under Chrome, a call to _exit() often indicates that something
441 * has gone awry, so we report it here to aid debugging.
443 * This conditional does not run if the NaCl process was
444 * terminated forcibly, which is the normal case under Chrome.
445 * This forcible exit is triggered by the renderer closing the
446 * trusted SRPC channel, which we record as NACL_ABI_SIGKILL
449 NaClLog(LOG_INFO, "NaCl untrusted code called _exit(0x%x)\n", ret_code);
454 void NaClChromeMainStartApp(struct NaClApp *nap,
455 struct NaClChromeMainArgs *args) {
457 NaClChromeMainStart(nap, args, &status);
459 * exit_group or equiv kills any still running threads while module
460 * addr space is still valid. otherwise we'd have to kill threads
461 * before we clean up the address space.
466 int NaClChromeMainStart(struct NaClApp *nap,
467 struct NaClChromeMainArgs *args,
469 int load_ok = LOAD_OK == LoadApp(nap, args);
471 *exit_status = StartApp(nap, args);