NaClErrorCode NaClAllocateSpace(void **mem, size_t addrsp_size) { int result; CHECK(NULL != mem); NaClAddrSpaceBeforeAlloc(addrsp_size); #if NACL_LINUX /* * On 32 bit Linux, a 1 gigabyte block of address space may be reserved at * the zero-end of the address space during process creation, to address * sandbox layout requirements on ARM and performance issues on Intel ATOM. * Look for this prereserved block and if found, pass its address to the * page allocation function. */ if (NaClFindPrereservedSandboxMemory(mem, addrsp_size)) { void *tmp_mem = (void *) NACL_TRAMPOLINE_START; CHECK(*mem == 0); addrsp_size -= NACL_TRAMPOLINE_START; result = NaCl_page_alloc_at_addr(&tmp_mem, addrsp_size); } else { /* Zero-based sandbox not prereserved. Attempt to allocate anyway. */ result = NaCl_page_alloc(mem, addrsp_size); } #elif NACL_WINDOWS /* * On 32 bit Windows, a 1 gigabyte block of address space is reserved before * starting up this process to make sure we can create the sandbox. Look for * this prereserved block and if found, pass its address to the page * allocation function. */ if (0 == NaClFindPrereservedSandboxMemory(mem, addrsp_size)) { result = NaCl_page_alloc_at_addr(mem, addrsp_size); } else { result = NaCl_page_alloc(mem, addrsp_size); } #else result = NaCl_page_alloc(mem, addrsp_size); #endif if (0 != result) { NaClLog(2, "NaClAllocateSpace: NaCl_page_alloc 0x%08"NACL_PRIxPTR " failed\n", (uintptr_t) *mem); return LOAD_NO_MEMORY; } NaClLog(4, "NaClAllocateSpace: %"NACL_PRIxPTR", %"NACL_PRIxS"\n", (uintptr_t) *mem, addrsp_size); return LOAD_OK; }
/* NOTE: This routine is almost identical to the x86_32 version. */ NaClErrorCode NaClAllocateSpaceAslr(void **mem, size_t addrsp_size, enum NaClAslrMode aslr_mode) { int result; void *tmp_mem = (void *) NACL_TRAMPOLINE_START; UNREFERENCED_PARAMETER(aslr_mode); CHECK(NULL != mem); /* * On ARM, we cheat slightly: we add two pages to the requested * allocation! This accomodates the guard region we require at the * top end of untrusted memory. */ addrsp_size += NACL_ADDRSPACE_UPPER_GUARD_SIZE; NaClAddrSpaceBeforeAlloc(addrsp_size); /* * On 32 bit Linux, a 1 gigabyte block of address space may be reserved at * the zero-end of the address space during process creation, to address * sandbox layout requirements on ARM and performance issues on Intel ATOM. * Look for this prereserved block and if found, pass its address to the * page allocation function. */ if (!NaClFindPrereservedSandboxMemory(mem, addrsp_size)) { /* On ARM, we should always have prereserved sandbox memory. */ NaClLog(LOG_ERROR, "NaClAllocateSpace:" " Could not find correct amount of prereserved memory" " (looked for 0x%016"NACL_PRIxS" bytes).\n", addrsp_size); return LOAD_NO_MEMORY; } /* * When creating a zero-based sandbox, we do not allocate the first 64K of * pages beneath the trampolines, because -- on Linux at least -- we cannot. * Instead, we allocate starting at the trampolines, and then coerce the * "mem" out parameter. */ CHECK(*mem == NULL); addrsp_size -= NACL_TRAMPOLINE_START; result = NaCl_page_alloc_at_addr(&tmp_mem, addrsp_size); if (0 != result) { NaClLog(2, "NaClAllocateSpace: NaCl_page_alloc_at_addr 0x%08"NACL_PRIxPTR " failed\n", (uintptr_t) tmp_mem); return LOAD_NO_MEMORY; } NaClLog(4, "NaClAllocateSpace: %"NACL_PRIxPTR", %"NACL_PRIxS"\n", (uintptr_t) *mem, addrsp_size); return LOAD_OK; }
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; }