NaClErrorCode NaClAllocateSpace(void **mem, size_t addrsp_size) { size_t mem_sz = 2 * GUARDSIZE + FOURGIG; /* 40G guard on each side */ size_t log_align = ALIGN_BITS; void *mem_ptr; NaClLog(4, "NaClAllocateSpace(*, 0x%016"NACL_PRIxS" bytes).\n", addrsp_size); CHECK(addrsp_size == FOURGIG); errno = 0; mem_ptr = NaClAllocatePow2AlignedMemory(mem_sz, log_align); if (NULL == mem_ptr) { if (0 != errno) { perror("NaClAllocatePow2AlignedMemory"); } NaClLog(LOG_WARNING, "Memory allocation failed\n"); return LOAD_NO_MEMORY; } /* * The module lives in the middle FOURGIG of the allocated region -- * we skip over an initial 40G guard. */ *mem = (void *) (((char *) mem_ptr) + GUARDSIZE); NaClLog(4, "NaClAllocateSpace: addr space at 0x%016"NACL_PRIxPTR"\n", (uintptr_t) *mem); return LOAD_OK; }
/* * Platform-specific routine to allocate memory space for the NaCl * module. mem is an out argument; addrsp_size is the requested * address space size, currently always ((size_t) 1) << * nap->addr_bits. On x86-64, there's a further requirement that this * is 4G. * * The actual amount of memory allocated is larger than requested on * x86-64 and on the ARM, since guard pages are also allocated to be * contiguous with the allocated address space. * * If successful, the guard pages are not yet memory protected. The * function NaClMprotectGuards must be called for the guard pages to * be active. * * update: abort zvm if failed */ static void NaClAllocateSpace(void **mem, size_t addrsp_size) { size_t mem_sz = 2 * GUARDSIZE + FOURGIG; /* 40G guard on each side */ size_t log_align = ALIGN_BITS; void *mem_ptr; ZLOGS(LOG_INSANE, "NaClAllocateSpace(*, 0x%016lx bytes)", addrsp_size); ZLOGFAIL(addrsp_size != FOURGIG, EFAULT, "addrsp_size != FOURGIG"); errno = 0; mem_ptr = NaClAllocatePow2AlignedMemory(mem_sz, log_align); ZLOGFAIL(NULL == mem_ptr, errno, "NaClAllocatePow2AlignedMemory failed"); /* * The module lives in the middle FOURGIG of the allocated region -- * we skip over an initial 40G guard. */ *mem = (void *)(((char *)mem_ptr) + GUARDSIZE); ZLOGS(LOG_INSANE, "addr space at 0x%016lx", (uintptr_t)*mem); }
NaClErrorCode NaClAllocateSpaceAslr(void **mem, size_t addrsp_size, enum NaClAslrMode aslr_mode) { /* 40G guard on each side */ size_t mem_sz = (NACL_ADDRSPACE_LOWER_GUARD_SIZE + FOURGIG + NACL_ADDRSPACE_UPPER_GUARD_SIZE); size_t log_align = ALIGN_BITS; void *mem_ptr; #if NACL_LINUX struct rlimit rlim; #endif NaClLog(4, "NaClAllocateSpace(*, 0x%016"NACL_PRIxS" bytes).\n", addrsp_size); CHECK(addrsp_size == FOURGIG); if (NACL_X86_64_ZERO_BASED_SANDBOX) { mem_sz = 11 * FOURGIG; if (getenv("NACL_ENABLE_INSECURE_ZERO_BASED_SANDBOX") != NULL) { /* * For the zero-based 64-bit sandbox, we want to reserve 44GB of address * space: 4GB for the program plus 40GB of guard pages. Due to a binutils * bug (see http://sourceware.org/bugzilla/show_bug.cgi?id=13400), the * amount of address space that the linker can pre-reserve is capped * at 4GB. For proper reservation, GNU ld version 2.22 or higher * needs to be used. * * Without the bug fix, trying to reserve 44GB will result in * pre-reserving the entire capped space of 4GB. This tricks the run-time * into thinking that we can mmap up to 44GB. This is unsafe as it can * overwrite the run-time program itself and/or other programs. * * For now, we allow a 4GB address space as a proof-of-concept insecure * sandboxing model. * * TODO(arbenson): remove this if block once the binutils bug is fixed */ mem_sz = FOURGIG; } NaClAddrSpaceBeforeAlloc(mem_sz); if (NaClFindPrereservedSandboxMemory(mem, mem_sz)) { int result; void *tmp_mem = (void *) NACL_TRAMPOLINE_START; CHECK(*mem == 0); mem_sz -= NACL_TRAMPOLINE_START; result = NaClPageAllocAtAddr(&tmp_mem, mem_sz); if (0 != result) { NaClLog(2, "NaClAllocateSpace: NaClPageAlloc 0x%08"NACL_PRIxPTR " failed\n", (uintptr_t) *mem); return LOAD_NO_MEMORY; } NaClLog(4, "NaClAllocateSpace: %"NACL_PRIxPTR", %"NACL_PRIxS"\n", (uintptr_t) *mem, mem_sz); return LOAD_OK; } NaClLog(LOG_ERROR, "Failed to find prereserved memory\n"); return LOAD_NO_MEMORY; } NaClAddrSpaceBeforeAlloc(mem_sz); errno = 0; mem_ptr = NaClAllocatePow2AlignedMemory(mem_sz, log_align, aslr_mode); if (NULL == mem_ptr) { if (0 != errno) { perror("NaClAllocatePow2AlignedMemory"); } NaClLog(LOG_WARNING, "Memory allocation failed\n"); #if NACL_LINUX /* * Check with getrlimit whether RLIMIT_AS was likely to be the * problem with an allocation failure. If so, generate a log * message. Since this is a debugging aid and we don't know about * the memory requirement of the code that is embedding native * client, there is some slop. */ if (0 != getrlimit(RLIMIT_AS, &rlim)) { perror("NaClAllocatePow2AlignedMemory::getrlimit"); } else { if (rlim.rlim_cur < mem_sz) { /* * Developer hint/warning; this will show up in the crash log * and must be brief. */ NaClLog(LOG_INFO, "Please run \"ulimit -v unlimited\" (bash)" " or \"limit vmemoryuse unlimited\" (tcsh)\n"); NaClLog(LOG_INFO, "and restart the app. NaCl requires at least %"NACL_PRIdS"" " kilobytes of virtual\n", mem_sz / 1024); NaClLog(LOG_INFO, "address space. NB: Raising the hard limit requires" " root access.\n"); } } #elif NACL_OSX /* * In OSX, RLIMIT_AS and RLIMIT_RSS have the same value; i.e., OSX * conflates the notion of virtual address space used with the * resident set size. In particular, the way NaCl uses virtual * address space is to allocate guard pages so that certain * addressing modes will not need to be explicitly masked; the * guard pages are allocated but inaccessible, never faulted so * not even zero-filled on demand, so they should not count * against the resident set -- which is supposed to be only the * frequently accessed pages in the first place. */ #endif return LOAD_NO_MEMORY; } /* * The module lives in the middle FOURGIG of the allocated region -- * we skip over an initial 40G guard. */ *mem = (void *) (((char *) mem_ptr) + NACL_ADDRSPACE_LOWER_GUARD_SIZE); NaClLog(4, "NaClAllocateSpace: addr space at 0x%016"NACL_PRIxPTR"\n", (uintptr_t) *mem); return LOAD_OK; }