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->enable_exception_handling = 0;
90 args->enable_debug_stub = 0;
91 args->enable_dyncode_syscalls = 1;
93 /* TODO(ncbray): default to 0. */
94 args->skip_qualification =
95 getenv("NACL_DANGEROUS_SKIP_QUALIFICATION_TEST") != NULL;
96 args->initial_nexe_max_code_bytes = 0; /* No limit */
97 #if NACL_LINUX || NACL_OSX
98 args->debug_stub_server_bound_socket_fd = NACL_INVALID_SOCKET;
101 args->debug_stub_server_port_selected_handler_func = NULL;
103 args->create_memory_object_func = NULL;
104 args->validation_cache = NULL;
106 args->broker_duplicate_handle_func = NULL;
107 args->attach_debug_exception_handler_func = NULL;
109 #if NACL_LINUX || NACL_OSX
110 args->number_of_cores = -1; /* unknown */
113 args->prereserved_sandbox_size = 0;
115 args->nexe_desc = NULL;
117 args->argc = default_argc;
118 args->argv = (char **) default_argv;
122 static struct NaClDesc *IrtDescFromFd(int irt_fd) {
123 struct NaClDesc *irt_desc;
126 NaClLog(LOG_FATAL, "NaClLoadIrt: Integrated runtime (IRT) not present.\n");
129 /* Takes ownership of the FD. */
130 irt_desc = NaClDescIoDescFromDescAllocCtor(irt_fd, NACL_ABI_O_RDONLY);
131 if (NULL == irt_desc) {
133 "NaClLoadIrt: failed to construct NaClDesc object from"
140 static char kFakeIrtName[] = "\0IRT";
142 static void NaClLoadIrt(struct NaClApp *nap, struct NaClDesc *irt_desc) {
143 struct NaClRichFileInfo info;
144 struct NaClValidationMetadata metadata;
145 NaClErrorCode errcode;
147 /* Attach file origin info to the IRT's NaClDesc. */
148 NaClRichFileInfoCtor(&info);
150 * For the IRT use a fake file name with null characters at the begining and
151 * the end of the name.
152 * TODO(ncbray): plumb the real filename in from Chrome.
153 * Note that when the file info is attached to the NaClDesc, information from
154 * stat is incorporated into the metadata. There is no functional reason to
155 * attach the info to the NaClDesc other than to create the final metadata.
156 * TODO(ncbray): API for deriving metadata from a NaClDesc without attaching.
159 info.file_path = kFakeIrtName;
160 info.file_path_length = sizeof(kFakeIrtName);
161 NaClSetFileOriginInfo(irt_desc, &info);
162 /* Don't free the info struct because we don't own the file path string. */
164 NaClMetadataFromNaClDescCtor(&metadata, irt_desc);
165 errcode = NaClMainLoadIrt(nap, irt_desc, &metadata);
166 if (errcode != LOAD_OK) {
168 "NaClLoadIrt: Failed to load the integrated runtime (IRT): %s\n",
169 NaClErrorString(errcode));
172 NaClMetadataDtor(&metadata);
175 static int LoadApp(struct NaClApp *nap, struct NaClChromeMainArgs *args) {
176 NaClErrorCode errcode = LOAD_OK;
177 int has_bootstrap_channel = args->imc_bootstrap_handle != NACL_INVALID_HANDLE;
179 CHECK(g_initialized);
182 * TODO(teravest): Remove this once Chromium uses NaClSetFatalErrorCallback.
184 if (has_bootstrap_channel && g_fatal_error_handler == NULL) {
185 NaClBootstrapChannelErrorReporterInit();
186 NaClErrorLogHookInit(NaClBootstrapChannelErrorReporter, nap);
189 /* Allow or disallow dyncode API based on args. */
190 nap->enable_dyncode_syscalls = args->enable_dyncode_syscalls;
191 nap->initial_nexe_max_code_bytes = args->initial_nexe_max_code_bytes;
192 nap->pnacl_mode = args->pnacl_mode;
195 g_prereserved_sandbox_size = args->prereserved_sandbox_size;
197 #if NACL_LINUX || NACL_OSX
199 * Overwrite value of sc_nprocessors_onln set in NaClAppCtor. In
200 * the Chrome embedding, the outer sandbox was already enabled when
201 * the NaClApp Ctor was invoked, so a bogus value was written in
202 * sc_nprocessors_onln.
204 if (-1 != args->number_of_cores) {
205 nap->sc_nprocessors_onln = args->number_of_cores;
209 if (args->create_memory_object_func != NULL)
210 NaClSetCreateMemoryObjectFunc(args->create_memory_object_func);
212 /* Inject the validation caching interface, if it exists. */
213 nap->validation_cache = args->validation_cache;
216 if (args->broker_duplicate_handle_func != NULL)
217 NaClSetBrokerDuplicateHandleFunc(args->broker_duplicate_handle_func);
220 NaClAppInitialDescriptorHookup(nap);
223 * NACL_SERVICE_PORT_DESCRIPTOR and NACL_SERVICE_ADDRESS_DESCRIPTOR
228 * in order to report load error to the browser plugin through the
229 * secure command channel, we do not immediate jump to cleanup code
230 * on error. rather, we continue processing (assuming earlier
231 * errors do not make it inappropriate) until the secure command
232 * channel is set up, and then bail out.
236 * Ensure this operating system platform is supported.
238 if (args->skip_qualification) {
239 fprintf(stderr, "PLATFORM QUALIFICATION DISABLED - "
240 "Native Client's sandbox will be unreliable!\n");
242 errcode = NACL_FI_VAL("pq", NaClErrorCode,
243 NaClRunSelQualificationTests());
244 if (LOAD_OK != errcode) {
245 nap->module_load_status = errcode;
246 fprintf(stderr, "Error while loading in SelMain: %s\n",
247 NaClErrorString(errcode));
252 * Patch the Windows exception dispatcher to be safe in the case
253 * of faults inside x86-64 sandboxed code. The sandbox is not
254 * secure on 64-bit Windows without this.
256 #if (NACL_WINDOWS && NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && \
257 NACL_BUILD_SUBARCH == 64)
258 NaClPatchWindowsExceptionDispatcher();
260 NaClSignalTestCrashOnStartup();
262 nap->enable_exception_handling = args->enable_exception_handling;
264 if (args->enable_exception_handling || args->enable_debug_stub) {
266 /* NaCl's signal handler is always enabled on Linux. */
268 if (!NaClInterceptMachExceptions()) {
269 NaClLog(LOG_FATAL, "LoadApp: Failed to set up Mach exception handler\n");
272 nap->attach_debug_exception_handler_func =
273 args->attach_debug_exception_handler_func;
275 # error Unknown host OS
279 NaClSignalHandlerInit();
282 /* Give debuggers a well known point at which xlate_base is known. */
285 if (has_bootstrap_channel) {
286 NaClCreateServiceSocket(nap);
288 * LOG_FATAL errors that occur before NaClSetUpBootstrapChannel will
289 * not be reported via the crash log mechanism (for Chromium
290 * embedding of NaCl, shown in the JavaScript console).
292 * Some errors, such as due to NaClRunSelQualificationTests, do not
293 * trigger a LOG_FATAL but instead set module_load_status to be sent
294 * in the start_module RPC reply. Log messages associated with such
295 * errors would be seen, since NaClSetUpBootstrapChannel will get
298 NaClSetUpBootstrapChannel(nap, args->imc_bootstrap_handle);
301 if (args->nexe_desc) {
302 NaClAppLoadModule(nap, args->nexe_desc, NULL, NULL);
303 NaClDescUnref(args->nexe_desc);
304 args->nexe_desc = NULL;
307 if (has_bootstrap_channel) {
308 NACL_FI_FATAL("BeforeSecureCommandChannel");
310 * Spawns a thread that uses the command channel.
311 * Hereafter any changes to nap should be done while holding locks.
313 NaClSecureCommandChannel(nap);
315 NaClLog(4, "NaClSecureCommandChannel has spawned channel\n");
317 NaClLog(4, "secure service = %"NACL_PRIxPTR"\n",
318 (uintptr_t) nap->secure_service);
319 NACL_FI_FATAL("BeforeWaitForStartModule");
321 if (NULL != nap->secure_service) {
322 NaClErrorCode start_result;
324 * wait for start_module RPC call on secure channel thread.
326 start_result = NaClWaitForStartModuleCommand(nap);
327 if (LOAD_OK == errcode) {
328 errcode = start_result;
333 NACL_FI_FATAL("BeforeLoadIrt");
336 * error reporting done; can quit now if there was an error earlier.
338 if (LOAD_OK != errcode) {
343 * Load the integrated runtime (IRT) library.
345 if (args->irt_fd != -1) {
346 CHECK(args->irt_desc == NULL);
347 args->irt_desc = IrtDescFromFd(args->irt_fd);
350 if (args->irt_desc != NULL) {
351 NaClLoadIrt(nap, args->irt_desc);
352 NaClDescUnref(args->irt_desc);
353 args->irt_desc = NULL;
356 if (has_bootstrap_channel) {
357 if (NACL_FI_ERROR_COND("LaunchServiceThreads",
358 !NaClAppLaunchServiceThreads(nap))) {
359 NaClLog(LOG_FATAL, "Launch service threads failed\n");
363 if (args->enable_debug_stub) {
364 #if NACL_LINUX || NACL_OSX
365 if (args->debug_stub_server_bound_socket_fd != NACL_INVALID_SOCKET) {
366 NaClDebugSetBoundSocket(args->debug_stub_server_bound_socket_fd);
369 if (!NaClDebugInit(nap)) {
373 if (NULL != args->debug_stub_server_port_selected_handler_func) {
374 args->debug_stub_server_port_selected_handler_func(nap->debug_stub_port);
385 * If there is a secure command channel, we sent an RPC reply with
386 * the reason that the nexe was rejected. If we exit now, that
387 * reply may still be in-flight and the various channel closure (esp
388 * reverse channel) may be detected first. This would result in a
389 * crash being reported, rather than the error in the RPC reply.
390 * Instead, we wait for the hard-shutdown on the command channel.
392 if (LOAD_OK != errcode) {
393 NaClBlockIfCommandChannelExists(nap);
396 * Don't return LOAD_OK if we had some failure loading.
398 errcode = LOAD_INTERNAL;
403 static int StartApp(struct NaClApp *nap, struct NaClChromeMainArgs *args) {
405 struct NaClEnvCleanser env_cleanser;
406 char const *const *envp;
408 NACL_FI_FATAL("BeforeEnvCleanserCtor");
410 NaClEnvCleanserCtor(&env_cleanser, 1);
411 if (!NaClEnvCleanserInit(&env_cleanser, NaClGetEnviron(), NULL)) {
412 NaClLog(LOG_FATAL, "Failed to initialise env cleanser\n");
414 envp = NaClEnvCleanserEnvironment(&env_cleanser);
416 if (NACL_FI_ERROR_COND(
418 !NaClCreateMainThread(nap, args->argc, args->argv, envp))) {
419 NaClLog(LOG_FATAL, "creating main thread failed\n");
421 NACL_FI_FATAL("BeforeEnvCleanserDtor");
423 NaClEnvCleanserDtor(&env_cleanser);
425 ret_code = NaClWaitForMainThreadToExit(nap);
427 if (NACL_ABI_WIFEXITED(nap->exit_status)) {
429 * Under Chrome, a call to _exit() often indicates that something
430 * has gone awry, so we report it here to aid debugging.
432 * This conditional does not run if the NaCl process was
433 * terminated forcibly, which is the normal case under Chrome.
434 * This forcible exit is triggered by the renderer closing the
435 * trusted SRPC channel, which we record as NACL_ABI_SIGKILL
438 NaClLog(LOG_INFO, "NaCl untrusted code called _exit(0x%x)\n", ret_code);
443 void NaClChromeMainStartApp(struct NaClApp *nap,
444 struct NaClChromeMainArgs *args) {
446 NaClChromeMainStart(nap, args, &status);
448 * exit_group or equiv kills any still running threads while module
449 * addr space is still valid. otherwise we'd have to kill threads
450 * before we clean up the address space.
455 int NaClChromeMainStart(struct NaClApp *nap,
456 struct NaClChromeMainArgs *args,
458 int load_ok = LOAD_OK == LoadApp(nap, args);
460 *exit_status = StartApp(nap, args);