/* helper. sets custom user attributes for user */ static void SetCustomAttributes(struct SystemManifest *policy) { int i; int count; char *environment; char *buf[BIG_ENOUGH_SPACE], **tokens = buf; assert(policy != NULL); assert(policy->envp == NULL); /* get environment */ environment = GetValueByKey(MFT_ENVIRONMENT); if(environment == NULL) return; /* parse and check count of attributes */ count = ParseValue(environment, ", \t", tokens, BIG_ENOUGH_SPACE); ZLOGFAIL(count % 2 != 0, EFAULT, "odd number of user environment variables"); ZLOGFAIL(count == 0, EFAULT, "invalid user environment"); ZLOGFAIL(count == BIG_ENOUGH_SPACE, EFAULT, "user environment exceeded the limit"); /* allocate space to hold string pointers */ count >>= 1; policy->envp = g_malloc0((count + 1) * sizeof(*policy->envp)); /* construct array of environment variables */ for(i = 0; i < count; ++i) { char *key = *tokens++; char *value = *tokens++; int length = strlen(key) + strlen(value) + 1 + 1; /* + '=' + '\0' */ policy->envp[i] = g_malloc0((length + 1) * sizeof(*policy->envp[0])); sprintf(policy->envp[i], "%s=%s", key, value); } }
/* * Fill from static_text_end to end of that page with halt * instruction, which is at least NACL_HALT_LEN in size when no * dynamic text is present. Does not touch dynamic text region, which * should be pre-filled with HLTs. * * By adding NACL_HALT_SLED_SIZE, we ensure that the code region ends * with HLTs, just in case the CPU has a bug in which it fails to * check for running off the end of the x86 code segment. */ void static FillEndOfTextRegion(struct NaClApp *nap) { size_t page_pad; /* * NOTE: make sure we are not silently overwriting data. It is the * toolchain's responsibility to ensure that a NACL_HALT_SLED_SIZE * gap exists. */ ZLOGFAIL(0 != nap->data_start && nap->static_text_end + NACL_HALT_SLED_SIZE > nap->data_start, EFAULT, "Missing gap between text and data for halt_sled"); ZLOGFAIL(0 != nap->rodata_start && nap->static_text_end + NACL_HALT_SLED_SIZE > nap->rodata_start, EFAULT, "Missing gap between text and rodata for halt_sled"); /* No dynamic text exists. Space for NACL_HALT_SLED_SIZE must exist */ page_pad = ROUNDUP_64K(nap->static_text_end + NACL_HALT_SLED_SIZE) - nap->static_text_end; ZLOGFAIL(page_pad < NACL_HALT_SLED_SIZE, EFAULT, FAILED_MSG); ZLOGFAIL(page_pad >= NACL_MAP_PAGESIZE + NACL_HALT_SLED_SIZE, EFAULT, FAILED_MSG); ZLOGS(LOG_INSANE, "Filling with halts: %08lx, %08lx bytes", nap->mem_start + nap->static_text_end, page_pad); FillMemoryRegionWithHalt((void*)(nap->mem_start + nap->static_text_end), page_pad); nap->static_text_end += page_pad; }
/* * set validation state according to zvm command line options * note: updates nap->validation_state */ static void ValidateNexe(struct NaClApp *nap) { char *args[3] = {VALIDATOR_NAME, NULL, NULL}; GError *error = NULL; int exit_status = 0; enum ValidationState { NotValidated, ValidationOK, ValidationFailed }; assert(nap != NULL); assert(nap->system_manifest != NULL); /* skip validation? */ nap->validation_state = NotValidated; if(nap->skip_validator != 0) return; /* prepare command line and run it */ args[1] = nap->system_manifest->nexe; ZLOGFAIL(g_spawn_sync(NULL, args, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, NULL, NULL, &exit_status, &error) == 0, EPERM, "cannot start validator"); /* check the result */ nap->validation_state = exit_status == 0 ? ValidationOK : ValidationFailed; ZLOGFAIL(nap->validation_state != ValidationOK, ENOEXEC, "validation failed"); }
/* sets node_id in nap object and user argv[0] */ static void SetNodeName(struct NaClApp *nap) { int i; char *buf[BIG_ENOUGH_SPACE], **tokens = buf; char *pgm_name = GetValueByKey(MFT_NODE); assert(nap != NULL); assert(nap->system_manifest != NULL); assert(nap->system_manifest->cmd_line != NULL); assert(nap->system_manifest->cmd_line_size > 0); /* set the node name (0st parameter) and node id (n/a for the user) */ if(pgm_name == NULL) { nap->system_manifest->cmd_line[0] = NEXE_PGM_NAME; nap->system_manifest->node_id = 0; } else { i = ParseValue(pgm_name, ",", tokens, BIG_ENOUGH_SPACE); ZLOGFAIL(i != 2, EFAULT, "invalid NodeName specified"); ZLOGFAIL(tokens[0] == NULL, EFAULT, "invalid node name"); ZLOGFAIL(tokens[1] == NULL, EFAULT, "invalid node id"); nap->system_manifest->cmd_line[0] = tokens[0]; nap->system_manifest->node_id = ATOI(tokens[1]); ZLOGFAIL(nap->system_manifest->node_id == 0, EFAULT, "node id must be > 0"); } /* put node name and id to the log */ ZLOGS(LOG_DEBUG, "node name = %s, node id = %d", nap->system_manifest->cmd_line[0], nap->system_manifest->node_id); }
/* 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; }
/* helper. sets command line parameters for the user */ static void SetCommandLine(struct SystemManifest *policy) { int i; char *parameters; char *buf[BIG_ENOUGH_SPACE], **tokens = buf; assert(policy != NULL); assert(policy->cmd_line == NULL); assert(policy->cmd_line_size == 0); /* get parameters */ parameters = GetValueByKey(MFT_COMMANDLINE); /* if there is command line parse and check count of parameters */ if(parameters != NULL) { policy->cmd_line_size = ParseValue(parameters, " \t", tokens, BIG_ENOUGH_SPACE); ZLOGFAIL(policy->cmd_line_size == 0, EFAULT, "invalid user parameters"); ZLOGFAIL(policy->cmd_line_size == BIG_ENOUGH_SPACE, EFAULT, "too long user command line"); } /* * allocate space to hold string pointers. 0st element reserved for argv[0]. * also, the last element should be NULL so 1 extra element must be reserved */ ++policy->cmd_line_size; policy->cmd_line = g_malloc0((policy->cmd_line_size + 1) * sizeof *policy->cmd_line); /* populate command line arguments array with pointers */ for(i = 0; i < policy->cmd_line_size - 1; ++i) policy->cmd_line[i + 1] = tokens[i]; }
void NameServiceCtor(struct Manifest *manifest, uint32_t b, uint32_t c) { char *parcel; uint32_t psize; uint32_t control_psize; uint32_t control_nsources; assert(manifest != NULL); assert(manifest->channels != NULL); assert(b + c <= MAX_CHANNELS_NUMBER); /* return if there is no name service or network sources */ if(manifest->name_server == NULL) return; if(b + c < 1) return; /* create parcel */ ZLOGFAIL(manifest->node < 1, EFAULT, "invalid node: %d", manifest->node); ZLOGFAIL(manifest->name_server->protocol != ProtoUDP, EFAULT, "name server only support udp protocol"); parcel = ParcelCtor(manifest, &psize, b, c); /* exchange parcels with name server */ control_psize = PollServer(manifest->name_server, parcel, psize); ZLOGFAIL(control_psize != psize, EFAULT, "received parcel size %u is not equal to sent one %u", control_psize, psize); /* decode received parcel to channels */ control_nsources = ParcelDtor(manifest, parcel); ZLOGFAIL(control_nsources != b + c, EFAULT, "received parcel records %u is not equal to sent ones %u", control_nsources, b + c); }
void NaClRunSelQualificationTests() { /* fail if Operating system platform is not supported */ ZLOGFAIL(!NaClOsIsSupported(), EFAULT, "os not supported"); /* fail if Data Execution Prevention is required but is not supported */ ZLOGFAIL(!NaClCheckDEP(), EFAULT, "dep not supported"); }
/* get manifest data */ static void GetManifestData(const char *name, char *buf) { FILE *h = fopen(name, "r"); ZLOGFAIL(h == NULL, ENOENT, "manifest open error"); ZLOGFAIL(fread(buf, 1, MANIFEST_SIZE_LIMIT, h) < 1, EIO, "manifest read error"); fclose(h); }
/* serve trap invoked from the untrusted code */ NORETURN void SyscallHook() { struct NaClApp *nap; struct ThreadContext *user; uintptr_t tramp_ret; nacl_reg_t user_ret; size_t sysnum; uintptr_t sp_user; uintptr_t sp_sys; /* restore trusted side environment */ nap = gnap; /* restore NaClApp object */ user = nacl_user; sp_user = GetThreadCtxSp(user); sp_sys = sp_user; /* * sp_sys points to the top of user stack where there is a retaddr to * trampoline slot */ tramp_ret = *(uintptr_t *)sp_sys; sysnum = (tramp_ret - (nap->mem_start + NACL_SYSCALL_START_ADDR)) >> NACL_SYSCALL_BLOCK_SHIFT; /* * getting user return address (the address where we need to return after * system call) from the user stack. (see stack layout above) */ user_ret = *(uintptr_t *)(sp_sys + NACL_USERRET_FIX); /* * Fix the user stack, throw away return addresses from the top of the stack. * After this fix, the first argument to a system call must be on the top of * the user stack (see user stack layout above) */ sp_sys += NACL_SYSARGS_FIX; sp_user += NACL_SYSCALLRET_FIX; SetThreadCtxSp(user, sp_user); /* fail if nacl syscall received */ ZLOGFAIL(sysnum != 0, EINVAL, "nacl syscall #%d received", sysnum); /* * syscall_args must point to the first argument of a system call. * System call arguments are placed on the untrusted user stack. */ nap->sysret = TrapHandler(nap, *(uint32_t*)sp_sys); /* * before switching back to user module, we need to make sure that the * user_ret is properly sandboxed. */ user_ret = (nacl_reg_t)NaClSandboxCodeAddr(nap, (uintptr_t)user_ret); /* d'b: give control to the user side */ SwitchToApp(nap, user_ret); ZLOGFAIL(1, EFAULT, "the unreachable has been reached"); }
/* * NaClAllocatePow2AlignedMemory is for allocating a large amount of * memory of mem_sz bytes that must be address aligned, so that * log_alignment low-order address bits must be zero. * * Returns the aligned region on success, or NULL on failure. */ static void *NaClAllocatePow2AlignedMemory(size_t mem_sz, size_t log_alignment) { uintptr_t pow2align; size_t request_sz; void *mem_ptr; uintptr_t orig_addr; uintptr_t rounded_addr; size_t extra; pow2align = ((uintptr_t)1) << log_alignment; request_sz = mem_sz + pow2align; ZLOGS(LOG_INSANE, "%25s %016lx", " Ask:", request_sz); /* d'b: try to get the fixed address r15 (user base register) */ /* * WARNING: mmap can overwrite the zerovm dynamically linked code. * to prevent it the code should be linked statically */ mem_ptr = mmap(R15_CONST, request_sz, PROT_NONE, ABSOLUTE_MMAP, -1, (off_t)0); if(MAP_FAILED == mem_ptr) { ZLOG(LOG_ERROR, "the base register absolute address allocation failed!" " trying to allocate user space in NOT DETERMINISTIC WAY"); mem_ptr = mmap(NULL, request_sz, PROT_NONE, RELATIVE_MMAP, -1, (off_t)0); ZLOGFAIL(MAP_FAILED == mem_ptr, ENOMEM, FAILED_MSG); } orig_addr = (uintptr_t)mem_ptr; ZLOGS(LOG_INSANE, "%25s %016lx", "orig memory at", orig_addr); rounded_addr = (orig_addr + (pow2align - 1)) & ~(pow2align - 1); extra = rounded_addr - orig_addr; if(0 != extra) { ZLOGS(LOG_INSANE, "%25s %016lx, %016lx", "Freeing front:", orig_addr, extra); ZLOGFAIL(-1 == munmap((void *)orig_addr, extra), errno, "munmap front failed"); } extra = pow2align - extra; if(0 != extra) { ZLOGS(LOG_INSANE, "%25s %016lx, %016lx", "Freeing tail:", rounded_addr + mem_sz, extra); ZLOGFAIL(-1 == munmap((void *)(rounded_addr + mem_sz), extra), errno, "munmap tail failed"); } ZLOGS(LOG_INSANE, "%25s %016lx", "Aligned memory:", rounded_addr); /* * we could also mmap again at rounded_addr w/o MAP_NORESERVE etc to * ensure that we have the memory, but that's better done in another * utility function. the semantics here is no paging space * reserved, as in Windows MEM_RESERVE without MEM_COMMIT. */ return (void *)rounded_addr; }
/* * check if obligatory keywords appeared and check if the fields * which should appear only once did so */ static void CheckCounters(int *counters, int n) { while(--n) { ZLOGFAIL(counters[n] == 0 && (CHECK_KEYWORDS[n] & 1), EFAULT, "%s not specified", XSTR(KEYWORDS, n)); ZLOGFAIL(counters[n] > 1 && (CHECK_KEYWORDS[n] & 2), EFAULT, "duplicate %s keyword", XSTR(KEYWORDS, n)); } }
void ElfImageLoad(const struct ElfImage *image, struct Gio *gp, uint8_t addr_bits, uintptr_t mem_start) { int segnum; uintptr_t paddr; uintptr_t end_vaddr; for(segnum = 0; segnum < image->ehdr.e_phnum; ++segnum) { const Elf_Phdr *php = &image->phdrs[segnum]; /* did we decide that we will load this segment earlier? */ if(!image->loadable[segnum]) continue; ZLOGS(LOG_INSANE, "loading segment %d", segnum); if(0 == php->p_filesz) { ZLOGS(LOG_INSANE, "zero-sized segment. ignoring..."); continue; } end_vaddr = php->p_vaddr + php->p_filesz; /* integer overflow? */ ZLOGFAIL(end_vaddr < php->p_vaddr, EFAULT, "parameter error should have been detected already"); /* * is the end virtual address within the NaCl application's * address space? if it is, it implies that the start virtual * address is also. */ ZLOGFAIL(end_vaddr >= ((uintptr_t)1U << addr_bits), EFAULT, "parameter error should have been detected already"); paddr = mem_start + php->p_vaddr; ZLOGS(LOG_INSANE, "Seek to position %d (0x%x)", php->p_offset, php->p_offset); /* * NB: php->p_offset may not be a valid off_t on 64-bit systems, but * in that case Seek() will error out. * d'b: fail if ELF executable segment header parameter error */ ZLOGFAIL((*gp->vtbl->Seek)(gp, (off_t)php->p_offset, SEEK_SET) == (off_t)-1, ENOEXEC, "seek failure segment %d", segnum); ZLOGS(LOG_INSANE, "Reading %d (0x%x) bytes to address 0x%x", php->p_filesz, php->p_filesz, paddr); ZLOGFAIL((Elf_Word)(*gp->vtbl->Read)(gp, (void *)paddr, php->p_filesz) != php->p_filesz, ENOEXEC, "load failure segment %d", segnum); /* region from p_filesz to p_memsz should already be zero filled */ } }
void ValidateElfHeader(const struct ElfImage *image) { const Elf_Ehdr *hdr = &image->ehdr; ZLOGFAIL(memcmp(hdr->e_ident, ELFMAG, SELFMAG), ENOEXEC, "bad elf magic"); ZLOGFAIL(ELFCLASS64 != hdr->e_ident[EI_CLASS], ENOEXEC, "bad elf class"); ZLOGFAIL(ET_EXEC != hdr->e_type, ENOEXEC, "non executable"); ZLOGFAIL(EM_EXPECTED_BY_NACL != hdr->e_machine, ENOEXEC, "bad machine: %x", hdr->e_machine); ZLOGFAIL(EV_CURRENT != hdr->e_version, ENOEXEC, "bad elf version: %x", hdr->e_version); }
/* set timeout. by design timeout must be specified in manifest */ static void SetTimeout(struct SystemManifest *policy) { struct rlimit rl; assert(policy != NULL); GET_INT_BY_KEY(policy->timeout, MFT_TIMEOUT); ZLOGFAIL(policy->timeout < 1, EFAULT, "invalid or absent timeout"); rl.rlim_cur = policy->timeout; rl.rlim_max = -1; setrlimit(RLIMIT_CPU, &rl); ZLOGFAIL(setrlimit(RLIMIT_CPU, &rl) != 0, errno, "cannot set timeout"); }
/* construct system_manifest object and initialize it from manifest */ void SystemManifestCtor(struct NaClApp *nap) { struct SystemManifest *policy; /* check for design errors */ assert(nap != NULL); assert(nap->system_manifest != NULL); policy = nap->system_manifest; policy->syscallback = 0; /* get zerovm settings from manifest */ policy->version = GetValueByKey("Version"); policy->nexe_etag = GetValueByKey("NexeEtag"); /* check mandatory manifest keys */ ZLOGFAIL(nap->system_manifest->version == NULL, EFAULT, "the manifest version is not provided"); ZLOGFAIL(STRCMP(nap->system_manifest->version, MANIFEST_VERSION), EFAULT, "manifest version not supported"); SetTimeout(policy); /* user data (environment, command line) */ policy->envp = NULL; SetCustomAttributes(policy); /* prepare command line arguments for nexe */ policy->cmd_line = NULL; policy->cmd_line_size = 0; SetCommandLine(policy); /* get node name and id */ SetNodeName(nap); /* construct and initialize all channels */ ChannelsCtor(nap); /* * allocate "whole memory chunk" if specified. should be the last allocation * in raw because after chunk allocated there will be no free user memory * note: will set "heap_ptr" */ GET_INT_BY_KEY(nap->heap_end, "MemMax"); PreallocateUserMemory(nap); /* zerovm return code */ nap->system_manifest->ret_code = OK_CODE; }
void NaClAllocAddrSpace(struct NaClApp *nap) { void *mem; uintptr_t hole_start; size_t hole_size; uintptr_t stack_start; ZLOGS(LOG_DEBUG, "calling NaClAllocateSpace(*,0x%016x)", ((size_t)1 << nap->addr_bits)); NaClAllocateSpace(&mem, (uintptr_t) 1U << nap->addr_bits); nap->mem_start = (uintptr_t) mem; ZLOGS(LOG_DEBUG, "allocated memory at 0x%08x", nap->mem_start); hole_start = NaClRoundAllocPage(nap->data_end); ZLOGFAIL(nap->stack_size >= ((uintptr_t) 1U) << nap->addr_bits, EFAULT, "NaClAllocAddrSpace: stack too large!"); stack_start = (((uintptr_t) 1U) << nap->addr_bits) - nap->stack_size; stack_start = NaClTruncAllocPage(stack_start); ZLOGFAIL(stack_start < hole_start, EFAULT, "Memory 'hole' between end of BSS and start of stack is negative in size"); hole_size = stack_start - hole_start; hole_size = NaClTruncAllocPage(hole_size); /* * mprotect and madvise unused data space to "free" it up, but * retain mapping so no other memory can be mapped into those * addresses. */ if(hole_size != 0) { ZLOGS(LOG_DEBUG, "madvising 0x%08x, 0x%08x, MADV_DONTNEED", nap->mem_start + hole_start, hole_size); ZLOGFAIL(0 != NaCl_madvise((void*)(nap->mem_start + hole_start), hole_size, MADV_DONTNEED), errno, "madvise failed. cannot release unused data segment"); ZLOGS(LOG_DEBUG, "mprotecting 0x%08x, 0x%08x, PROT_NONE", nap->mem_start + hole_start, hole_size); ZLOGFAIL(0 != NaCl_mprotect((void *)(nap->mem_start + hole_start), hole_size, PROT_NONE), errno, "mprotect failed. cannot protect pages"); } else ZLOGS(LOG_DEBUG, "there is no hole between end of data and the beginning of stack"); }
void PreloadChannelCtor(struct ChannelDesc *channel, int n) { assert(channel != NULL); assert(n < channel->source->len); /* check the given channel */ ZLOGS(LOG_DEBUG, "mounting file %s to alias %s", CH_NAME(channel, n), channel->alias); SetChannelSource(channel, n); switch(CH_PROTO(channel, n)) { case ProtoRegular: RegularChannel(channel, n); break; case ProtoCharacter: case ProtoFIFO: CharacterChannel(channel, n); break; default: ZLOGFAIL(1, EPROTONOSUPPORT, "invalid %s source type %d", channel->alias, channel->type); break; } }
/* * Check that signal handlers are not registered. We want to * discourage Chrome or libraries from registering signal handlers * themselves, because those signal handlers are often not safe when * triggered from untrusted code. For background, see: * http://code.google.com/p/nativeclient/issues/detail?id=1607 */ void NaClSignalAssertNoHandlers() { int i; for(i = 0; i < SIGNAL_COUNT; i++) { int signum = s_Signals[i]; struct sigaction sa; ZLOGFAIL(-1 == sigaction(signum, NULL, &sa), errno, "sigaction() call failed"); ZLOGFAIL((sa.sa_flags & SA_SIGINFO) != 0 ? sa.sa_sigaction != NULL : (sa.sa_handler != SIG_DFL && sa.sa_handler != SIG_IGN), EFAULT, "A signal handler is registered for signal %d. Did Breakpad register this?", signum); } }
NORETURN void CreateSession(struct NaClApp *nap) { uintptr_t stack_ptr; assert(nap != NULL); /* set up user stack */ stack_ptr = nap->mem_start + ((uintptr_t)1U << nap->addr_bits); stack_ptr -= STACK_USER_DATA_SIZE; memset((void*)stack_ptr, 0, STACK_USER_DATA_SIZE); ((uint32_t*)stack_ptr)[4] = 1; ((uint32_t*)stack_ptr)[5] = 0xfffffff0; /* * construct "nacl_user" and "nacl_sys" globals * note: nacl_sys->prog_ctr meaningless but should not be 0 */ ThreadContextCtor(nacl_user, nap, nap->initial_entry_pt, stack_ptr); ThreadContextCtor(nacl_sys, nap, 1, GetStackPtr()); /* pass control to the user side */ ZLOGS(LOG_DEBUG, "SESSION %d STARTED", nap->manifest->node); ContextSwitch(nacl_user); ZLOGFAIL(1, EFAULT, "the unreachable has been reached"); }
int NaClSignalStackAllocate(void **result) { /* * We use mmap() to allocate the signal stack for two reasons: * * 1) By page-aligning the memory allocation (which malloc() does * not do for small allocations), we avoid allocating any real * memory in the common case in which the signal handler is never * run. * * 2) We get to create a guard page, to guard against the unlikely * occurrence of the signal handler both overrunning and doing so in * an exploitable way. */ uint8_t *stack = mmap(NULL, SIGNAL_STACK_SIZE + STACK_GUARD_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if(stack == MAP_FAILED) return 0; /* We assume that the stack grows downwards. */ ZLOGFAIL(-1 == mprotect(stack, STACK_GUARD_SIZE, PROT_NONE), errno, "Failed to mprotect() the stack guard page"); *result = stack; return 1; }
NORETURN void CreateSession(struct NaClApp *nap) { uintptr_t stack_ptr; assert(nap != NULL); /* set up user stack */ stack_ptr = nap->mem_start + ((uintptr_t)1U << nap->addr_bits); stack_ptr -= STACK_USER_DATA_SIZE; memset((void*)stack_ptr, 0, STACK_USER_DATA_SIZE); ((uint32_t*)stack_ptr)[4] = 1; ((uint32_t*)stack_ptr)[5] = 0xfffffff0; /* construct "nacl_user" global */ ThreadContextCtor(nacl_user, nap, nap->initial_entry_pt, stack_ptr, 0); 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 */ nacl_sys->rbp = GetStackPtr(); nacl_sys->rsp = GetStackPtr(); /* pass control to the user side */ ZLOGS(LOG_DEBUG, "SESSION %d STARTED", nap->manifest->node); SwitchToApp(nap, nacl_user->new_prog_ctr); ZLOGFAIL(1, EFAULT, "the unreachable has been reached"); }
/* set timeout. by design timeout must be specified in manifest */ static void SetTimeout(struct Manifest *manifest) { assert(manifest != NULL); ZLOGFAIL(manifest->timeout < 1, EFAULT, "invalid timeout %d", manifest->timeout); alarm(manifest->timeout); }
void NaClLoadTrampoline(struct NaClApp *nap) { int num_syscalls; int i; uintptr_t addr; ZLOGFAIL(!NaClMakeDispatchThunk(nap), EFAULT, FAILED_MSG); NaClFillTrampolineRegion(nap); /* * Do not bother to fill in the contents of page 0, since we make it * inaccessible later (see sel_addrspace.c, NaClMemoryProtection) * anyway to help detect NULL pointer errors, and we might as well * not dirty the page. * * The last syscall entry point is reserved (nacl springboard code) */ num_syscalls = ((NACL_TRAMPOLINE_END - NACL_SYSCALL_START_ADDR) / NACL_SYSCALL_BLOCK_SIZE) - 1; ZLOGS(LOG_INSANE, "num_syscalls = %d (0x%x)", num_syscalls, num_syscalls); addr = nap->mem_start + NACL_SYSCALL_START_ADDR; for (i = 0; i < num_syscalls; ++i) { NaClPatchOneTrampoline(nap, addr); addr += NACL_SYSCALL_BLOCK_SIZE; } }
void *TagCtor() { GChecksum *ctx; ctx = g_checksum_new(TAG_ENCRYPTION); ZLOGFAIL(ctx == NULL, EFAULT, "error initializing tag context"); return ctx; }
/* switch to the nacl module (untrusted content) after signal */ NORETURN void NaClSwitchToAppAfterSignal(struct NaClApp *nap) { /* prevent rdtsc execution */ ZLOGFAIL(prctl(PR_SET_TSC, PR_TSC_SIGSEGV) == -1, errno, "cannot prevent rdtsc execution"); NaClSwitch(nacl_user); }
/* * prepare "bind" channel information for the name service * note: should be called before name service invocation */ static void PrepareBind(const struct ChannelDesc *channel) { struct ChannelConnection *record; static uint16_t port = LOWEST_AVAILABLE_PORT; int result = 1; assert(channel != NULL); /* update netlist with the connection info */ StoreChannelConnectionInfo(channel); /* if no name service is available just use given url and return */ if(!NameServiceSet()) { result = DoBind(channel); ZLOGFAIL(result != 0, EFAULT, "cannot bind %s", channel->alias); return; } record = GetChannelConnectionInfo(channel); assert(record != NULL); /* in case channel has port assigned (full url) bind it and return */ if(record->port != 0) { result = DoBind(channel); ZLOGFAIL(result != 0, EFAULT, "cannot bind %s", channel->alias); return; } /* * if channel does not have port pick up the port in the loop * todo(d'b): check upcoming zmq version for port 0 binding */ assert(record != NULL); for(;port >= LOWEST_AVAILABLE_PORT; ++port) { record->port = port; result = DoBind(channel); if(result == 0) break; } ZLOGFAIL(result != 0, EFAULT ,"cannot get port to bind %s", channel->alias); ZLOGS(LOG_DEBUG, "host = %u, port = %u", record->host, record->port); }
/* preallocate channel space if not disabled with "-P" */ static void PreallocateChannel(const struct ChannelDesc *channel, int n) { int code; int handle = GPOINTER_TO_INT(CH_HANDLE(channel, n)); if(disable_preallocation) return; code = ftruncate(handle, channel->limits[PutSizeLimit]); ZLOGFAIL(code == -1, errno, "cannot preallocate %s", channel->alias); }
void NaClInitSwitchToApp(struct NaClApp *nap) { int cpu = CPUTest(); UNREFERENCED_PARAMETER(nap); ZLOGFAIL(cpu == -1, EFAULT, "zerovm needs SSE CPU"); NaClSwitch = cpu == 0 ? NaClSwitchSSE : NaClSwitchAVX; ZLOGS(LOG_DEBUG, "%s cpu detected", cpu == 0 ? "SSE" : "AVX"); }
/* set channels field */ static void Channel(struct Manifest *manifest, char *value) { char **tokens; char **names; int i; struct ChannelDesc *channel; /* allocate a new channel */ channel = g_malloc0(sizeof *channel); channel->source = g_ptr_array_new(); /* get tokens from channel description */ tokens = g_strsplit(value, VALUE_DELIMITER, ChannelTokensNumber); /* TODO(d'b): fix "invalid numeric value ', 0'" bug here */ ZLOGFAIL(tokens[ChannelTokensNumber] != NULL || tokens[PutSize] == NULL, EFAULT, "invalid channel tokens number"); /* parse alias and name(s) */ channel->alias = g_strdup(g_strstrip(tokens[Alias])); names = g_strsplit(tokens[Name], TOKEN_DELIMITER, MANIFEST_TOKENS_LIMIT); for(i = 0; names[i] != NULL; ++i) ParseName(names[i], channel->source); channel->type = ToInt(tokens[Type]); i = ToInt(tokens[Tag]); ZLOGFAIL(i != 0 && i != 1, EFAULT, "invalid channel mumeric token"); channel->tag = i == 0 ? NULL : TagCtor(); for(i = 0; i < LimitsNumber; ++i) { channel->limits[i] = ToInt(tokens[i + Gets]); ZLOGFAIL(channel->limits[i] < 0, EFAULT, "negative limits for %s", channel->alias); } /* append a new channel */ g_ptr_array_add(manifest->channels, channel); g_strfreev(names); g_strfreev(tokens); }