int main(int argc, char **argv) { struct NaClApp app; struct NaClApp *nap = &app; struct NaClSyscallTableEntry syscall_table[NACL_MAX_SYSCALLS] = {{0}}; int index; int use_separate_thread = 0; for (index = 0; index < NACL_MAX_SYSCALLS; index++) { syscall_table[index].handler = NotImplementedDecoder; } syscall_table[TEST_SYSCALL_INVOKE].handler = MySyscallInvoke; syscall_table[TEST_SYSCALL_EXIT].handler = MySyscallExit; NaClAllModulesInit(); CHECK(NaClAppWithSyscallTableCtor(&app, syscall_table)); CHECK(NaClAppLoadFileFromFilename(&app, argv[1]) == LOAD_OK); struct NaClAppThread *natp; uintptr_t stack_ptr = NaClGetInitialStackTop(nap); /* Ensure the stack pointer is suitably aligned. */ stack_ptr &= ~NACL_STACK_ALIGN_MASK; stack_ptr -= NACL_STACK_PAD_BELOW_ALIGN; /* TODO(mseaborn): Make this interface more straightforward to use. */ stack_ptr = NaClSysToUserStackAddr(nap, NaClUserToSys(nap, stack_ptr)); natp = NaClAppThreadMake(nap, nap->initial_entry_pt, stack_ptr, 0, 0); CHECK(natp != NULL); NaClAppThreadLauncher(natp); printf("game over\n"); return 1; }
/* * 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; }