/* * d'b: alternative mechanism to pass control to user side * note: initializes "nacl_user" global */ NORETURN void SwitchToApp(struct NaClApp *nap, uintptr_t stack_ptr) { /* initialize "nacl_user" global */ if(!nacl_user) nacl_user = malloc(sizeof(*nacl_user)); assert(nacl_user != NULL); /* construct "nacl_user" global */ NaClThreadContextCtor(nacl_user, nap, nap->initial_entry_pt, NaClSysToUserStackAddr(nap, stack_ptr), 0); assert(NaClSignalStackAllocate(&nap->signal_stack)); nacl_user->sysret = nap->break_addr; nacl_user->prog_ctr = NaClUserToSys(nap, nap->initial_entry_pt); nacl_user->new_prog_ctr = NaClUserToSys(nap, nap->initial_entry_pt); /* initialize "nacl_sys" global */ if(!nacl_sys) nacl_sys = malloc(sizeof(*nacl_sys)); assert(nacl_sys != NULL); nacl_sys->rbp = NaClGetStackPtr(); nacl_sys->rsp = NaClGetStackPtr(); /* set global nap to current nap object */ gnap = nap; /* * todo: put here switch to chose proper function: avx or sse */ NaClSwitchSSE(nacl_user); /* unreachable */ }
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; }
/* * stack_ptr is from syscall, so a 32-bit address. */ int32_t NaClCreateAdditionalThread(struct NaClApp *nap, uintptr_t prog_ctr, uintptr_t sys_stack_ptr, uint32_t user_tls1, uint32_t user_tls2) { if (!NaClAppThreadSpawn(nap, prog_ctr, NaClSysToUserStackAddr(nap, sys_stack_ptr), user_tls1, user_tls2)) { NaClLog(LOG_WARNING, ("NaClCreateAdditionalThread: could not allocate thread." " Returning EAGAIN per POSIX specs.\n")); return -NACL_ABI_EAGAIN; } return 0; }
/* * stack_ptr is from syscall, so a 32-bit address. */ int32_t NaClCreateAdditionalThread(struct NaClApp *nap, uintptr_t prog_ctr, uintptr_t sys_stack_ptr, uintptr_t sys_tdb, size_t tdb_size) { struct NaClAppThread *natp; uintptr_t stack_ptr; natp = malloc(sizeof *natp); if (NULL == natp) { NaClLog(LOG_WARNING, ("NaClCreateAdditionalThread: no memory for new thread context." " Returning EAGAIN per POSIX specs.\n")); return -NACL_ABI_EAGAIN; } stack_ptr = NaClSysToUserStackAddr(nap, sys_stack_ptr); if (0 != ((stack_ptr + sizeof(nacl_reg_t)) & NACL_STACK_ALIGN_MASK)) { NaClLog(3, ("NaClCreateAdditionalThread: user thread library provided " "an unaligned user stack pointer: 0x%"NACL_PRIxPTR"\n"), stack_ptr); } if (!NaClAppThreadAllocSegCtor(natp, nap, 0, prog_ctr, stack_ptr, sys_tdb, tdb_size)) { NaClLog(LOG_WARNING, ("NaClCreateAdditionalThread: could not allocate thread index." " Returning EAGAIN per POSIX specs.\n")); free(natp); return -NACL_ABI_EAGAIN; } return 0; }
/* * 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; }
/* * 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; }