1 : /*
2 : * Copyright (c) 2011 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 :
11 : #include "native_client/src/include/portability.h"
12 :
13 : #include <stdio.h>
14 : #include <stdlib.h>
15 : #include <string.h>
16 :
17 : #include "native_client/src/include/elf_constants.h"
18 : #include "native_client/src/include/elf.h"
19 : #include "native_client/src/include/nacl_macros.h"
20 : #include "native_client/src/include/win/mman.h"
21 : #include "native_client/src/shared/platform/nacl_check.h"
22 : #include "native_client/src/shared/platform/nacl_log.h"
23 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
24 : #include "native_client/src/shared/platform/nacl_time.h"
25 :
26 : #include "native_client/src/shared/srpc/nacl_srpc.h"
27 :
28 : #include "native_client/src/trusted/manifest_name_service_proxy/manifest_proxy.h"
29 : #include "native_client/src/trusted/perf_counter/nacl_perf_counter.h"
30 :
31 : #include "native_client/src/trusted/reverse_service/reverse_control_rpc.h"
32 :
33 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
34 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
35 :
36 : #include "native_client/src/trusted/service_runtime/arch/sel_ldr_arch.h"
37 : #include "native_client/src/trusted/service_runtime/elf_util.h"
38 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
39 : #include "native_client/src/trusted/service_runtime/nacl_debug_init.h"
40 : #include "native_client/src/trusted/service_runtime/nacl_kern_services.h"
41 : #include "native_client/src/trusted/service_runtime/nacl_oop_debugger_hooks.h"
42 : #include "native_client/src/trusted/service_runtime/nacl_switch_to_app.h"
43 : #include "native_client/src/trusted/service_runtime/nacl_syscall_common.h"
44 : #include "native_client/src/trusted/service_runtime/nacl_text.h"
45 : #include "native_client/src/trusted/service_runtime/outer_sandbox.h"
46 : #include "native_client/src/trusted/service_runtime/sel_memory.h"
47 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
48 : #include "native_client/src/trusted/service_runtime/sel_ldr_thread_interface.h"
49 : #include "native_client/src/trusted/service_runtime/sel_util.h"
50 : #include "native_client/src/trusted/service_runtime/sel_addrspace.h"
51 :
52 : #if !defined(SIZE_T_MAX)
53 : # define SIZE_T_MAX (~(size_t) 0)
54 : #endif
55 :
56 :
57 : /*
58 : * Fill from static_text_end to end of that page with halt
59 : * instruction, which is at least NACL_HALT_LEN in size when no
60 : * dynamic text is present. Does not touch dynamic text region, which
61 : * should be pre-filled with HLTs.
62 : *
63 : * By adding NACL_HALT_SLED_SIZE, we ensure that the code region ends
64 : * with HLTs, just in case the CPU has a bug in which it fails to
65 : * check for running off the end of the x86 code segment.
66 : */
67 3 : void NaClFillEndOfTextRegion(struct NaClApp *nap) {
68 : size_t page_pad;
69 :
70 : /*
71 : * NOTE: make sure we are not silently overwriting data. It is the
72 : * toolchain's responsibility to ensure that a NACL_HALT_SLED_SIZE
73 : * gap exists.
74 : */
75 3 : if (0 != nap->data_start &&
76 : nap->static_text_end + NACL_HALT_SLED_SIZE > nap->data_start) {
77 0 : NaClLog(LOG_FATAL, "Missing gap between text and data for halt_sled\n");
78 : }
79 3 : if (0 != nap->rodata_start &&
80 : nap->static_text_end + NACL_HALT_SLED_SIZE > nap->rodata_start) {
81 0 : NaClLog(LOG_FATAL, "Missing gap between text and rodata for halt_sled\n");
82 : }
83 :
84 3 : if (NULL == nap->text_shm) {
85 : /*
86 : * No dynamic text exists. Space for NACL_HALT_SLED_SIZE must
87 : * exist.
88 : */
89 0 : page_pad = (NaClRoundAllocPage(nap->static_text_end + NACL_HALT_SLED_SIZE)
90 : - nap->static_text_end);
91 0 : CHECK(page_pad >= NACL_HALT_SLED_SIZE);
92 0 : CHECK(page_pad < NACL_MAP_PAGESIZE + NACL_HALT_SLED_SIZE);
93 : } else {
94 : /*
95 : * Dynamic text exists; the halt sled resides in the dynamic text
96 : * region, so all we need to do here is to round out the last
97 : * static text page with HLT instructions. It doesn't matter if
98 : * the size of this region is smaller than NACL_HALT_SLED_SIZE --
99 : * this is just to fully initialize the page, rather than (later)
100 : * decoding/validating zero-filled memory as instructions.
101 : */
102 3 : page_pad = NaClRoundAllocPage(nap->static_text_end) - nap->static_text_end;
103 : }
104 :
105 3 : NaClLog(4,
106 : "Filling with halts: %08"NACL_PRIxPTR", %08"NACL_PRIxS" bytes\n",
107 : nap->mem_start + nap->static_text_end,
108 : page_pad);
109 :
110 3 : NaClFillMemoryRegionWithHalt((void *)(nap->mem_start + nap->static_text_end),
111 : page_pad);
112 :
113 3 : nap->static_text_end += page_pad;
114 3 : }
115 :
116 : /*
117 : * Basic address space layout sanity check.
118 : */
119 : NaClErrorCode NaClCheckAddressSpaceLayoutSanity(struct NaClApp *nap,
120 : uintptr_t rodata_end,
121 : uintptr_t data_end,
122 8 : uintptr_t max_vaddr) {
123 8 : if (0 != nap->data_start) {
124 8 : if (data_end != max_vaddr) {
125 1 : NaClLog(LOG_INFO, "data segment is not last\n");
126 1 : return LOAD_DATA_NOT_LAST_SEGMENT;
127 : }
128 0 : } else if (0 != nap->rodata_start) {
129 0 : if (NaClRoundAllocPage(rodata_end) != max_vaddr) {
130 : /*
131 : * This should be unreachable, but we include it just for
132 : * completeness.
133 : *
134 : * Here is why it is unreachable:
135 : *
136 : * NaClPhdrChecks checks the test segment starting address. The
137 : * only allowed loaded segments are text, data, and rodata.
138 : * Thus unless the rodata is in the trampoline region, it must
139 : * be after the text. And NaClElfImageValidateProgramHeaders
140 : * ensures that all segments start after the trampoline region.
141 : */
142 0 : NaClLog(LOG_INFO, "no data segment, but rodata segment is not last\n");
143 0 : return LOAD_NO_DATA_BUT_RODATA_NOT_LAST_SEGMENT;
144 : }
145 : }
146 7 : if (0 != nap->rodata_start && 0 != nap->data_start) {
147 6 : if (rodata_end > nap->data_start) {
148 1 : NaClLog(LOG_INFO, "rodata_overlaps data.\n");
149 1 : return LOAD_RODATA_OVERLAPS_DATA;
150 : }
151 : }
152 6 : if (0 != nap->rodata_start) {
153 5 : if (NaClRoundAllocPage(NaClEndOfStaticText(nap)) > nap->rodata_start) {
154 1 : return LOAD_TEXT_OVERLAPS_RODATA;
155 : }
156 1 : } else if (0 != nap->data_start) {
157 1 : if (NaClRoundAllocPage(NaClEndOfStaticText(nap)) > nap->data_start) {
158 1 : return LOAD_TEXT_OVERLAPS_DATA;
159 : }
160 : }
161 4 : if (0 != nap->rodata_start &&
162 : NaClRoundAllocPage(nap->rodata_start) != nap->rodata_start) {
163 0 : NaClLog(LOG_INFO, "rodata_start not a multiple of allocation size\n");
164 0 : return LOAD_BAD_RODATA_ALIGNMENT;
165 : }
166 4 : if (0 != nap->data_start &&
167 : NaClRoundAllocPage(nap->data_start) != nap->data_start) {
168 0 : NaClLog(LOG_INFO, "data_start not a multiple of allocation size\n");
169 0 : return LOAD_BAD_DATA_ALIGNMENT;
170 : }
171 4 : return LOAD_OK;
172 : }
173 :
174 11 : void NaClLogAddressSpaceLayout(struct NaClApp *nap) {
175 11 : NaClLog(2, "NaClApp addr space layout:\n");
176 11 : NaClLog(2, "nap->static_text_end = 0x%016"NACL_PRIxPTR"\n",
177 : nap->static_text_end);
178 11 : NaClLog(2, "nap->dynamic_text_start = 0x%016"NACL_PRIxPTR"\n",
179 : nap->dynamic_text_start);
180 11 : NaClLog(2, "nap->dynamic_text_end = 0x%016"NACL_PRIxPTR"\n",
181 : nap->dynamic_text_end);
182 11 : NaClLog(2, "nap->rodata_start = 0x%016"NACL_PRIxPTR"\n",
183 : nap->rodata_start);
184 11 : NaClLog(2, "nap->data_start = 0x%016"NACL_PRIxPTR"\n",
185 : nap->data_start);
186 11 : NaClLog(2, "nap->data_end = 0x%016"NACL_PRIxPTR"\n",
187 : nap->data_end);
188 11 : NaClLog(2, "nap->break_addr = 0x%016"NACL_PRIxPTR"\n",
189 : nap->break_addr);
190 11 : NaClLog(2, "nap->initial_entry_pt = 0x%016"NACL_PRIxPTR"\n",
191 : nap->initial_entry_pt);
192 11 : NaClLog(2, "nap->user_entry_pt = 0x%016"NACL_PRIxPTR"\n",
193 : nap->user_entry_pt);
194 11 : NaClLog(2, "nap->bundle_size = 0x%x\n", nap->bundle_size);
195 11 : }
196 :
197 : NaClErrorCode NaClAppLoadFile(struct Gio *gp,
198 11 : struct NaClApp *nap) {
199 11 : NaClErrorCode ret = LOAD_INTERNAL;
200 : NaClErrorCode subret;
201 : uintptr_t rodata_end;
202 : uintptr_t data_end;
203 : uintptr_t max_vaddr;
204 11 : struct NaClElfImage *image = NULL;
205 : struct NaClPerfCounter time_load_file;
206 :
207 11 : NaClPerfCounterCtor(&time_load_file, "NaClAppLoadFile");
208 :
209 : /* NACL_MAX_ADDR_BITS < 32 */
210 11 : if (nap->addr_bits > NACL_MAX_ADDR_BITS) {
211 0 : ret = LOAD_ADDR_SPACE_TOO_BIG;
212 0 : goto done;
213 : }
214 :
215 11 : nap->stack_size = NaClRoundAllocPage(nap->stack_size);
216 :
217 : /* temporay object will be deleted at end of function */
218 11 : image = NaClElfImageNew(gp, &subret);
219 11 : if (NULL == image) {
220 0 : ret = subret;
221 0 : goto done;
222 : }
223 :
224 11 : subret = NaClElfImageValidateElfHeader(image);
225 11 : if (LOAD_OK != subret) {
226 0 : ret = subret;
227 0 : goto done;
228 : }
229 :
230 11 : subret = NaClElfImageValidateProgramHeaders(image,
231 : nap->addr_bits,
232 : &nap->static_text_end,
233 : &nap->rodata_start,
234 : &rodata_end,
235 : &nap->data_start,
236 : &data_end,
237 : &max_vaddr);
238 11 : if (LOAD_OK != subret) {
239 3 : ret = subret;
240 3 : goto done;
241 : }
242 :
243 8 : if (0 == nap->data_start) {
244 0 : if (0 == nap->rodata_start) {
245 0 : if (NaClRoundAllocPage(max_vaddr) - max_vaddr < NACL_HALT_SLED_SIZE) {
246 : /*
247 : * if no rodata and no data, we make sure that there is space for
248 : * the halt sled.
249 : */
250 0 : max_vaddr += NACL_MAP_PAGESIZE;
251 : }
252 : } else {
253 : /*
254 : * no data, but there is rodata. this means max_vaddr is just
255 : * where rodata ends. this might not be at an allocation
256 : * boundary, and in this the page would not be writable. round
257 : * max_vaddr up to the next allocation boundary so that bss will
258 : * be at the next writable region.
259 : */
260 : ;
261 : }
262 0 : max_vaddr = NaClRoundAllocPage(max_vaddr);
263 : }
264 : /*
265 : * max_vaddr -- the break or the boundary between data (initialized
266 : * and bss) and the address space hole -- does not have to be at a
267 : * page boundary.
268 : */
269 8 : nap->break_addr = max_vaddr;
270 8 : nap->data_end = max_vaddr;
271 :
272 8 : NaClLog(4, "Values from NaClElfImageValidateProgramHeaders:\n");
273 8 : NaClLog(4, "rodata_start = 0x%08"NACL_PRIxPTR"\n", nap->rodata_start);
274 8 : NaClLog(4, "rodata_end = 0x%08"NACL_PRIxPTR"\n", rodata_end);
275 8 : NaClLog(4, "data_start = 0x%08"NACL_PRIxPTR"\n", nap->data_start);
276 8 : NaClLog(4, "data_end = 0x%08"NACL_PRIxPTR"\n", data_end);
277 8 : NaClLog(4, "max_vaddr = 0x%08"NACL_PRIxPTR"\n", max_vaddr);
278 :
279 : /* We now support only one bundle size. */
280 8 : nap->bundle_size = NACL_INSTR_BLOCK_SIZE;
281 :
282 8 : nap->initial_entry_pt = NaClElfImageGetEntryPoint(image);
283 8 : NaClLogAddressSpaceLayout(nap);
284 :
285 8 : if (!NaClAddrIsValidEntryPt(nap, nap->initial_entry_pt)) {
286 0 : ret = LOAD_BAD_ENTRY;
287 0 : goto done;
288 : }
289 :
290 8 : subret = NaClCheckAddressSpaceLayoutSanity(nap, rodata_end, data_end,
291 : max_vaddr);
292 8 : if (LOAD_OK != subret) {
293 4 : ret = subret;
294 4 : goto done;
295 : }
296 :
297 4 : NaClLog(2, "Allocating address space\n");
298 4 : NaClPerfCounterMark(&time_load_file, "PreAllocAddrSpace");
299 4 : NaClPerfCounterIntervalLast(&time_load_file);
300 4 : subret = NaClAllocAddrSpace(nap);
301 4 : NaClPerfCounterMark(&time_load_file,
302 : NACL_PERF_IMPORTANT_PREFIX "AllocAddrSpace");
303 4 : NaClPerfCounterIntervalLast(&time_load_file);
304 4 : if (LOAD_OK != subret) {
305 1 : ret = subret;
306 1 : goto done;
307 : }
308 :
309 : /*
310 : * Make sure the static image pages are marked writable before we try
311 : * to write them.
312 : */
313 3 : NaClLog(2, "Loading into memory\n");
314 3 : ret = NaCl_mprotect((void *) (nap->mem_start + NACL_TRAMPOLINE_START),
315 : NaClRoundAllocPage(nap->data_end) - NACL_TRAMPOLINE_START,
316 : PROT_READ | PROT_WRITE);
317 3 : if (0 != ret) {
318 0 : NaClLog(LOG_FATAL,
319 : "NaClAppLoadFile: Failed to make image pages writable. "
320 : "Error code 0x%x\n",
321 : ret);
322 : }
323 3 : subret = NaClElfImageLoad(image, gp, nap->addr_bits, nap->mem_start);
324 3 : if (LOAD_OK != subret) {
325 0 : ret = subret;
326 0 : goto done;
327 : }
328 :
329 : /*
330 : * NB: mem_map object has been initialized, but is empty.
331 : * NaClMakeDynamicTextShared does not touch it.
332 : *
333 : * NaClMakeDynamicTextShared also fills the dynamic memory region
334 : * with the architecture-specific halt instruction. If/when we use
335 : * memory mapping to save paging space for the dynamic region and
336 : * lazily halt fill the memory as the pages become
337 : * readable/executable, we must make sure that the *last*
338 : * NACL_MAP_PAGESIZE chunk is nonetheless mapped and written with
339 : * halts.
340 : */
341 3 : NaClLog(2,
342 : ("Replacing gap between static text and"
343 : " (ro)data with shareable memory\n"));
344 3 : subret = NaClMakeDynamicTextShared(nap);
345 3 : NaClPerfCounterMark(&time_load_file,
346 : NACL_PERF_IMPORTANT_PREFIX "MakeDynText");
347 3 : NaClPerfCounterIntervalLast(&time_load_file);
348 3 : if (LOAD_OK != subret) {
349 0 : ret = subret;
350 0 : goto done;
351 : }
352 : #if NACL_OSX && defined(NACL_STANDALONE)
353 : /*
354 : * Enable the outer sandbox on Mac. Do this as soon as possible.
355 : *
356 : * This is needed only when built for "Standalone" mode (which
357 : * currently means firefox plugin, as opposed to as an built-in
358 : * plugin in Chrome), since in the Chrome built-in/integrated build
359 : * the sandbox is enabled earlier, in chrome's zygote process
360 : * initialization / specialization when the zygote forks a copy of
361 : * itself to become sel_ldr (via sel_main_chrome.c's
362 : * NaClMainForChromium).
363 : *
364 : * It would be good to do this as soon as we have opened the
365 : * executable file and log file, but nacl_text.c currently does not
366 : * work in the sandbox. See
367 : * http://code.google.com/p/nativeclient/issues/detail?id=583
368 : *
369 : * This is needed for the test version of the ppapi plugin and post
370 : * saucer separation; the integrated plugin/sel_ldr implementation
371 : * that spawns from a chrome zygote image would have already enabled
372 : * the sandbox.
373 : *
374 : * We cannot enable the sandbox if file access is enabled.
375 : *
376 : * OSX's sandbox has a race condition where mprotect in the address
377 : * space set up code would sometimes fail, even before nacl_text.c.
378 : * Ideally we would enable the OSX sandbox before examining any
379 : * untrusted data (the nexe), but we have to wait until we address
380 : * space setup is done, which requires reading the ELF headers.
381 : */
382 3 : if (!NaClAclBypassChecks) {
383 0 : NaClEnableOuterSandbox();
384 : }
385 : #endif
386 :
387 : /*
388 : * NaClFillEndOfTextRegion will fill with halt instructions the
389 : * padding space after the static text region.
390 : *
391 : * Shm-backed dynamic text space was filled with halt instructions
392 : * in NaClMakeDynamicTextShared. This extends to the rodata. For
393 : * non-shm-backed text space, this extend to the next page (and not
394 : * allocation page). static_text_end is updated to include the
395 : * padding.
396 : */
397 3 : NaClFillEndOfTextRegion(nap);
398 :
399 : #if 0 == NACL_DANGEROUS_DEBUG_MODE_DISABLE_INNER_SANDBOX
400 3 : NaClLog(2, "Validating image\n");
401 3 : subret = NaClValidateImage(nap);
402 3 : NaClPerfCounterMark(&time_load_file,
403 : NACL_PERF_IMPORTANT_PREFIX "ValidateImg");
404 3 : NaClPerfCounterIntervalLast(&time_load_file);
405 3 : if (LOAD_OK != subret) {
406 0 : ret = subret;
407 0 : goto done;
408 : }
409 : #endif
410 :
411 3 : NaClLog(2, "Initializing arch switcher\n");
412 3 : NaClInitSwitchToApp(nap);
413 :
414 3 : NaClLog(2, "Installing trampoline\n");
415 3 : NaClLoadTrampoline(nap);
416 :
417 3 : NaClLog(2, "Installing springboard\n");
418 3 : NaClLoadSpringboard(nap);
419 :
420 : /*
421 : * NaClMemoryProtect also initializes the mem_map w/ information
422 : * about the memory pages and their current protection value.
423 : *
424 : * The contents of the dynamic text region will get remapped as
425 : * non-writable.
426 : */
427 3 : NaClLog(2, "Applying memory protection\n");
428 3 : subret = NaClMemoryProtection(nap);
429 3 : if (LOAD_OK != subret) {
430 0 : ret = subret;
431 0 : goto done;
432 : }
433 :
434 3 : NaClLog(2, "NaClAppLoadFile done; ");
435 3 : NaClLogAddressSpaceLayout(nap);
436 3 : ret = LOAD_OK;
437 11 : done:
438 11 : NaClElfImageDelete(image);
439 :
440 11 : NaClPerfCounterMark(&time_load_file, "EndLoadFile");
441 11 : NaClPerfCounterIntervalTotal(&time_load_file);
442 11 : return ret;
443 : }
444 :
445 : NaClErrorCode NaClAppLoadFileDynamically(struct NaClApp *nap,
446 0 : struct Gio *gio_file) {
447 0 : struct NaClElfImage *image = NULL;
448 0 : NaClErrorCode ret = LOAD_INTERNAL;
449 :
450 0 : image = NaClElfImageNew((struct Gio *) gio_file, &ret);
451 0 : if (NULL == image) {
452 0 : goto done;
453 : }
454 0 : ret = NaClElfImageValidateElfHeader(image);
455 0 : if (LOAD_OK != ret) {
456 0 : goto done;
457 : }
458 0 : ret = NaClElfImageLoadDynamically(image, nap, gio_file);
459 0 : if (LOAD_OK != ret) {
460 0 : goto done;
461 : }
462 0 : nap->user_entry_pt = nap->initial_entry_pt;
463 0 : nap->initial_entry_pt = NaClElfImageGetEntryPoint(image);
464 :
465 0 : done:
466 0 : NaClElfImageDelete(image);
467 0 : return ret;
468 : }
469 :
470 : int NaClAddrIsValidEntryPt(struct NaClApp *nap,
471 8 : uintptr_t addr) {
472 : #if defined(NACL_TARGET_ARM_THUMB2_MODE)
473 : /*
474 : * The entry point needs to be aligned 0xe mod 0x10. But ARM processors need
475 : * an odd target address to indicate that the target is in thumb mode. When
476 : * control is actually transferred, it is to the target address minus one.
477 : */
478 : if (0xf != (addr & (nap->bundle_size - 1))) {
479 : return 0;
480 : }
481 : #else
482 8 : if (0 != (addr & (nap->bundle_size - 1))) {
483 0 : return 0;
484 : }
485 : #endif
486 :
487 8 : return addr < nap->static_text_end;
488 : }
489 :
490 3 : int NaClAppLaunchServiceThreads(struct NaClApp *nap) {
491 3 : struct NaClManifestProxy *manifest_proxy = NULL;
492 3 : struct NaClKernService *kern_service = NULL;
493 3 : int rv = 0;
494 : enum NaClReverseChannelInitializationState init_state;
495 :
496 3 : NaClNameServiceLaunch(nap->name_service);
497 :
498 3 : kern_service = (struct NaClKernService *) malloc(sizeof *kern_service);
499 3 : if (NULL == kern_service) {
500 0 : NaClLog(LOG_ERROR,
501 : "NaClAppLaunchServiceThreads: No memory for kern service\n");
502 0 : goto done;
503 : }
504 :
505 3 : if (!NaClKernServiceCtor(kern_service,
506 : NaClAddrSpSquattingThreadIfFactoryFunction,
507 : (void *) nap,
508 : nap)) {
509 0 : NaClLog(LOG_ERROR,
510 : "NaClAppLaunchServiceThreads: KernServiceCtor failed\n");
511 0 : free(kern_service);
512 0 : kern_service = NULL;
513 0 : goto done;
514 : }
515 :
516 3 : if (!NaClSimpleServiceStartServiceThread((struct NaClSimpleService *)
517 : kern_service)) {
518 0 : NaClLog(LOG_ERROR,
519 : "NaClAppLaunchServiceThreads: KernService start service failed\n");
520 0 : goto done;
521 : }
522 : /*
523 : * NB: StartServiceThread grabbed another reference to kern_service,
524 : * used by the service thread. Closing the connection capability
525 : * should cause the service thread to shut down and in turn release
526 : * that reference.
527 : */
528 :
529 : /*
530 : * The locking here isn't really needed. Here is why:
531 : * reverse_channel_initialized is written in reverse_setup RPC
532 : * handler of the secure command channel RPC handler thread. and
533 : * the RPC order requires that the plugin invoke reverse_setup prior
534 : * to invoking start_module, so there will have been plenty of other
535 : * synchronization operations to force cache coherency
536 : * (module_may_start, for example, is set in the cache of the secure
537 : * channel RPC handler (in start_module) and read by the main
538 : * thread, and the synchronization operations needed to propagate
539 : * its value properly suffices to propagate
540 : * reverse_channel_initialized as well). However, reading it while
541 : * holding a lock is more obviously correct for tools like tsan.
542 : * Due to the RPC order, it is impossible for
543 : * reverse_channel_initialized to get set after the unlock and
544 : * before the if test.
545 : */
546 3 : NaClXMutexLock(&nap->mu);
547 : /*
548 : * If no reverse_setup RPC was made, then we do not set up a
549 : * manifest proxy. Otherwise, we make sure that the reverse channel
550 : * setup is done, so that the application can actually use
551 : * reverse-channel-based services such as the manifest proxy.
552 : */
553 3 : if (NACL_REVERSE_CHANNEL_UNINITIALIZED !=
554 : (init_state = nap->reverse_channel_initialization_state)) {
555 0 : while (NACL_REVERSE_CHANNEL_INITIALIZED !=
556 : (init_state = nap->reverse_channel_initialization_state)) {
557 0 : NaClXCondVarWait(&nap->cv, &nap->mu);
558 : }
559 : }
560 3 : NaClXMutexUnlock(&nap->mu);
561 3 : if (NACL_REVERSE_CHANNEL_INITIALIZED != init_state) {
562 3 : NaClLog(3,
563 : ("NaClAppLaunchServiceThreads: no reverse channel;"
564 : " launched kernel services.\n"));
565 3 : NaClLog(3,
566 : ("NaClAppLaunchServiceThreads: no reverse channel;"
567 : " NOT launching manifest proxy.\n"));
568 3 : nap->kern_service = kern_service;
569 3 : kern_service = NULL;
570 :
571 3 : rv = 1;
572 3 : goto done;
573 : }
574 :
575 : /*
576 : * Allocate/construct the manifest proxy without grabbing global
577 : * locks.
578 : */
579 0 : NaClLog(3, "NaClAppLaunchServiceThreads: launching manifest proxy\n");
580 :
581 : /*
582 : * ReverseClientSetup RPC should be done via the command channel
583 : * prior to the load_module / start_module RPCs, and
584 : * occurs after that, so checking
585 : * nap->reverse_client suffices for determining whether the proxy is
586 : * exporting reverse services.
587 : */
588 0 : manifest_proxy = (struct NaClManifestProxy *) malloc(sizeof *manifest_proxy);
589 0 : if (NULL == manifest_proxy) {
590 0 : NaClLog(LOG_ERROR, "No memory for manifest proxy\n");
591 0 : NaClDescUnref(kern_service->base.bound_and_cap[1]);
592 0 : goto done;
593 : }
594 0 : if (!NaClManifestProxyCtor(manifest_proxy,
595 : NaClAddrSpSquattingThreadIfFactoryFunction,
596 : (void *) nap,
597 : nap)) {
598 0 : NaClLog(LOG_ERROR, "ManifestProxyCtor failed\n");
599 : /* do not leave a non-NULL pointer to a not-fully constructed object */
600 0 : free(manifest_proxy);
601 0 : manifest_proxy = NULL;
602 0 : NaClDescUnref(kern_service->base.bound_and_cap[1]);
603 0 : goto done;
604 : }
605 :
606 : /*
607 : * NaClSimpleServiceStartServiceThread requires the nap->mu lock.
608 : */
609 0 : if (!NaClSimpleServiceStartServiceThread((struct NaClSimpleService *)
610 : manifest_proxy)) {
611 0 : NaClLog(LOG_ERROR, "ManifestProxy start service failed\n");
612 0 : NaClDescUnref(kern_service->base.bound_and_cap[1]);
613 0 : goto done;
614 : }
615 :
616 0 : NaClXMutexLock(&nap->mu);
617 0 : CHECK(NULL == nap->manifest_proxy);
618 0 : CHECK(NULL == nap->kern_service);
619 :
620 0 : nap->manifest_proxy = manifest_proxy;
621 0 : manifest_proxy = NULL;
622 0 : nap->kern_service = kern_service;
623 0 : kern_service = NULL;
624 0 : NaClXMutexUnlock(&nap->mu);
625 0 : rv = 1;
626 :
627 3 : done:
628 3 : NaClXMutexLock(&nap->mu);
629 3 : if (NULL != nap->manifest_proxy) {
630 0 : NaClLog(3,
631 : ("NaClAppLaunchServiceThreads: adding manifest proxy to"
632 : " name service\n"));
633 0 : (*NACL_VTBL(NaClNameService, nap->name_service)->
634 : CreateDescEntry)(nap->name_service,
635 : "ManifestNameService", NACL_ABI_O_RDWR,
636 : NaClDescRef(nap->manifest_proxy->base.bound_and_cap[1]));
637 : }
638 3 : if (NULL != nap->kern_service) {
639 3 : NaClLog(3,
640 : ("NaClAppLaunchServiceThreads: adding kernel service to"
641 : " name service\n"));
642 3 : (*NACL_VTBL(NaClNameService, nap->name_service)->
643 : CreateDescEntry)(nap->name_service,
644 : "KernelService", NACL_ABI_O_RDWR,
645 : NaClDescRef(nap->kern_service->base.bound_and_cap[1]));
646 : }
647 :
648 3 : NaClXMutexUnlock(&nap->mu);
649 :
650 : /*
651 : * Single exit path.
652 : *
653 : * Error cleanup invariant. No service thread should be running
654 : * (modulo asynchronous shutdown). Automatic variables refer to
655 : * fully constructed objects if non-NULL, and when ownership is
656 : * transferred to the NaClApp object the corresponding automatic
657 : * variable is set to NULL.
658 : */
659 3 : NaClRefCountSafeUnref((struct NaClRefCount *) manifest_proxy);
660 3 : NaClRefCountSafeUnref((struct NaClRefCount *) kern_service);
661 3 : return rv;
662 : }
663 :
664 3 : int NaClReportExitStatus(struct NaClApp *nap, int exit_status) {
665 : int rv;
666 : NaClSrpcError rpc_result;
667 :
668 3 : NaClXMutexLock(&nap->mu);
669 :
670 3 : if (NACL_REVERSE_CHANNEL_INITIALIZED !=
671 : nap->reverse_channel_initialization_state) {
672 3 : rv = 0;
673 : } else {
674 0 : rpc_result = NaClSrpcInvokeBySignature(&nap->reverse_channel,
675 : NACL_REVERSE_CONTROL_REPORT_STATUS,
676 : exit_status & 0xff);
677 0 : rv = NACL_SRPC_RESULT_OK == rpc_result;
678 : /*
679 : * Due to cross-repository checkins, the Cr-side might not yet
680 : * implement this RPC. We return whether shutdown was reported.
681 : */
682 : }
683 3 : nap->exit_status = exit_status;
684 3 : nap->running = 0;
685 3 : NaClXCondVarSignal(&nap->cv);
686 :
687 3 : NaClXMutexUnlock(&nap->mu);
688 :
689 3 : return rv;
690 : }
691 :
692 : /*
693 : * preconditions:
694 : * * argc is the length of the argv array
695 : * * envv may be NULL (this happens on MacOS/Cocoa and in tests)
696 : * * if envv is non-NULL it is 'consistent', null terminated etc.
697 : */
698 : int NaClCreateMainThread(struct NaClApp *nap,
699 : int argc,
700 : char **argv,
701 3 : char const *const *envv) {
702 : /*
703 : * Compute size of string tables for argv and envv
704 : */
705 : int retval;
706 : int envc;
707 : size_t size;
708 : int auxv_entries;
709 : size_t ptr_tbl_size;
710 : int i;
711 : uint32_t *p;
712 : char *strp;
713 : size_t *argv_len;
714 : size_t *envv_len;
715 : struct NaClAppThread *natp;
716 : uintptr_t stack_ptr;
717 :
718 3 : retval = 0; /* fail */
719 3 : CHECK(argc >= 0);
720 3 : CHECK(NULL != argv || 0 == argc);
721 :
722 3 : envc = 0;
723 3 : if (NULL != envv) {
724 : char const *const *pp;
725 3 : for (pp = envv; NULL != *pp; ++pp) {
726 0 : ++envc;
727 : }
728 : }
729 3 : envv_len = 0;
730 3 : argv_len = malloc(argc * sizeof argv_len[0]);
731 3 : envv_len = malloc(envc * sizeof envv_len[0]);
732 3 : if (NULL == argv_len) {
733 0 : goto cleanup;
734 : }
735 3 : if (NULL == envv_len && 0 != envc) {
736 0 : goto cleanup;
737 : }
738 :
739 3 : if (nap->enable_debug_stub) {
740 : /*
741 : * Enable the debug stub.
742 : */
743 0 : if (!NaClDebugInit(nap,
744 : argc, (char const * const *) argv,
745 : envc, (char const * const *) envv)) {
746 0 : goto cleanup;
747 : }
748 : }
749 :
750 3 : size = 0;
751 :
752 : /*
753 : * The following two loops cannot overflow. The reason for this is
754 : * that they are counting the number of bytes used to hold the
755 : * NUL-terminated strings that comprise the argv and envv tables.
756 : * If the entire address space consisted of just those strings, then
757 : * the size variable would overflow; however, since there's the code
758 : * space required to hold the code below (and we are not targetting
759 : * Harvard architecture machines), at least one page holds code, not
760 : * data. We are assuming that the caller is non-adversarial and the
761 : * code does not look like string data....
762 : */
763 9 : for (i = 0; i < argc; ++i) {
764 6 : argv_len[i] = strlen(argv[i]) + 1;
765 6 : size += argv_len[i];
766 : }
767 3 : for (i = 0; i < envc; ++i) {
768 0 : envv_len[i] = strlen(envv[i]) + 1;
769 0 : size += envv_len[i];
770 : }
771 :
772 : /*
773 : * NaCl modules are ILP32, so the argv, envv pointers, as well as
774 : * the terminating NULL pointers at the end of the argv/envv tables,
775 : * are 32-bit values. We also have the auxv to take into account.
776 : *
777 : * The argv and envv pointer tables came from trusted code and is
778 : * part of memory. Thus, by the same argument above, adding in
779 : * "ptr_tbl_size" cannot possibly overflow the "size" variable since
780 : * it is a size_t object. However, the extra pointers for auxv and
781 : * the space for argv could cause an overflow. The fact that we
782 : * used stack to get here etc means that ptr_tbl_size could not have
783 : * overflowed.
784 : *
785 : * NB: the underlying OS would have limited the amount of space used
786 : * for argv and envv -- on linux, it is ARG_MAX, or 128KB -- and
787 : * hence the overflow check is for obvious auditability rather than
788 : * for correctness.
789 : */
790 3 : auxv_entries = 1;
791 3 : if (0 != nap->user_entry_pt) {
792 0 : auxv_entries++;
793 : }
794 3 : ptr_tbl_size = (((NACL_STACK_GETS_ARG ? 1 : 0) +
795 : (3 + argc + 1 + envc + 1 + auxv_entries * 2)) *
796 : sizeof(uint32_t));
797 :
798 3 : if (SIZE_T_MAX - size < ptr_tbl_size) {
799 0 : NaClLog(LOG_WARNING,
800 : "NaClCreateMainThread: ptr_tbl_size cause size of"
801 : " argv / environment copy to overflow!?!\n");
802 0 : retval = 0;
803 0 : goto cleanup;
804 : }
805 3 : size += ptr_tbl_size;
806 :
807 3 : size = (size + NACL_STACK_ALIGN_MASK) & ~NACL_STACK_ALIGN_MASK;
808 :
809 3 : if (size > nap->stack_size) {
810 0 : retval = 0;
811 0 : goto cleanup;
812 : }
813 :
814 : /* write strings and char * arrays to stack */
815 3 : stack_ptr = (nap->mem_start + ((uintptr_t) 1U << nap->addr_bits) - size);
816 :
817 3 : NaClLog(2, "setting stack to : %016"NACL_PRIxPTR"\n", stack_ptr);
818 :
819 3 : VCHECK(0 == (stack_ptr & NACL_STACK_ALIGN_MASK),
820 : ("stack_ptr not aligned: %016"NACL_PRIxPTR"\n", stack_ptr));
821 :
822 3 : p = (uint32_t *) stack_ptr;
823 3 : strp = (char *) stack_ptr + ptr_tbl_size;
824 :
825 : /*
826 : * For x86-32, we push an initial argument that is the address of
827 : * the main argument block. For other machines, this is passed
828 : * in a register and that's set in NaClStartThreadInApp.
829 : */
830 : if (NACL_STACK_GETS_ARG) {
831 3 : uint32_t *argloc = p++;
832 3 : *argloc = (uint32_t) NaClSysToUser(nap, (uintptr_t) p);
833 : }
834 :
835 3 : *p++ = 0; /* Cleanup function pointer, always NULL. */
836 3 : *p++ = envc;
837 3 : *p++ = argc;
838 :
839 9 : for (i = 0; i < argc; ++i) {
840 6 : *p++ = (uint32_t) NaClSysToUser(nap, (uintptr_t) strp);
841 6 : NaClLog(2, "copying arg %d %p -> %p\n",
842 : i, argv[i], strp);
843 6 : strcpy(strp, argv[i]);
844 6 : strp += argv_len[i];
845 : }
846 3 : *p++ = 0; /* argv[argc] is NULL. */
847 :
848 3 : for (i = 0; i < envc; ++i) {
849 0 : *p++ = (uint32_t) NaClSysToUser(nap, (uintptr_t) strp);
850 0 : NaClLog(2, "copying env %d %p -> %p\n",
851 : i, envv[i], strp);
852 0 : strcpy(strp, envv[i]);
853 0 : strp += envv_len[i];
854 : }
855 3 : *p++ = 0; /* envp[envc] is NULL. */
856 :
857 : /* Push an auxv */
858 3 : if (0 != nap->user_entry_pt) {
859 0 : *p++ = AT_ENTRY;
860 0 : *p++ = (uint32_t) nap->user_entry_pt;
861 : }
862 3 : *p++ = AT_NULL;
863 3 : *p++ = 0;
864 :
865 3 : CHECK((char *) p == (char *) stack_ptr + ptr_tbl_size);
866 :
867 : /* now actually spawn the thread */
868 3 : natp = malloc(sizeof *natp);
869 3 : if (!natp) {
870 0 : goto cleanup;
871 : }
872 :
873 : /* NaClApp initialization is completed, call OOP debugger hook. */
874 3 : NaClOopDebuggerAppCreateHook(nap);
875 :
876 3 : NaClXMutexLock(&nap->mu);
877 3 : nap->running = 1;
878 3 : NaClXMutexUnlock(&nap->mu);
879 :
880 3 : NaClVmHoleWaitToStartThread(nap);
881 :
882 : /*
883 : * For x86, we adjust the stack pointer down to push a dummy return
884 : * address. This happens after the stack pointer alignment.
885 : * We avoid the otherwise harmless call for the zero case because
886 : * _FORTIFY_SOURCE memset can warn about zero-length calls.
887 : */
888 : if (NACL_STACK_PAD_BELOW_ALIGN != 0) {
889 3 : stack_ptr -= NACL_STACK_PAD_BELOW_ALIGN;
890 3 : memset((void *) stack_ptr, 0, NACL_STACK_PAD_BELOW_ALIGN);
891 : }
892 :
893 3 : NaClLog(2, "system stack ptr : %016"NACL_PRIxPTR"\n", stack_ptr);
894 3 : NaClLog(2, " user stack ptr : %016"NACL_PRIxPTR"\n",
895 : NaClSysToUserStackAddr(nap, stack_ptr));
896 :
897 : /* e_entry is user addr */
898 3 : if (!NaClAppThreadAllocSegCtor(natp,
899 : nap,
900 : nap->initial_entry_pt,
901 : NaClSysToUserStackAddr(nap, stack_ptr),
902 : NaClUserToSys(nap, nap->break_addr),
903 : 0)) {
904 0 : retval = 0;
905 0 : goto cleanup;
906 : }
907 :
908 3 : retval = 1;
909 3 : cleanup:
910 3 : free(argv_len);
911 3 : free(envv_len);
912 :
913 3 : return retval;
914 : }
915 :
916 3 : int NaClWaitForMainThreadToExit(struct NaClApp *nap) {
917 3 : NaClLog(3, "NaClWaitForMainThreadToExit: taking NaClApp lock\n");
918 3 : NaClXMutexLock(&nap->mu);
919 3 : NaClLog(3, " waiting for exit status\n");
920 16 : while (nap->running) {
921 10 : NaClXCondVarWait(&nap->cv, &nap->mu);
922 10 : NaClLog(3, " wakeup, nap->running %d, nap->exit_status %d\n",
923 : nap->running, nap->exit_status);
924 : }
925 3 : NaClXMutexUnlock(&nap->mu);
926 : /*
927 : * Some thread invoked the exit (exit_group) syscall.
928 : */
929 :
930 3 : if (NULL != nap->debug_stub_callbacks) {
931 0 : nap->debug_stub_callbacks->process_exit_hook(nap->exit_status);
932 : }
933 :
934 3 : NaClOopDebuggerAppExitHook(nap->exit_status);
935 :
936 3 : return (nap->exit_status);
937 : }
938 :
939 : /*
940 : * stack_ptr is from syscall, so a 32-bit address.
941 : */
942 : int32_t NaClCreateAdditionalThread(struct NaClApp *nap,
943 : uintptr_t prog_ctr,
944 : uintptr_t sys_stack_ptr,
945 : uintptr_t sys_tls,
946 0 : uint32_t user_tls2) {
947 : struct NaClAppThread *natp;
948 : uintptr_t stack_ptr;
949 :
950 0 : natp = malloc(sizeof *natp);
951 0 : if (NULL == natp) {
952 0 : NaClLog(LOG_WARNING,
953 : ("NaClCreateAdditionalThread: no memory for new thread context."
954 : " Returning EAGAIN per POSIX specs.\n"));
955 0 : return -NACL_ABI_EAGAIN;
956 : }
957 :
958 0 : stack_ptr = NaClSysToUserStackAddr(nap, sys_stack_ptr);
959 :
960 0 : if (0 != ((stack_ptr + sizeof(nacl_reg_t)) & NACL_STACK_ALIGN_MASK)) {
961 0 : NaClLog(3,
962 : ("NaClCreateAdditionalThread: user thread library provided "
963 : "an unaligned user stack pointer: 0x%"NACL_PRIxPTR"\n"),
964 : stack_ptr);
965 : }
966 :
967 0 : if (!NaClAppThreadAllocSegCtor(natp,
968 : nap,
969 : prog_ctr,
970 : stack_ptr,
971 : sys_tls,
972 : user_tls2)) {
973 0 : NaClLog(LOG_WARNING,
974 : ("NaClCreateAdditionalThread: could not allocate thread index."
975 : " Returning EAGAIN per POSIX specs.\n"));
976 0 : free(natp);
977 0 : return -NACL_ABI_EAGAIN;
978 : }
979 0 : return 0;
980 : }
|