void PreallocateUserMemory(struct NaClApp *nap) { uintptr_t i; int64_t heap; void *p; assert(nap != NULL); assert(nap->manifest != NULL); /* quit function if "Memory" is not specified or invalid */ ZLOGFAIL(nap->manifest->mem_size <= 0 || nap->manifest->mem_size > FOURGIG, ENOMEM, "invalid memory size"); /* calculate user heap size (must be allocated next to the data_end) */ p = (void*)ROUNDUP_64K(nap->data_end); heap = nap->manifest->mem_size - nap->stack_size; heap = ROUNDUP_64K(heap) - ROUNDUP_64K(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; }
/* * 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; }
/* Basic address space layout sanity check */ static void CheckAddressSpaceLayoutSanity(struct NaClApp *nap, uintptr_t rodata_end, uintptr_t data_end, uintptr_t max_vaddr) { /* fail if Data segment exists, but is not last segment */ if(0 != nap->data_start) ZLOGFAIL(data_end != max_vaddr, ENOEXEC, FAILED_MSG); /* * This should be unreachable, but we include it just for completeness * * Here is why it is unreachable: * * PhdrChecks checks the test segment starting address. The * only allowed loaded segments are text, data, and rodata. * Thus unless the rodata is in the trampoline region, it must * be after the text. And ValidateProgramHeaders ensures that * all segments start after the trampoline region. * * d'b: fail if no data segment. read-only data segment exists * but is not last segment */ else if(0 != nap->rodata_start) ZLOGFAIL(ROUNDUP_64K(rodata_end) != max_vaddr, ENOEXEC, FAILED_MSG); /* fail if Read-only data segment overlaps data segment */ if(0 != nap->rodata_start && 0 != nap->data_start) ZLOGFAIL(rodata_end > nap->data_start, ENOEXEC, FAILED_MSG); /* fail if Text segment overlaps rodata segment */ if(0 != nap->rodata_start) ZLOGFAIL(ROUNDUP_64K(NaClEndOfStaticText(nap)) > nap->rodata_start, ENOEXEC, FAILED_MSG); /* fail if No rodata segment, and text segment overlaps data segment */ else if(0 != nap->data_start) ZLOGFAIL(ROUNDUP_64K(NaClEndOfStaticText(nap)) > nap->data_start, ENOEXEC, FAILED_MSG); /* fail if rodata_start not a multiple of allocation size */ ZLOGFAIL(0 != nap->rodata_start && ROUNDUP_64K(nap->rodata_start) != nap->rodata_start, ENOEXEC, FAILED_MSG); /* fail if data_start not a multiple of allocation size */ ZLOGFAIL(0 != nap->data_start && ROUNDUP_64K(nap->data_start) != nap->data_start, ENOEXEC, FAILED_MSG); }
/* align area to page(s) both bounds an protect. return pointer to (new) area */ static uintptr_t AlignAndProtect(uintptr_t area, int64_t size, int prot) { uintptr_t page_ptr; uint64_t aligned_size; int code; page_ptr = ROUNDDOWN_64K(area); aligned_size = ROUNDUP_64K(size + (area - page_ptr)); code = NaCl_mprotect((void*)page_ptr, aligned_size, prot); ZLOGFAIL(0 != code, code, "cannot protect 0x%x of %d bytes with %d", page_ptr, aligned_size, prot); return page_ptr; }
void AppLoadFile(struct Gio *gp, struct NaClApp *nap) { uintptr_t rodata_end; uintptr_t data_end; uintptr_t max_vaddr; struct ElfImage *image = NULL; int err; /* fail if Address space too big */ ZLOGFAIL(nap->addr_bits > NACL_MAX_ADDR_BITS, EFAULT, FAILED_MSG); nap->stack_size = ROUNDUP_64K(nap->stack_size); /* temporay object will be deleted at end of function */ image = ElfImageNew(gp); ValidateElfHeader(image); ValidateProgramHeaders(image, nap->addr_bits, &nap->static_text_end, &nap->rodata_start, &rodata_end, &nap->data_start, &data_end, &max_vaddr); /* * if no rodata and no data, we make sure that there is space for * the halt sled. else if no data, but there is rodata. this means * max_vaddr is just where rodata ends. this might not be at an * allocation boundary, and in this the page would not be writable. * round max_vaddr up to the next allocation boundary so that bss * will be at the next writable region. */ if(0 == nap->data_start) { if(0 == nap->rodata_start) { if(ROUNDUP_64K(max_vaddr) - max_vaddr < NACL_HALT_SLED_SIZE) max_vaddr += NACL_MAP_PAGESIZE; } max_vaddr = ROUNDUP_64K(max_vaddr); } /* * max_vaddr -- the break or the boundary between data (initialized * and bss) and the address space hole -- does not have to be at a * page boundary. */ nap->break_addr = max_vaddr; nap->data_end = max_vaddr; ZLOGS(LOG_INSANE, "Values from ValidateProgramHeaders:"); DUMP(nap->rodata_start); DUMP(rodata_end); DUMP(nap->data_start); DUMP(data_end); DUMP(max_vaddr); nap->initial_entry_pt = ElfImageGetEntryPoint(image); LogAddressSpaceLayout(nap); /* Bad program entry point address */ ZLOGFAIL(!AddrIsValidEntryPt(nap, nap->initial_entry_pt), ENOEXEC, FAILED_MSG); CheckAddressSpaceLayoutSanity(nap, rodata_end, data_end, max_vaddr); ZLOGS(LOG_DEBUG, "Allocating address space"); AllocAddrSpace(nap); /* * Make sure the static image pages are marked writable before we try * to write them. */ ZLOGS(LOG_DEBUG, "Loading into memory"); err = NaCl_mprotect((void *)(nap->mem_start + NACL_TRAMPOLINE_START), ROUNDUP_64K(nap->data_end) - NACL_TRAMPOLINE_START, PROT_READ | PROT_WRITE); ZLOGFAIL(0 != err, EFAULT, "Failed to make image pages writable. errno = %d", err); ElfImageLoad(image, gp, nap->addr_bits, nap->mem_start); /* d'b: shared memory for the dynamic text disabled */ nap->dynamic_text_start = ROUNDUP_64K(NaClEndOfStaticText(nap)); nap->dynamic_text_end = nap->dynamic_text_start; /* * FillEndOfTextRegion will fill with halt instructions the * padding space after the static text region. * * Shm-backed dynamic text space was filled with halt instructions * in NaClMakeDynamicTextShared. This extends to the rodata. For * non-shm-backed text space, this extend to the next page (and not * allocation page). static_text_end is updated to include the * padding. */ FillEndOfTextRegion(nap); ZLOGS(LOG_DEBUG, "Initializing arch switcher"); InitSwitchToApp(nap); ZLOGS(LOG_DEBUG, "Installing trampoline"); LoadTrampoline(nap); /* * NaClMemoryProtect also initializes the mem_map w/ information * about the memory pages and their current protection value. * * The contents of the dynamic text region will get remapped as * non-writable. */ ZLOGS(LOG_DEBUG, "Applying memory protection"); MemoryProtection(nap); ZLOGS(LOG_DEBUG, "AppLoadFile done"); LogAddressSpaceLayout(nap); ElfImageDelete(image); }