/* preallocate memory area of given size. abort if fail */ static void PreallocateUserMemory(struct NaClApp *nap) { uintptr_t i; int64_t heap; void *p; assert(nap != NULL); assert(nap->system_manifest != NULL); /* quit function if max_mem is not specified or invalid */ ZLOGFAIL(nap->heap_end == 0 || nap->heap_end > FOURGIG, ENOMEM, "invalid memory size"); /* calculate user heap size (must be allocated next to the data_end) */ p = (void*)NaClRoundAllocPage(nap->data_end); heap = nap->heap_end - nap->stack_size; heap = NaClRoundAllocPage(heap) - NaClRoundAllocPage(nap->data_end); ZLOGFAIL(heap <= LEAST_USER_HEAP_SIZE, ENOMEM, "user heap size is too small"); /* since 4gb of user space is already allocated just set protection to the heap */ p = (void*)NaClUserToSys(nap, (uintptr_t)p); i = NaCl_mprotect(p, heap, PROT_READ | PROT_WRITE); ZLOGFAIL(0 != i, -i, "cannot set protection on user heap"); nap->heap_end = NaClSysToUser(nap, (uintptr_t)p + heap); nap->mem_map[HeapIdx].size += heap; nap->mem_map[HeapIdx].end += heap; }
/* protect user manifest and update mem_map */ static void ProtectUserManifest(struct NaClApp *nap, void *mft) { uintptr_t page_ptr; uint64_t size; size = FOURGIG - nap->stack_size - NaClSysToUser(nap, (uintptr_t)mft); page_ptr = AlignAndProtect((uintptr_t) mft, size, PROT_READ); /* update mem_map */ SET_MEM_MAP_IDX(nap->mem_map[SysDataIdx], "UserManifest", page_ptr, ROUNDUP_64K(size + ((uintptr_t)mft - page_ptr)), PROT_READ); /* its time to add hole to memory map */ page_ptr = nap->mem_map[HeapIdx].end; size = nap->mem_map[SysDataIdx].start - nap->mem_map[HeapIdx].end; SET_MEM_MAP_IDX(nap->mem_map[HoleIdx], "Hole", page_ptr, size, PROT_NONE); /* * patch: change the heap size to correct value. the user manifest * contains the different heap start (it does not include r/w data) * todo(d'b): fix it by adding a new memory region */ nap->mem_map[HeapIdx].size = nap->mem_map[HeapIdx].end - nap->mem_map[HeapIdx].start; }
/* set pointer to user manifest */ static void SetUserManifestPtr(struct NaClApp *nap, void *mft) { uintptr_t *p; p = (void*)NaClUserToSys(nap, FOURGIG - nap->stack_size - USER_PTR_SIZE); *p = NaClSysToUser(nap, (uintptr_t)mft); }
/* tls get */ int32_t NaClSysTls_Get(struct NaClApp *nap) { uint32_t user_tls; /* too frequently used, and syscall-number level logging suffices */ user_tls = (int32_t) NaClSysToUser(nap, nap->sys_tls); return user_tls; }
/* * preconditions: * argc > 0, argc and argv table is consistent * envv may be NULL (this happens on MacOS/Cocoa * if envv is non-NULL it is 'consistent', null terminated etc. */ int NaClCreateMainThread(struct NaClApp *nap, int argc, char **argv, char const *const *envv) { /* * Compute size of string tables for argv and envv */ int retval; int envc; size_t size; size_t ptr_tbl_size; int i; char *p; char *strp; size_t *argv_len; size_t *envv_len; struct NaClAppThread *natp; uintptr_t stack_ptr; /* * Set an exception handler so Windows won't show a message box if * an exception occurs */ WINDOWS_EXCEPTION_TRY; retval = 0; /* fail */ CHECK(argc > 0); CHECK(NULL != argv); envc = 0; if (NULL != envv) { char const *const *pp; for (pp = envv; NULL != *pp; ++pp) { ++envc; } } envv_len = 0; argv_len = malloc(argc * sizeof argv_len[0]); envv_len = malloc(envc * sizeof envv_len[0]); if (NULL == argv_len) { goto cleanup; } if (NULL == envv_len && 0 != envc) { goto cleanup; } /* * Store information for remote query when debugging. */ NaClDebugSetAppInfo(nap); NaClDebugSetAppEnvironment(argc, (char const * const *) argv, envc, (char const * const *) envv); NaClDebugStart(); size = 0; /* * The following two loops cannot overflow. The reason for this is * that they are counting the number of bytes used to hold the * NUL-terminated strings that comprise the argv and envv tables. * If the entire address space consisted of just those strings, then * the size variable would overflow; however, since there's the code * space required to hold the code below (and we are not targetting * Harvard architecture machines), at least one page holds code, not * data. We are assuming that the caller is non-adversarial and the * code does not look like string data.... */ for (i = 0; i < argc; ++i) { argv_len[i] = strlen(argv[i]) + 1; size += argv_len[i]; } for (i = 0; i < envc; ++i) { envv_len[i] = strlen(envv[i]) + 1; size += envv_len[i]; } /* * NaCl modules are ILP32, so the argv, envv pointers, as well as * the terminating NULL pointers at the end of the argv/envv tables, * are 32-bit values. We also have the empty auxv to take into * account, so that's 6 additional 32-bit values on top of the * argv/envv contents. Note that on nacl64, argc is popped, and * is an 8-byte value! * * The argv and envv pointer tables came from trusted code and is * part of memory. Thus, by the same argument above, adding in * (argc+envc+4)*sizeof(void *) cannot possibly overflow the size * variable since it is a size_t object. However, the two more * pointers for auxv and the space for argv could cause an overflow. * The fact that we used stack to get here etc means that * ptr_tb_size could not have overflowed. * * NB: the underlying OS would have limited the amount of space used * for argv and envv -- on linux, it is ARG_MAX, or 128KB -- and * hence the overflow check is for obvious auditability rather than * for correctness. */ ptr_tbl_size = (argc + envc + 6) * sizeof(uint32_t) + sizeof(int); if (SIZE_T_MAX - size < ptr_tbl_size) { NaClLog(LOG_WARNING, "NaClCreateMainThread: ptr_tb_size cause size of" " argv / environment copy to overflow!?!\n"); retval = 0; goto cleanup; } size += ptr_tbl_size; size = (size + NACL_STACK_ALIGN_MASK) & ~NACL_STACK_ALIGN_MASK; if (size > nap->stack_size) { retval = 0; goto cleanup; } /* write strings and char * arrays to stack */ stack_ptr = (nap->mem_start + ((uintptr_t) 1U << nap->addr_bits) - size); NaClLog(2, "setting stack to : %016"NACL_PRIxPTR"\n", stack_ptr); VCHECK(0 == (stack_ptr & NACL_STACK_ALIGN_MASK), ("stack_ptr not aligned: %016"NACL_PRIxPTR"\n", stack_ptr)); p = (char *) stack_ptr; strp = p + ptr_tbl_size; #define BLAT(t, v) do { \ *(t *) p = (t) v; p += sizeof(t); \ } while (0); BLAT(nacl_reg_t, argc); for (i = 0; i < argc; ++i) { BLAT(uint32_t, NaClSysToUser(nap, (uintptr_t) strp)); NaClLog(2, "copying arg %d %p -> %p\n", i, argv[i], strp); strcpy(strp, argv[i]); strp += argv_len[i]; } BLAT(uint32_t, 0); for (i = 0; i < envc; ++i) { BLAT(uint32_t, NaClSysToUser(nap, (uintptr_t) strp)); NaClLog(2, "copying env %d %p -> %p\n", i, envv[i], strp); strcpy(strp, envv[i]); strp += envv_len[i]; } BLAT(uint32_t, 0); /* Push an empty auxv for glibc support */ BLAT(uint32_t, 0); BLAT(uint32_t, 0); #undef BLAT /* now actually spawn the thread */ natp = malloc(sizeof *natp); if (!natp) { goto cleanup; } /* We are ready to distinguish crashes in trusted and untrusted code. */ NaClSignalRegisterApp(nap); nap->running = 1; NaClLog(2, "system stack ptr : %016"NACL_PRIxPTR"\n", stack_ptr); NaClLog(2, " user stack ptr : %016"NACL_PRIxPTR"\n", NaClSysToUserStackAddr(nap, stack_ptr)); /* e_entry is user addr */ if (!NaClAppThreadAllocSegCtor(natp, nap, 1, nap->entry_pt, NaClSysToUserStackAddr(nap, stack_ptr), NaClUserToSys(nap, nap->break_addr), 1)) { retval = 0; goto cleanup; } /* * NB: Hereafter locking is required to access nap. */ retval = 1; cleanup: free(argv_len); free(envv_len); WINDOWS_EXCEPTION_CATCH; return retval; }
/* * Apply memory protection to memory regions. */ void NaClMemoryProtection(struct NaClApp *nap) { uintptr_t start_addr; size_t region_size; int err; /* * The first NACL_SYSCALL_START_ADDR bytes are mapped as PROT_NONE. * This enables NULL pointer checking, and provides additional protection * against addr16/data16 prefixed operations being used for attacks. * * NaClMprotectGuards also sets up guard pages outside of the * virtual address space of the NaClApp -- for the ARM and x86-64 * where data sandboxing only sandbox memory writes and not reads, * we need to ensure that certain addressing modes that might * otherwise allow the NaCl app to write outside its address space * (given how we using masking / base registers to implement data * write sandboxing) won't affect the trusted data structures. */ ZLOGS(LOG_DEBUG, "Protecting guard pages for 0x%08x", nap->mem_start); NaClMprotectGuards(nap); start_addr = nap->mem_start + NACL_SYSCALL_START_ADDR; /* * The next pages up to NACL_TRAMPOLINE_END are the trampolines. * Immediately following that is the loaded text section. * These are collectively marked as PROT_READ | PROT_EXEC. */ region_size = NaClRoundPage(nap->static_text_end - NACL_SYSCALL_START_ADDR); ZLOGS(LOG_DEBUG, "Trampoline/text region start 0x%08x, size 0x%08x, end 0x%08x", start_addr, region_size, start_addr + region_size); err = NaCl_mprotect((void *)start_addr, region_size, PROT_READ | PROT_EXEC); ZLOGFAIL(0 != err, err, FAILED_MSG); SET_MEM_MAP_IDX(nap->mem_map[TextIdx], "Text", start_addr, region_size, PROT_READ | PROT_EXEC); /* * Page protections for this region have already been set up by * nacl_text.c. * * todo(d'b): since text.c exists no more, protection should be set here * * We record the mapping for consistency with other fixed * mappings, but the record is not actually used. Overmapping is * prevented by a separate range check, which is done by * NaClSysCommonAddrRangeContainsExecutablePages_mu(). */ /* * zerovm does not support dynamic text. the code below will check its * existence, log information and fail if needed. * todo(d'b): after the dynamic text support will be added or completely * removed the block below should be rewritten or removed */ start_addr = NaClUserToSys(nap, nap->dynamic_text_start); region_size = nap->dynamic_text_end - nap->dynamic_text_start; ZLOGS(LOG_DEBUG, "shm txt region start 0x%08x, size 0x%08x, end 0x%08x", start_addr, region_size, start_addr + region_size); ZLOGFAIL(0 != region_size, EFAULT, "zerovm does not support nexe with dynamic text!"); if(0 != nap->rodata_start) { uintptr_t rodata_end; /* * TODO(mseaborn): Could reduce the number of cases by ensuring * that nap->data_start is always non-zero, even if * nap->rodata_start == nap->data_start == nap->break_addr. */ if(0 != nap->data_start) rodata_end = nap->data_start; else rodata_end = nap->break_addr; start_addr = NaClUserToSys(nap, nap->rodata_start); region_size = NaClRoundPage(NaClRoundAllocPage(rodata_end) - NaClSysToUser(nap, start_addr)); ZLOGS(LOG_DEBUG, "RO data region start 0x%08x, size 0x%08x, end 0x%08x", start_addr, region_size, start_addr + region_size); err = NaCl_mprotect((void *)start_addr, region_size, PROT_READ); ZLOGFAIL(0 != err, err, FAILED_MSG); SET_MEM_MAP_IDX(nap->mem_map[RODataIdx], "ROData", start_addr, region_size, PROT_READ); } /* * data_end is max virtual addr seen, so start_addr <= data_end * must hold. */ if(0 != nap->data_start) { start_addr = NaClUserToSys(nap, nap->data_start); region_size = NaClRoundPage(NaClRoundAllocPage(nap->data_end) - NaClSysToUser(nap, start_addr)); ZLOGS(LOG_DEBUG, "RW data region start 0x%08x, size 0x%08x, end 0x%08x", start_addr, region_size, start_addr + region_size); err = NaCl_mprotect((void *)start_addr, region_size, PROT_READ | PROT_WRITE); ZLOGFAIL(0 != err, err, FAILED_MSG); SET_MEM_MAP_IDX(nap->mem_map[HeapIdx], "Heap", start_addr, region_size, PROT_READ | PROT_WRITE); } /* stack is read/write but not execute */ region_size = nap->stack_size; start_addr = NaClUserToSys(nap, NaClTruncAllocPage(((uintptr_t) 1U << nap->addr_bits) - nap->stack_size)); ZLOGS(LOG_DEBUG, "RW stack region start 0x%08x, size 0x%08lx, end 0x%08x", start_addr, region_size, start_addr + region_size); err = NaCl_mprotect((void *)start_addr, NaClRoundAllocPage(nap->stack_size), PROT_READ | PROT_WRITE); ZLOGFAIL(0 != err, err, FAILED_MSG); SET_MEM_MAP_IDX(nap->mem_map[StackIdx], "Stack", start_addr, NaClRoundAllocPage(nap->stack_size), PROT_READ | PROT_WRITE); }
/* serialize system data to user space */ static void SetSystemData(struct NaClApp *nap) { struct SystemManifest *manifest; struct ChannelSerialized *channels; struct UserManifestSerialized *user_manifest; void *ptr; /* pointer to the user manifest area */ int64_t size; int i; assert(nap != NULL); assert(nap->system_manifest != NULL); assert(nap->system_manifest->channels != NULL); manifest = nap->system_manifest; /* * 1. calculate channels array size (w/o aliases) * 2. calculate user manifest size (w/o aliases) * 3. calculate pointer to user manifest * 4. calculate pointer to channels array */ size = manifest->channels_count * CHANNEL_STRUCT_SIZE; size += USER_MANIFEST_STRUCT_SIZE + USER_PTR_SIZE; ptr = (void*)(FOURGIG - nap->stack_size - size); user_manifest = (void*)NaClUserToSys(nap, (uintptr_t)ptr); channels = (void*)((uintptr_t)&user_manifest->channels + USER_PTR_SIZE); /* make the 1st page of user manifest writeable */ CopyDown((void*)NaClUserToSys(nap, FOURGIG - nap->stack_size), ""); ptr = user_manifest; /* initialize pointer to user manifest */ SetUserManifestPtr(nap, user_manifest); /* serialize channels */ for(i = 0; i < manifest->channels_count; ++i) { /* limits */ int j; for(j = 0; j < IOLimitsCount; ++j) channels[i].limits[j] = manifest->channels[i].limits[j]; /* type and size */ channels[i].type = (int32_t)manifest->channels[i].type; channels[i].size = channels[i].type == SGetSPut ? 0 : manifest->channels[i].size; /* alias */ ptr = CopyDown(ptr, manifest->channels[i].alias); channels[i].name = NaClSysToUser(nap, (uintptr_t)ptr); } /* update heap_size in the user manifest */ size = ROUNDDOWN_64K(NaClSysToUser(nap, (uintptr_t)ptr)); size = MIN(nap->heap_end, size); user_manifest->heap_size = size - nap->break_addr; /* note that rw data merged with heap! */ /* update memory map */ nap->mem_map[HeapIdx].end = NaClUserToSys(nap, size); nap->mem_map[HeapIdx].size = user_manifest->heap_size; /* serialize the rest of the user manifest records */ user_manifest->heap_ptr = nap->break_addr; user_manifest->stack_size = nap->stack_size; user_manifest->channels_count = manifest->channels_count; user_manifest->channels = NaClSysToUser(nap, (uintptr_t)channels); /* make the user manifest read only */ ProtectUserManifest(nap, ptr); }
/* * preconditions: * argc > 0, argc and argv table is consistent * envv may be NULL (this happens on MacOS/Cocoa * if envv is non-NULL it is 'consistent', null terminated etc. */ int NaClCreateMainThread(struct NaClApp *nap, int argc, char **argv, char const *const *envv) { /* * Compute size of string tables for argv and envv */ int retval; int envc; size_t size; int auxv_entries; size_t ptr_tbl_size; int i; uint32_t *p; char *strp; size_t *argv_len; size_t *envv_len; uintptr_t stack_ptr; retval = 0; /* fail */ CHECK(argc > 0); CHECK(NULL != argv); envc = 0; if (NULL != envv) { char const *const *pp; for (pp = envv; NULL != *pp; ++pp) { ++envc; } } envv_len = 0; argv_len = malloc(argc * sizeof argv_len[0]); envv_len = malloc(envc * sizeof envv_len[0]); if (NULL == argv_len) { goto cleanup; } if (NULL == envv_len && 0 != envc) { goto cleanup; } size = 0; /* * The following two loops cannot overflow. The reason for this is * that they are counting the number of bytes used to hold the * NUL-terminated strings that comprise the argv and envv tables. * If the entire address space consisted of just those strings, then * the size variable would overflow; however, since there's the code * space required to hold the code below (and we are not targetting * Harvard architecture machines), at least one page holds code, not * data. We are assuming that the caller is non-adversarial and the * code does not look like string data.... */ for (i = 0; i < argc; ++i) { argv_len[i] = strlen(argv[i]) + 1; size += argv_len[i]; } for (i = 0; i < envc; ++i) { envv_len[i] = strlen(envv[i]) + 1; size += envv_len[i]; } /* * NaCl modules are ILP32, so the argv, envv pointers, as well as * the terminating NULL pointers at the end of the argv/envv tables, * are 32-bit values. We also have the auxv to take into account. * * The argv and envv pointer tables came from trusted code and is * part of memory. Thus, by the same argument above, adding in * "ptr_tbl_size" cannot possibly overflow the "size" variable since * it is a size_t object. However, the extra pointers for auxv and * the space for argv could cause an overflow. The fact that we * used stack to get here etc means that ptr_tbl_size could not have * overflowed. * * NB: the underlying OS would have limited the amount of space used * for argv and envv -- on linux, it is ARG_MAX, or 128KB -- and * hence the overflow check is for obvious auditability rather than * for correctness. */ auxv_entries = 1; if (0 != nap->user_entry_pt) { auxv_entries++; } ptr_tbl_size = (((NACL_STACK_GETS_ARG ? 1 : 0) + (3 + argc + 1 + envc + 1 + auxv_entries * 2)) * sizeof(uint32_t)); if (SIZE_T_MAX - size < ptr_tbl_size) { NaClLog(LOG_WARNING, "NaClCreateMainThread: ptr_tbl_size cause size of" " argv / environment copy to overflow!?!\n"); retval = 0; goto cleanup; } size += ptr_tbl_size; size = (size + NACL_STACK_ALIGN_MASK) & ~NACL_STACK_ALIGN_MASK; if (size > nap->stack_size) { retval = 0; goto cleanup; } /* write strings and char * arrays to stack */ stack_ptr = (nap->mem_start + ((uintptr_t) 1U << nap->addr_bits) - size); NaClLog(2, "setting stack to : %016"NACL_PRIxPTR"\n", stack_ptr); VCHECK(0 == (stack_ptr & NACL_STACK_ALIGN_MASK), ("stack_ptr not aligned: %016"NACL_PRIxPTR"\n", stack_ptr)); p = (uint32_t *) stack_ptr; strp = (char *) stack_ptr + ptr_tbl_size; /* * For x86-32, we push an initial argument that is the address of * the main argument block. For other machines, this is passed * in a register and that's set in NaClStartThreadInApp. */ if (NACL_STACK_GETS_ARG) { uint32_t *argloc = p++; *argloc = (uint32_t) NaClSysToUser(nap, (uintptr_t) p); } *p++ = 0; /* Cleanup function pointer, always NULL. */ *p++ = envc; *p++ = argc; for (i = 0; i < argc; ++i) { *p++ = (uint32_t) NaClSysToUser(nap, (uintptr_t) strp); NaClLog(2, "copying arg %d %p -> %p\n", i, argv[i], strp); strcpy(strp, argv[i]); strp += argv_len[i]; } *p++ = 0; /* argv[argc] is NULL. */ for (i = 0; i < envc; ++i) { *p++ = (uint32_t) NaClSysToUser(nap, (uintptr_t) strp); NaClLog(2, "copying env %d %p -> %p\n", i, envv[i], strp); strcpy(strp, envv[i]); strp += envv_len[i]; } *p++ = 0; /* envp[envc] is NULL. */ /* Push an auxv */ if (0 != nap->user_entry_pt) { *p++ = AT_ENTRY; *p++ = (uint32_t) nap->user_entry_pt; } *p++ = AT_NULL; *p++ = 0; CHECK((char *) p == (char *) stack_ptr + ptr_tbl_size); /* * For x86, we adjust the stack pointer down to push a dummy return * address. This happens after the stack pointer alignment. */ stack_ptr -= NACL_STACK_PAD_BELOW_ALIGN; memset((void *) stack_ptr, 0, NACL_STACK_PAD_BELOW_ALIGN); NaClLog(2, "system stack ptr : %016"NACL_PRIxPTR"\n", stack_ptr); NaClLog(2, " user stack ptr : %016"NACL_PRIxPTR"\n", NaClSysToUserStackAddr(nap, stack_ptr)); /* d'b: jump directly to user code instead of using thread launching */ ResumeCpuClock(nap); SwitchToApp(nap, stack_ptr); /* d'b end */ retval = 1; cleanup: free(argv_len); free(envv_len); return retval; }
/* * preconditions: * * argc is the length of the argv array * * envv may be NULL (this happens on MacOS/Cocoa and in tests) * * if envv is non-NULL it is 'consistent', null terminated etc. */ int NaClCreateMainThread(struct NaClApp *nap, int argc, char **argv, char const *const *envv) { /* * Compute size of string tables for argv and envv */ int retval; int envc; size_t size; int auxv_entries; size_t ptr_tbl_size; int i; uint32_t *p; char *strp; size_t *argv_len; size_t *envv_len; uintptr_t stack_ptr; retval = 0; /* fail */ CHECK(argc >= 0); CHECK(NULL != argv || 0 == argc); envc = 0; if (NULL != envv) { char const *const *pp; for (pp = envv; NULL != *pp; ++pp) { ++envc; } } envv_len = 0; argv_len = malloc(argc * sizeof argv_len[0]); envv_len = malloc(envc * sizeof envv_len[0]); if (NULL == argv_len) { goto cleanup; } if (NULL == envv_len && 0 != envc) { goto cleanup; } size = 0; /* * The following two loops cannot overflow. The reason for this is * that they are counting the number of bytes used to hold the * NUL-terminated strings that comprise the argv and envv tables. * If the entire address space consisted of just those strings, then * the size variable would overflow; however, since there's the code * space required to hold the code below (and we are not targetting * Harvard architecture machines), at least one page holds code, not * data. We are assuming that the caller is non-adversarial and the * code does not look like string data.... */ for (i = 0; i < argc; ++i) { argv_len[i] = strlen(argv[i]) + 1; size += argv_len[i]; } for (i = 0; i < envc; ++i) { envv_len[i] = strlen(envv[i]) + 1; size += envv_len[i]; } /* * NaCl modules are ILP32, so the argv, envv pointers, as well as * the terminating NULL pointers at the end of the argv/envv tables, * are 32-bit values. We also have the auxv to take into account. * * The argv and envv pointer tables came from trusted code and is * part of memory. Thus, by the same argument above, adding in * "ptr_tbl_size" cannot possibly overflow the "size" variable since * it is a size_t object. However, the extra pointers for auxv and * the space for argv could cause an overflow. The fact that we * used stack to get here etc means that ptr_tbl_size could not have * overflowed. * * NB: the underlying OS would have limited the amount of space used * for argv and envv -- on linux, it is ARG_MAX, or 128KB -- and * hence the overflow check is for obvious auditability rather than * for correctness. */ auxv_entries = 1; if (0 != nap->user_entry_pt) { auxv_entries++; } if (0 != nap->dynamic_text_start) { auxv_entries++; } ptr_tbl_size = (((NACL_STACK_GETS_ARG ? 1 : 0) + (3 + argc + 1 + envc + 1 + auxv_entries * 2)) * sizeof(uint32_t)); if (SIZE_T_MAX - size < ptr_tbl_size) { NaClLog(LOG_WARNING, "NaClCreateMainThread: ptr_tbl_size cause size of" " argv / environment copy to overflow!?!\n"); retval = 0; goto cleanup; } size += ptr_tbl_size; size = (size + NACL_STACK_ALIGN_MASK) & ~NACL_STACK_ALIGN_MASK; if (size > nap->stack_size) { retval = 0; goto cleanup; } /* * Write strings and char * arrays to stack. */ stack_ptr = NaClUserToSysAddrRange(nap, NaClGetInitialStackTop(nap) - size, size); if (stack_ptr == kNaClBadAddress) { retval = 0; goto cleanup; } NaClLog(2, "setting stack to : %016"NACL_PRIxPTR"\n", stack_ptr); VCHECK(0 == (stack_ptr & NACL_STACK_ALIGN_MASK), ("stack_ptr not aligned: %016"NACL_PRIxPTR"\n", stack_ptr)); p = (uint32_t *) stack_ptr; strp = (char *) stack_ptr + ptr_tbl_size; /* * For x86-32, we push an initial argument that is the address of * the main argument block. For other machines, this is passed * in a register and that's set in NaClStartThreadInApp. */ if (NACL_STACK_GETS_ARG) { uint32_t *argloc = p++; *argloc = (uint32_t) NaClSysToUser(nap, (uintptr_t) p); } *p++ = 0; /* Cleanup function pointer, always NULL. */ *p++ = envc; *p++ = argc; for (i = 0; i < argc; ++i) { *p++ = (uint32_t) NaClSysToUser(nap, (uintptr_t) strp); NaClLog(2, "copying arg %d %p -> %p\n", i, argv[i], strp); strcpy(strp, argv[i]); strp += argv_len[i]; } *p++ = 0; /* argv[argc] is NULL. */ for (i = 0; i < envc; ++i) { *p++ = (uint32_t) NaClSysToUser(nap, (uintptr_t) strp); NaClLog(2, "copying env %d %p -> %p\n", i, envv[i], strp); strcpy(strp, envv[i]); strp += envv_len[i]; } *p++ = 0; /* envp[envc] is NULL. */ /* Push an auxv */ if (0 != nap->user_entry_pt) { *p++ = AT_ENTRY; *p++ = (uint32_t) nap->user_entry_pt; } if (0 != nap->dynamic_text_start) { *p++ = AT_BASE; *p++ = (uint32_t) nap->dynamic_text_start; } *p++ = AT_NULL; *p++ = 0; CHECK((char *) p == (char *) stack_ptr + ptr_tbl_size); /* now actually spawn the thread */ NaClXMutexLock(&nap->mu); /* * Unreference the main nexe and irt at this point if no debug stub callbacks * have been registered, as these references to the main nexe and irt * descriptors are only used when providing file access to the debugger. * In the debug case, let shutdown take care of cleanup. */ if (NULL == nap->debug_stub_callbacks) { if (NULL != nap->main_nexe_desc) { NaClDescUnref(nap->main_nexe_desc); nap->main_nexe_desc = NULL; } if (NULL != nap->irt_nexe_desc) { NaClDescUnref(nap->irt_nexe_desc); nap->irt_nexe_desc = NULL; } } nap->running = 1; NaClXMutexUnlock(&nap->mu); NaClVmHoleWaitToStartThread(nap); /* * For x86, we adjust the stack pointer down to push a dummy return * address. This happens after the stack pointer alignment. * We avoid the otherwise harmless call for the zero case because * _FORTIFY_SOURCE memset can warn about zero-length calls. */ if (NACL_STACK_PAD_BELOW_ALIGN != 0) { stack_ptr -= NACL_STACK_PAD_BELOW_ALIGN; memset((void *) stack_ptr, 0, NACL_STACK_PAD_BELOW_ALIGN); } NaClLog(2, "system stack ptr : %016"NACL_PRIxPTR"\n", stack_ptr); NaClLog(2, " user stack ptr : %016"NACL_PRIxPTR"\n", NaClSysToUserStackAddr(nap, stack_ptr)); /* e_entry is user addr */ retval = NaClAppThreadSpawn(nap, nap->initial_entry_pt, NaClSysToUserStackAddr(nap, stack_ptr), /* user_tls1= */ (uint32_t) nap->break_addr, /* user_tls2= */ 0); cleanup: free(argv_len); free(envv_len); return retval; }
static void NaClDescEffLdrUnmapMemory(struct NaClDescEffector *vself, uintptr_t sysaddr, size_t nbytes) { struct NaClDescEffectorLdr *self = (struct NaClDescEffectorLdr *) vself; uintptr_t addr; uintptr_t endaddr; uintptr_t usraddr; struct NaClVmmapEntry const *map_region; NaClLog(4, ("NaClDescEffLdrUnmapMemory(0x%08"NACL_PRIxPTR", 0x%08"NACL_PRIxPTR ", 0x%"NACL_PRIxS")\n"), (uintptr_t) vself, (uintptr_t) sysaddr, nbytes); for (addr = sysaddr, endaddr = sysaddr + nbytes; addr < endaddr; addr += NACL_MAP_PAGESIZE) { usraddr = NaClSysToUser(self->nap, addr); map_region = NaClVmmapFindPage(&self->nap->mem_map, usraddr >> NACL_PAGESHIFT); /* * When mapping beyond the end of file, the mapping will be rounded to * the 64k page boundary and the remaining space will be marked as * inaccessible by marking the pages as MEM_RESERVE. * * When unmapping the memory region, we use the file size, recorded in * the VmmapEntry to prevent a race condition when file size changes * after it was mmapped, together with the page num and offset to check * whether the page is the one backed by the file, in which case we * need to unmap it, or whether it's one of the tail pages backed by the * virtual memory in which case we need to release it. */ if (NULL != map_region && NULL != map_region->desc && (map_region->offset + (usraddr - (map_region->page_num << NACL_PAGESHIFT)) < (uintptr_t) map_region->file_size)) { if (!UnmapViewOfFile((void *) addr)) { NaClLog(LOG_FATAL, ("NaClDescEffLdrUnmapMemory: UnmapViewOfFile failed at" " user addr 0x%08"NACL_PRIxPTR" (sys 0x%08"NACL_PRIxPTR")" " error %d\n"), usraddr, addr, GetLastError()); } } else { /* * No memory in address space, and we have only MEM_RESERVE'd * the address space; or memory is in address space, but not * backed by a file. */ if (!VirtualFree((void *) addr, 0, MEM_RELEASE)) { NaClLog(LOG_FATAL, ("NaClDescEffLdrUnmapMemory: VirtualFree at user addr" " 0x%08"NACL_PRIxPTR" (sys 0x%08"NACL_PRIxPTR") failed:" " error %d\n"), usraddr, addr, GetLastError()); } } } }