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 :
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/perf_counter/nacl_perf_counter.h"
29 :
30 : #include "native_client/src/trusted/reverse_service/reverse_control_rpc.h"
31 :
32 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
33 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
34 :
35 : #include "native_client/src/trusted/service_runtime/arch/sel_ldr_arch.h"
36 : #include "native_client/src/trusted/service_runtime/elf_util.h"
37 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
38 : #include "native_client/src/trusted/service_runtime/nacl_kernel_service.h"
39 : #include "native_client/src/trusted/service_runtime/nacl_runtime_host_interface.h"
40 : #include "native_client/src/trusted/service_runtime/nacl_signal.h"
41 : #include "native_client/src/trusted/service_runtime/nacl_switch_to_app.h"
42 : #include "native_client/src/trusted/service_runtime/nacl_syscall_common.h"
43 : #include "native_client/src/trusted/service_runtime/nacl_text.h"
44 : #include "native_client/src/trusted/service_runtime/sel_memory.h"
45 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
46 : #include "native_client/src/trusted/service_runtime/sel_ldr_thread_interface.h"
47 : #include "native_client/src/trusted/service_runtime/sel_util.h"
48 : #include "native_client/src/trusted/service_runtime/sel_addrspace.h"
49 :
50 : #if !defined(SIZE_T_MAX)
51 : # define SIZE_T_MAX (~(size_t) 0)
52 : #endif
53 :
54 :
55 : /*
56 : * Fill from static_text_end to end of that page with halt
57 : * instruction, which is at least NACL_HALT_LEN in size when no
58 : * dynamic text is present. Does not touch dynamic text region, which
59 : * should be pre-filled with HLTs.
60 : *
61 : * By adding NACL_HALT_SLED_SIZE, we ensure that the code region ends
62 : * with HLTs, just in case the CPU has a bug in which it fails to
63 : * check for running off the end of the x86 code segment.
64 : */
65 268 : void NaClFillEndOfTextRegion(struct NaClApp *nap) {
66 268 : size_t page_pad;
67 :
68 : /*
69 : * NOTE: make sure we are not silently overwriting data. It is the
70 : * toolchain's responsibility to ensure that a NACL_HALT_SLED_SIZE
71 : * gap exists.
72 : */
73 520 : if (0 != nap->data_start &&
74 : nap->static_text_end + NACL_HALT_SLED_SIZE >
75 252 : NaClTruncAllocPage(nap->data_start)) {
76 0 : NaClLog(LOG_FATAL, "Missing gap between text and data for halt_sled\n");
77 0 : }
78 532 : if (0 != nap->rodata_start &&
79 : nap->static_text_end + NACL_HALT_SLED_SIZE > nap->rodata_start) {
80 2 : NaClLog(LOG_FATAL, "Missing gap between text and rodata for halt_sled\n");
81 2 : }
82 :
83 266 : if (NULL == nap->text_shm) {
84 : /*
85 : * No dynamic text exists. Space for NACL_HALT_SLED_SIZE must
86 : * exist.
87 : */
88 6 : page_pad = (NaClRoundAllocPage(nap->static_text_end + NACL_HALT_SLED_SIZE)
89 : - nap->static_text_end);
90 18 : CHECK(page_pad >= NACL_HALT_SLED_SIZE);
91 18 : CHECK(page_pad < NACL_MAP_PAGESIZE + NACL_HALT_SLED_SIZE);
92 6 : } else {
93 : /*
94 : * Dynamic text exists; the halt sled resides in the dynamic text
95 : * region, so all we need to do here is to round out the last
96 : * static text page with HLT instructions. It doesn't matter if
97 : * the size of this region is smaller than NACL_HALT_SLED_SIZE --
98 : * this is just to fully initialize the page, rather than (later)
99 : * decoding/validating zero-filled memory as instructions.
100 : */
101 260 : page_pad = NaClRoundAllocPage(nap->static_text_end) - nap->static_text_end;
102 : }
103 :
104 266 : NaClLog(4,
105 : "Filling with halts: %08"NACL_PRIxPTR", %08"NACL_PRIxS" bytes\n",
106 : nap->mem_start + nap->static_text_end,
107 : page_pad);
108 :
109 266 : NaClFillMemoryRegionWithHalt((void *)(nap->mem_start + nap->static_text_end),
110 : page_pad);
111 :
112 266 : nap->static_text_end += page_pad;
113 266 : }
114 :
115 : /*
116 : * Basic address space layout sanity check.
117 : */
118 271 : NaClErrorCode NaClCheckAddressSpaceLayoutSanity(struct NaClApp *nap,
119 271 : uintptr_t rodata_end,
120 271 : uintptr_t data_end,
121 271 : uintptr_t max_vaddr) {
122 271 : if (0 != nap->data_start) {
123 255 : if (data_end != max_vaddr) {
124 1 : NaClLog(LOG_INFO, "data segment is not last\n");
125 1 : return LOAD_DATA_NOT_LAST_SEGMENT;
126 : }
127 270 : } else if (0 != nap->rodata_start) {
128 12 : if (NaClRoundAllocPage(rodata_end) != max_vaddr) {
129 : /*
130 : * This should be unreachable, but we include it just for
131 : * completeness.
132 : *
133 : * Here is why it is unreachable:
134 : *
135 : * NaClPhdrChecks checks the test segment starting address. The
136 : * only allowed loaded segments are text, data, and rodata.
137 : * Thus unless the rodata is in the trampoline region, it must
138 : * be after the text. And NaClElfImageValidateProgramHeaders
139 : * ensures that all segments start after the trampoline region.
140 : */
141 0 : NaClLog(LOG_INFO, "no data segment, but rodata segment is not last\n");
142 0 : return LOAD_NO_DATA_BUT_RODATA_NOT_LAST_SEGMENT;
143 : }
144 12 : }
145 536 : if (0 != nap->rodata_start && 0 != nap->data_start) {
146 254 : if (rodata_end > NaClTruncAllocPage(nap->data_start)) {
147 1 : NaClLog(LOG_INFO, "rodata_overlaps data.\n");
148 1 : return LOAD_RODATA_OVERLAPS_DATA;
149 : }
150 253 : }
151 269 : if (0 != nap->rodata_start) {
152 265 : if (NaClRoundAllocPage(NaClEndOfStaticText(nap)) > nap->rodata_start) {
153 0 : return LOAD_TEXT_OVERLAPS_RODATA;
154 : }
155 269 : } else if (0 != nap->data_start) {
156 0 : if (NaClRoundAllocPage(NaClEndOfStaticText(nap)) >
157 0 : NaClTruncAllocPage(nap->data_start)) {
158 0 : return LOAD_TEXT_OVERLAPS_DATA;
159 : }
160 0 : }
161 269 : if (0 != nap->rodata_start &&
162 265 : 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 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
167 : /*
168 : * This check is necessary to make MIPS sandbox secure, as there is no NX page
169 : * protection support on MIPS.
170 : */
171 : if (nap->rodata_start < NACL_DATA_SEGMENT_START) {
172 : NaClLog(LOG_INFO,
173 : "rodata_start is below NACL_DATA_SEGMENT_START (0x%X) address\n",
174 : NACL_DATA_SEGMENT_START);
175 : return LOAD_SEGMENT_BAD_LOC;
176 : }
177 : #endif
178 269 : return LOAD_OK;
179 271 : }
180 :
181 536 : void NaClLogAddressSpaceLayout(struct NaClApp *nap) {
182 536 : NaClLog(2, "NaClApp addr space layout:\n");
183 536 : NaClLog(2, "nap->static_text_end = 0x%016"NACL_PRIxPTR"\n",
184 : nap->static_text_end);
185 536 : NaClLog(2, "nap->dynamic_text_start = 0x%016"NACL_PRIxPTR"\n",
186 : nap->dynamic_text_start);
187 536 : NaClLog(2, "nap->dynamic_text_end = 0x%016"NACL_PRIxPTR"\n",
188 : nap->dynamic_text_end);
189 536 : NaClLog(2, "nap->rodata_start = 0x%016"NACL_PRIxPTR"\n",
190 : nap->rodata_start);
191 536 : NaClLog(2, "nap->data_start = 0x%016"NACL_PRIxPTR"\n",
192 : nap->data_start);
193 536 : NaClLog(2, "nap->data_end = 0x%016"NACL_PRIxPTR"\n",
194 : nap->data_end);
195 536 : NaClLog(2, "nap->break_addr = 0x%016"NACL_PRIxPTR"\n",
196 : nap->break_addr);
197 536 : NaClLog(2, "nap->initial_entry_pt = 0x%016"NACL_PRIxPTR"\n",
198 : nap->initial_entry_pt);
199 536 : NaClLog(2, "nap->user_entry_pt = 0x%016"NACL_PRIxPTR"\n",
200 : nap->user_entry_pt);
201 536 : NaClLog(2, "nap->bundle_size = 0x%x\n", nap->bundle_size);
202 536 : }
203 :
204 :
205 273 : NaClErrorCode NaClAppLoadFileAslr(struct NaClDesc *ndp,
206 273 : struct NaClApp *nap,
207 273 : enum NaClAslrMode aslr_mode) {
208 273 : NaClErrorCode ret = LOAD_INTERNAL;
209 273 : NaClErrorCode subret = LOAD_INTERNAL;
210 273 : uintptr_t rodata_end;
211 273 : uintptr_t data_end;
212 273 : uintptr_t max_vaddr;
213 273 : struct NaClElfImage *image = NULL;
214 273 : struct NaClPerfCounter time_load_file;
215 273 : struct NaClElfImageInfo info;
216 :
217 273 : NaClPerfCounterCtor(&time_load_file, "NaClAppLoadFile");
218 :
219 : /* NACL_MAX_ADDR_BITS < 32 */
220 273 : if (nap->addr_bits > NACL_MAX_ADDR_BITS) {
221 0 : ret = LOAD_ADDR_SPACE_TOO_BIG;
222 0 : goto done;
223 : }
224 :
225 273 : nap->stack_size = NaClRoundAllocPage(nap->stack_size);
226 :
227 : /* temporay object will be deleted at end of function */
228 273 : image = NaClElfImageNew(ndp, &subret);
229 544 : if (NULL == image || LOAD_OK != subret) {
230 2 : ret = subret;
231 2 : goto done;
232 : }
233 :
234 271 : subret = NaClElfImageValidateProgramHeaders(image,
235 : nap->addr_bits,
236 : &info);
237 271 : if (LOAD_OK != subret) {
238 0 : ret = subret;
239 0 : goto done;
240 : }
241 :
242 271 : if (nap->initial_nexe_max_code_bytes != 0) {
243 0 : size_t code_segment_size = info.static_text_end - NACL_TRAMPOLINE_END;
244 0 : if (code_segment_size > nap->initial_nexe_max_code_bytes) {
245 0 : NaClLog(LOG_ERROR, "NaClAppLoadFileAslr: "
246 : "Code segment size (%"NACL_PRIuS" bytes) exceeds limit (%"
247 : NACL_PRId32" bytes)\n",
248 : code_segment_size, nap->initial_nexe_max_code_bytes);
249 0 : ret = LOAD_CODE_SEGMENT_TOO_LARGE;
250 0 : goto done;
251 : }
252 0 : }
253 :
254 271 : nap->static_text_end = info.static_text_end;
255 271 : nap->rodata_start = info.rodata_start;
256 271 : rodata_end = info.rodata_end;
257 271 : nap->data_start = info.data_start;
258 271 : data_end = info.data_end;
259 271 : max_vaddr = info.max_vaddr;
260 :
261 271 : if (0 == nap->data_start) {
262 16 : if (0 == nap->rodata_start) {
263 4 : if (NaClRoundAllocPage(max_vaddr) - max_vaddr < NACL_HALT_SLED_SIZE) {
264 : /*
265 : * if no rodata and no data, we make sure that there is space for
266 : * the halt sled.
267 : */
268 4 : max_vaddr += NACL_MAP_PAGESIZE;
269 4 : }
270 4 : } else {
271 : /*
272 : * no data, but there is rodata. this means max_vaddr is just
273 : * where rodata ends. this might not be at an allocation
274 : * boundary, and in this the page would not be writable. round
275 : * max_vaddr up to the next allocation boundary so that bss will
276 : * be at the next writable region.
277 : */
278 : ;
279 : }
280 16 : max_vaddr = NaClRoundAllocPage(max_vaddr);
281 16 : }
282 : /*
283 : * max_vaddr -- the break or the boundary between data (initialized
284 : * and bss) and the address space hole -- does not have to be at a
285 : * page boundary.
286 : *
287 : * Memory allocation will use NaClRoundPage(nap->break_addr), but
288 : * the system notion of break is always an exact address. Even
289 : * though we must allocate and make accessible multiples of pages,
290 : * the linux-style brk system call (which returns current break on
291 : * failure) permits a non-aligned address as argument.
292 : */
293 271 : nap->break_addr = max_vaddr;
294 271 : nap->data_end = max_vaddr;
295 :
296 271 : NaClLog(4, "Values from NaClElfImageValidateProgramHeaders:\n");
297 271 : NaClLog(4, "rodata_start = 0x%08"NACL_PRIxPTR"\n", nap->rodata_start);
298 271 : NaClLog(4, "rodata_end = 0x%08"NACL_PRIxPTR"\n", rodata_end);
299 271 : NaClLog(4, "data_start = 0x%08"NACL_PRIxPTR"\n", nap->data_start);
300 271 : NaClLog(4, "data_end = 0x%08"NACL_PRIxPTR"\n", data_end);
301 271 : NaClLog(4, "max_vaddr = 0x%08"NACL_PRIxPTR"\n", max_vaddr);
302 :
303 : /* We now support only one bundle size. */
304 271 : nap->bundle_size = NACL_INSTR_BLOCK_SIZE;
305 :
306 271 : nap->initial_entry_pt = NaClElfImageGetEntryPoint(image);
307 271 : NaClLogAddressSpaceLayout(nap);
308 :
309 271 : if (!NaClAddrIsValidEntryPt(nap, nap->initial_entry_pt)) {
310 0 : ret = LOAD_BAD_ENTRY;
311 0 : goto done;
312 : }
313 :
314 271 : subret = NaClCheckAddressSpaceLayoutSanity(nap, rodata_end, data_end,
315 : max_vaddr);
316 271 : if (LOAD_OK != subret) {
317 2 : ret = subret;
318 2 : goto done;
319 : }
320 :
321 269 : NaClLog(2, "Allocating address space\n");
322 269 : NaClPerfCounterMark(&time_load_file, "PreAllocAddrSpace");
323 269 : NaClPerfCounterIntervalLast(&time_load_file);
324 269 : subret = NaClAllocAddrSpaceAslr(nap, aslr_mode);
325 269 : NaClPerfCounterMark(&time_load_file,
326 : NACL_PERF_IMPORTANT_PREFIX "AllocAddrSpace");
327 269 : NaClPerfCounterIntervalLast(&time_load_file);
328 269 : if (LOAD_OK != subret) {
329 1 : ret = subret;
330 1 : goto done;
331 : }
332 :
333 : /*
334 : * Make sure the static image pages are marked writable before we try
335 : * to write them.
336 : */
337 268 : NaClLog(2, "Loading into memory\n");
338 536 : ret = NaClMprotect((void *) (nap->mem_start + NACL_TRAMPOLINE_START),
339 268 : NaClRoundAllocPage(nap->data_end) - NACL_TRAMPOLINE_START,
340 : PROT_READ | PROT_WRITE);
341 268 : if (0 != ret) {
342 0 : NaClLog(LOG_FATAL,
343 : "NaClAppLoadFile: Failed to make image pages writable. "
344 : "Error code 0x%x\n",
345 : ret);
346 0 : }
347 268 : subret = NaClElfImageLoad(image, ndp, nap);
348 268 : NaClPerfCounterMark(&time_load_file,
349 : NACL_PERF_IMPORTANT_PREFIX "NaClElfImageLoad");
350 268 : NaClPerfCounterIntervalLast(&time_load_file);
351 268 : if (LOAD_OK != subret) {
352 0 : ret = subret;
353 0 : goto done;
354 : }
355 :
356 : /*
357 : * NB: mem_map object has been initialized, but is empty.
358 : * NaClMakeDynamicTextShared does not touch it.
359 : *
360 : * NaClMakeDynamicTextShared also fills the dynamic memory region
361 : * with the architecture-specific halt instruction. If/when we use
362 : * memory mapping to save paging space for the dynamic region and
363 : * lazily halt fill the memory as the pages become
364 : * readable/executable, we must make sure that the *last*
365 : * NACL_MAP_PAGESIZE chunk is nonetheless mapped and written with
366 : * halts.
367 : */
368 268 : NaClLog(2,
369 : ("Replacing gap between static text and"
370 : " (ro)data with shareable memory\n"));
371 268 : subret = NaClMakeDynamicTextShared(nap);
372 268 : NaClPerfCounterMark(&time_load_file,
373 : NACL_PERF_IMPORTANT_PREFIX "MakeDynText");
374 268 : NaClPerfCounterIntervalLast(&time_load_file);
375 268 : if (LOAD_OK != subret) {
376 0 : ret = subret;
377 0 : goto done;
378 : }
379 :
380 : /*
381 : * NaClFillEndOfTextRegion will fill with halt instructions the
382 : * padding space after the static text region.
383 : *
384 : * Shm-backed dynamic text space was filled with halt instructions
385 : * in NaClMakeDynamicTextShared. This extends to the rodata. For
386 : * non-shm-backed text space, this extend to the next page (and not
387 : * allocation page). static_text_end is updated to include the
388 : * padding.
389 : */
390 266 : NaClFillEndOfTextRegion(nap);
391 :
392 266 : if (nap->main_exe_prevalidated) {
393 3 : NaClLog(2, "Main executable segment hit validation cache and mapped in,"
394 : " skipping validation.\n");
395 3 : subret = LOAD_OK;
396 3 : } else {
397 263 : NaClLog(2, "Validating image\n");
398 263 : subret = NaClValidateImage(nap);
399 : }
400 266 : NaClPerfCounterMark(&time_load_file,
401 : NACL_PERF_IMPORTANT_PREFIX "ValidateImg");
402 266 : NaClPerfCounterIntervalLast(&time_load_file);
403 266 : if (LOAD_OK != subret) {
404 1 : ret = subret;
405 1 : goto done;
406 : }
407 :
408 265 : NaClLog(2, "Initializing arch switcher\n");
409 265 : NaClInitSwitchToApp(nap);
410 :
411 265 : NaClLog(2, "Installing trampoline\n");
412 265 : NaClLoadTrampoline(nap, aslr_mode);
413 :
414 265 : NaClLog(2, "Installing springboard\n");
415 265 : NaClLoadSpringboard(nap);
416 :
417 : /*
418 : * NaClMemoryProtection also initializes the mem_map w/ information
419 : * about the memory pages and their current protection value.
420 : *
421 : * The contents of the dynamic text region will get remapped as
422 : * non-writable.
423 : */
424 265 : NaClLog(2, "Applying memory protection\n");
425 265 : subret = NaClMemoryProtection(nap);
426 265 : if (LOAD_OK != subret) {
427 0 : ret = subret;
428 0 : goto done;
429 : }
430 :
431 265 : NaClLog(2, "NaClAppLoadFile done; ");
432 265 : NaClLogAddressSpaceLayout(nap);
433 265 : ret = LOAD_OK;
434 : done:
435 271 : NaClElfImageDelete(image);
436 :
437 271 : NaClPerfCounterMark(&time_load_file, "EndLoadFile");
438 271 : NaClPerfCounterIntervalTotal(&time_load_file);
439 271 : return ret;
440 : }
441 :
442 271 : NaClErrorCode NaClAppLoadFile(struct NaClDesc *ndp,
443 271 : struct NaClApp *nap) {
444 271 : return NaClAppLoadFileAslr(ndp, nap, NACL_ENABLE_ASLR);
445 : }
446 :
447 : NaClErrorCode NaClAppLoadFileDynamically(
448 5 : struct NaClApp *nap,
449 5 : struct NaClDesc *ndp,
450 5 : struct NaClValidationMetadata *metadata) {
451 5 : struct NaClElfImage *image = NULL;
452 5 : NaClErrorCode ret = LOAD_INTERNAL;
453 :
454 5 : image = NaClElfImageNew(ndp, &ret);
455 10 : if (NULL == image || LOAD_OK != ret) {
456 0 : goto done;
457 : }
458 5 : ret = NaClElfImageLoadDynamically(image, nap, ndp, metadata);
459 5 : if (LOAD_OK != ret) {
460 0 : goto done;
461 : }
462 5 : nap->user_entry_pt = nap->initial_entry_pt;
463 5 : nap->initial_entry_pt = NaClElfImageGetEntryPoint(image);
464 :
465 : done:
466 5 : NaClElfImageDelete(image);
467 5 : return ret;
468 : }
469 :
470 271 : int NaClAddrIsValidEntryPt(struct NaClApp *nap,
471 271 : uintptr_t addr) {
472 271 : if (0 != (addr & (nap->bundle_size - 1))) {
473 0 : return 0;
474 : }
475 :
476 271 : return addr < nap->static_text_end;
477 271 : }
478 :
479 255 : int NaClAppLaunchServiceThreads(struct NaClApp *nap) {
480 255 : struct NaClKernelService *kernel_service = NULL;
481 255 : int rv = 0;
482 :
483 255 : NaClLog(4, "NaClAppLaunchServiceThreads: Entered, nap 0x%"NACL_PRIxPTR"\n",
484 : (uintptr_t) nap);
485 :
486 255 : NaClNameServiceLaunch(nap->name_service);
487 :
488 255 : if (LOAD_OK != NaClWaitForStartModuleCommand(nap)) {
489 0 : return rv;
490 : }
491 :
492 255 : NaClXMutexLock(&nap->mu);
493 255 : if (NULL == nap->runtime_host_interface) {
494 252 : nap->runtime_host_interface = malloc(sizeof *nap->runtime_host_interface);
495 252 : if (NULL == nap->runtime_host_interface ||
496 252 : !NaClRuntimeHostInterfaceCtor_protected(nap->runtime_host_interface)) {
497 0 : NaClLog(LOG_ERROR, "NaClAppLaunchServiceThreads:"
498 : " Failed to initialise runtime host interface\n");
499 0 : goto done;
500 : }
501 252 : }
502 255 : NaClXMutexUnlock(&nap->mu);
503 :
504 255 : kernel_service = (struct NaClKernelService *) malloc(sizeof *kernel_service);
505 255 : if (NULL == kernel_service) {
506 0 : NaClLog(LOG_ERROR,
507 : "NaClAppLaunchServiceThreads: No memory for kern service\n");
508 0 : goto done;
509 : }
510 :
511 255 : if (!NaClKernelServiceCtor(kernel_service,
512 : NaClAddrSpSquattingThreadIfFactoryFunction,
513 : (void *) nap,
514 : nap->runtime_host_interface)) {
515 0 : NaClLog(LOG_ERROR,
516 : "NaClAppLaunchServiceThreads: KernServiceCtor failed\n");
517 0 : free(kernel_service);
518 0 : kernel_service = NULL;
519 0 : goto done;
520 : }
521 :
522 255 : if (!NaClSimpleServiceStartServiceThread((struct NaClSimpleService *)
523 : kernel_service)) {
524 0 : NaClLog(LOG_ERROR,
525 : "NaClAppLaunchServiceThreads: KernService start service failed\n");
526 0 : goto done;
527 : }
528 : /*
529 : * NB: StartServiceThread grabbed another reference to kernel_service,
530 : * used by the service thread. Closing the connection capability
531 : * should cause the service thread to shut down and in turn release
532 : * that reference.
533 : */
534 :
535 255 : NaClXMutexLock(&nap->mu);
536 765 : CHECK(NULL == nap->kernel_service);
537 :
538 255 : nap->kernel_service = kernel_service;
539 255 : kernel_service = NULL;
540 255 : NaClXMutexUnlock(&nap->mu);
541 255 : rv = 1;
542 :
543 : done:
544 255 : NaClXMutexLock(&nap->mu);
545 255 : if (NULL != nap->kernel_service) {
546 255 : NaClLog(3,
547 : ("NaClAppLaunchServiceThreads: adding kernel service to"
548 : " name service\n"));
549 510 : (*NACL_VTBL(NaClNameService, nap->name_service)->
550 : CreateDescEntry)(nap->name_service,
551 : "KernelService", NACL_ABI_O_RDWR,
552 255 : NaClDescRef(nap->kernel_service->base.bound_and_cap[1]));
553 255 : }
554 255 : NaClXMutexUnlock(&nap->mu);
555 :
556 : /*
557 : * Single exit path.
558 : *
559 : * Error cleanup invariant. No service thread should be running
560 : * (modulo asynchronous shutdown). Automatic variables refer to
561 : * fully constructed objects if non-NULL, and when ownership is
562 : * transferred to the NaClApp object the corresponding automatic
563 : * variable is set to NULL.
564 : */
565 255 : NaClRefCountSafeUnref((struct NaClRefCount *) kernel_service);
566 255 : return rv;
567 255 : }
568 :
569 243 : int NaClReportExitStatus(struct NaClApp *nap, int exit_status) {
570 243 : int rv = 0;
571 :
572 243 : NaClXMutexLock(&nap->mu);
573 : /*
574 : * If several threads are exiting/reporting signals at once, we should
575 : * let only one thread to pass through. This way we can use exit code
576 : * without synchronization once we know that running==0.
577 : */
578 243 : if (!nap->running) {
579 0 : NaClXMutexUnlock(&nap->mu);
580 0 : return 0;
581 : }
582 :
583 243 : if (NULL != nap->runtime_host_interface) {
584 : /* TODO(halyavin) update NaCl plugin to accept full exit_status value */
585 232 : if (NACL_ABI_WIFEXITED(exit_status)) {
586 232 : rv = (*NACL_VTBL(NaClRuntimeHostInterface, nap->runtime_host_interface)->
587 : ReportExitStatus)(nap->runtime_host_interface,
588 : NACL_ABI_WEXITSTATUS(exit_status));
589 232 : }
590 : /*
591 : * Due to cross-repository checkins, the Cr-side might not yet
592 : * implement this RPC. We return whether shutdown was reported.
593 : */
594 232 : }
595 243 : nap->exit_status = exit_status;
596 243 : nap->running = 0;
597 243 : NaClXCondVarSignal(&nap->cv);
598 :
599 243 : NaClXMutexUnlock(&nap->mu);
600 :
601 243 : return rv;
602 243 : }
603 :
604 270 : uintptr_t NaClGetInitialStackTop(struct NaClApp *nap) {
605 : /*
606 : * We keep the top of useful memory a page below the top of the
607 : * sandbox region so that compilers can do tricks like computing a
608 : * base register of sp + constant and then using a
609 : * register-minus-constant addressing mode, which comes up at least
610 : * on ARM where the compiler is trying to optimize given the limited
611 : * size of immediate offsets available. The maximum such negative
612 : * constant on ARM will be -4095, but we use page size (64k) for
613 : * good measure and do it on all machines just for uniformity.
614 : */
615 270 : return ((uintptr_t) 1U << nap->addr_bits) - NACL_MAP_PAGESIZE;
616 : }
617 :
618 : /*
619 : * preconditions:
620 : * * argc is the length of the argv array
621 : * * envv may be NULL (this happens on MacOS/Cocoa and in tests)
622 : * * if envv is non-NULL it is 'consistent', null terminated etc.
623 : */
624 270 : int NaClCreateMainThread(struct NaClApp *nap,
625 270 : int argc,
626 270 : char **argv,
627 270 : char const *const *envv) {
628 : /*
629 : * Compute size of string tables for argv and envv
630 : */
631 270 : int retval;
632 270 : int envc;
633 270 : size_t size;
634 270 : int auxv_entries;
635 270 : size_t ptr_tbl_size;
636 270 : int i;
637 270 : uint32_t *p;
638 270 : char *strp;
639 270 : size_t *argv_len;
640 270 : size_t *envv_len;
641 270 : uintptr_t stack_ptr;
642 :
643 270 : retval = 0; /* fail */
644 810 : CHECK(argc >= 0);
645 814 : CHECK(NULL != argv || 0 == argc);
646 :
647 270 : envc = 0;
648 270 : if (NULL != envv) {
649 255 : char const *const *pp;
650 538 : for (pp = envv; NULL != *pp; ++pp) {
651 14 : ++envc;
652 14 : }
653 255 : }
654 270 : envv_len = 0;
655 270 : argv_len = malloc(argc * sizeof argv_len[0]);
656 270 : envv_len = malloc(envc * sizeof envv_len[0]);
657 270 : if (NULL == argv_len) {
658 0 : goto cleanup;
659 : }
660 270 : if (NULL == envv_len && 0 != envc) {
661 0 : goto cleanup;
662 : }
663 :
664 270 : size = 0;
665 :
666 : /*
667 : * The following two loops cannot overflow. The reason for this is
668 : * that they are counting the number of bytes used to hold the
669 : * NUL-terminated strings that comprise the argv and envv tables.
670 : * If the entire address space consisted of just those strings, then
671 : * the size variable would overflow; however, since there's the code
672 : * space required to hold the code below (and we are not targetting
673 : * Harvard architecture machines), at least one page holds code, not
674 : * data. We are assuming that the caller is non-adversarial and the
675 : * code does not look like string data....
676 : */
677 1340 : for (i = 0; i < argc; ++i) {
678 400 : argv_len[i] = strlen(argv[i]) + 1;
679 400 : size += argv_len[i];
680 400 : }
681 568 : for (i = 0; i < envc; ++i) {
682 14 : envv_len[i] = strlen(envv[i]) + 1;
683 14 : size += envv_len[i];
684 14 : }
685 :
686 : /*
687 : * NaCl modules are ILP32, so the argv, envv pointers, as well as
688 : * the terminating NULL pointers at the end of the argv/envv tables,
689 : * are 32-bit values. We also have the auxv to take into account.
690 : *
691 : * The argv and envv pointer tables came from trusted code and is
692 : * part of memory. Thus, by the same argument above, adding in
693 : * "ptr_tbl_size" cannot possibly overflow the "size" variable since
694 : * it is a size_t object. However, the extra pointers for auxv and
695 : * the space for argv could cause an overflow. The fact that we
696 : * used stack to get here etc means that ptr_tbl_size could not have
697 : * overflowed.
698 : *
699 : * NB: the underlying OS would have limited the amount of space used
700 : * for argv and envv -- on linux, it is ARG_MAX, or 128KB -- and
701 : * hence the overflow check is for obvious auditability rather than
702 : * for correctness.
703 : */
704 270 : auxv_entries = 1;
705 270 : if (0 != nap->user_entry_pt) {
706 5 : auxv_entries++;
707 5 : }
708 270 : if (0 != nap->dynamic_text_start) {
709 270 : auxv_entries++;
710 270 : }
711 270 : ptr_tbl_size = (((NACL_STACK_GETS_ARG ? 1 : 0) +
712 : (3 + argc + 1 + envc + 1 + auxv_entries * 2)) *
713 : sizeof(uint32_t));
714 :
715 270 : if (SIZE_T_MAX - size < ptr_tbl_size) {
716 0 : NaClLog(LOG_WARNING,
717 : "NaClCreateMainThread: ptr_tbl_size cause size of"
718 : " argv / environment copy to overflow!?!\n");
719 0 : retval = 0;
720 0 : goto cleanup;
721 : }
722 270 : size += ptr_tbl_size;
723 :
724 270 : size = (size + NACL_STACK_ALIGN_MASK) & ~NACL_STACK_ALIGN_MASK;
725 :
726 270 : if (size > nap->stack_size) {
727 0 : retval = 0;
728 0 : goto cleanup;
729 : }
730 :
731 : /*
732 : * Write strings and char * arrays to stack.
733 : */
734 270 : stack_ptr = NaClUserToSysAddrRange(nap, NaClGetInitialStackTop(nap) - size,
735 : size);
736 270 : if (stack_ptr == kNaClBadAddress) {
737 0 : retval = 0;
738 0 : goto cleanup;
739 : }
740 :
741 270 : NaClLog(2, "setting stack to : %016"NACL_PRIxPTR"\n", stack_ptr);
742 :
743 810 : VCHECK(0 == (stack_ptr & NACL_STACK_ALIGN_MASK),
744 : ("stack_ptr not aligned: %016"NACL_PRIxPTR"\n", stack_ptr));
745 :
746 270 : p = (uint32_t *) stack_ptr;
747 270 : strp = (char *) stack_ptr + ptr_tbl_size;
748 :
749 : /*
750 : * For x86-32, we push an initial argument that is the address of
751 : * the main argument block. For other machines, this is passed
752 : * in a register and that's set in NaClStartThreadInApp.
753 : */
754 : if (NACL_STACK_GETS_ARG) {
755 : uint32_t *argloc = p++;
756 : *argloc = (uint32_t) NaClSysToUser(nap, (uintptr_t) p);
757 : }
758 :
759 270 : *p++ = 0; /* Cleanup function pointer, always NULL. */
760 270 : *p++ = envc;
761 270 : *p++ = argc;
762 :
763 1340 : for (i = 0; i < argc; ++i) {
764 400 : *p++ = (uint32_t) NaClSysToUser(nap, (uintptr_t) strp);
765 400 : NaClLog(2, "copying arg %d %p -> %p\n",
766 : i, argv[i], strp);
767 1200 : strcpy(strp, argv[i]);
768 400 : strp += argv_len[i];
769 400 : }
770 270 : *p++ = 0; /* argv[argc] is NULL. */
771 :
772 568 : for (i = 0; i < envc; ++i) {
773 14 : *p++ = (uint32_t) NaClSysToUser(nap, (uintptr_t) strp);
774 14 : NaClLog(2, "copying env %d %p -> %p\n",
775 : i, envv[i], strp);
776 42 : strcpy(strp, envv[i]);
777 14 : strp += envv_len[i];
778 14 : }
779 270 : *p++ = 0; /* envp[envc] is NULL. */
780 :
781 : /* Push an auxv */
782 270 : if (0 != nap->user_entry_pt) {
783 5 : *p++ = AT_ENTRY;
784 5 : *p++ = (uint32_t) nap->user_entry_pt;
785 5 : }
786 270 : if (0 != nap->dynamic_text_start) {
787 270 : *p++ = AT_BASE;
788 270 : *p++ = (uint32_t) nap->dynamic_text_start;
789 270 : }
790 270 : *p++ = AT_NULL;
791 270 : *p++ = 0;
792 :
793 810 : CHECK((char *) p == (char *) stack_ptr + ptr_tbl_size);
794 :
795 : /* now actually spawn the thread */
796 270 : NaClXMutexLock(&nap->mu);
797 : /*
798 : * Unreference the main nexe and irt at this point if no debug stub callbacks
799 : * have been registered, as these references to the main nexe and irt
800 : * descriptors are only used when providing file access to the debugger.
801 : * In the debug case, let shutdown take care of cleanup.
802 : */
803 270 : if (NULL == nap->debug_stub_callbacks) {
804 253 : if (NULL != nap->main_nexe_desc) {
805 244 : NaClDescUnref(nap->main_nexe_desc);
806 244 : nap->main_nexe_desc = NULL;
807 244 : }
808 253 : if (NULL != nap->irt_nexe_desc) {
809 5 : NaClDescUnref(nap->irt_nexe_desc);
810 5 : nap->irt_nexe_desc = NULL;
811 5 : }
812 253 : }
813 270 : nap->running = 1;
814 270 : NaClXMutexUnlock(&nap->mu);
815 :
816 270 : NaClVmHoleWaitToStartThread(nap);
817 :
818 : /*
819 : * For x86, we adjust the stack pointer down to push a dummy return
820 : * address. This happens after the stack pointer alignment.
821 : * We avoid the otherwise harmless call for the zero case because
822 : * _FORTIFY_SOURCE memset can warn about zero-length calls.
823 : */
824 : if (NACL_STACK_PAD_BELOW_ALIGN != 0) {
825 270 : stack_ptr -= NACL_STACK_PAD_BELOW_ALIGN;
826 810 : memset((void *) stack_ptr, 0, NACL_STACK_PAD_BELOW_ALIGN);
827 : }
828 :
829 270 : NaClLog(2, "system stack ptr : %016"NACL_PRIxPTR"\n", stack_ptr);
830 270 : NaClLog(2, " user stack ptr : %016"NACL_PRIxPTR"\n",
831 270 : NaClSysToUserStackAddr(nap, stack_ptr));
832 :
833 : /* e_entry is user addr */
834 540 : retval = NaClAppThreadSpawn(nap,
835 : nap->initial_entry_pt,
836 270 : NaClSysToUserStackAddr(nap, stack_ptr),
837 : /* user_tls1= */ (uint32_t) nap->break_addr,
838 : /* user_tls2= */ 0);
839 :
840 : cleanup:
841 270 : free(argv_len);
842 270 : free(envv_len);
843 :
844 270 : return retval;
845 : }
846 :
847 268 : int NaClWaitForMainThreadToExit(struct NaClApp *nap) {
848 268 : NaClLog(3, "NaClWaitForMainThreadToExit: taking NaClApp lock\n");
849 268 : NaClXMutexLock(&nap->mu);
850 268 : NaClLog(3, " waiting for exit status\n");
851 778 : while (nap->running) {
852 268 : NaClXCondVarWait(&nap->cv, &nap->mu);
853 268 : NaClLog(3, " wakeup, nap->running %d, nap->exit_status %d\n",
854 : nap->running, nap->exit_status);
855 268 : }
856 242 : NaClXMutexUnlock(&nap->mu);
857 : /*
858 : * Some thread invoked the exit (exit_group) syscall.
859 : */
860 :
861 242 : if (NULL != nap->debug_stub_callbacks) {
862 2 : nap->debug_stub_callbacks->process_exit_hook();
863 2 : }
864 :
865 242 : return NACL_ABI_WEXITSTATUS(nap->exit_status);
866 : }
867 :
868 : /*
869 : * stack_ptr is from syscall, so a 32-bit address.
870 : */
871 20611 : int32_t NaClCreateAdditionalThread(struct NaClApp *nap,
872 20611 : uintptr_t prog_ctr,
873 20611 : uintptr_t sys_stack_ptr,
874 20611 : uint32_t user_tls1,
875 20611 : uint32_t user_tls2) {
876 41222 : if (!NaClAppThreadSpawn(nap,
877 : prog_ctr,
878 20611 : NaClSysToUserStackAddr(nap, sys_stack_ptr),
879 : user_tls1,
880 : user_tls2)) {
881 0 : NaClLog(LOG_WARNING,
882 : ("NaClCreateAdditionalThread: could not allocate thread."
883 : " Returning EAGAIN per POSIX specs.\n"));
884 0 : return -NACL_ABI_EAGAIN;
885 : }
886 20611 : return 0;
887 20611 : }
|