int main(int argc, char **argv) { struct NaClApp app; uint32_t mmap_addr; char arg_string[32]; char *args[] = {"prog_name", arg_string}; NaClAllModulesInit(); if (argc != 2) { NaClLog(LOG_FATAL, "Expected 1 argument: executable filename\n"); } NaClAddSyscall(NACL_sys_test_syscall_1, TestSyscall); CHECK(NaClAppCtor(&app)); CHECK(NaClAppLoadFileFromFilename(&app, argv[1]) == LOAD_OK); NaClAppInitialDescriptorHookup(&app); CHECK(NaClAppPrepareToLaunch(&app) == LOAD_OK); NaClSignalHandlerInit(); NaClSignalHandlerSet(TrapSignalHandler); /* * Allocate some space in untrusted address space. We pass the * address to the guest program so that it can write a register * snapshot for us to compare against. */ mmap_addr = NaClSysMmapIntern( &app, NULL, sizeof(*g_test_shm), NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS, -1, 0); g_test_shm = (struct RegsTestShm *) NaClUserToSys(&app, mmap_addr); SNPRINTF(arg_string, sizeof(arg_string), "0x%x", (unsigned int) mmap_addr); CHECK(NaClCreateMainThread(&app, 2, args, NULL)); CHECK(NaClWaitForMainThreadToExit(&app) == 0); CHECK(!g_in_untrusted_code); ASSERT_EQ(g_context_switch_count, (kNumberOfCallsToTest + kFastPathSyscallsToTest - 1) * 2); /* * Avoid calling exit() because it runs process-global destructors * which might break code that is running in our unjoined threads. */ NaClExit(0); return 0; }
struct NaClSignalContext *StartGuestWithSharedMemory( struct NaClApp *nap) { char arg_string[32]; char *args[] = {"prog_name", arg_string}; uint32_t mmap_addr; struct NaClSignalContext *expected_regs; /* * Allocate some space in untrusted address space. We pass the * address to the guest program so that we can share data with it. */ mmap_addr = NaClSysMmapIntern( nap, NULL, sizeof(*expected_regs), NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS, -1, 0); SNPRINTF(arg_string, sizeof(arg_string), "0x%x", (unsigned int) mmap_addr); expected_regs = (struct NaClSignalContext *) NaClUserToSys(nap, mmap_addr); WaitForThreadToExitFully(nap); CHECK(NaClCreateMainThread(nap, NACL_ARRAY_SIZE(args), args, NULL)); return expected_regs; }
NaClErrorCode NaClElfImageLoadDynamically( struct NaClElfImage *image, struct NaClApp *nap, struct NaClDesc *ndp, struct NaClValidationMetadata *metadata) { ssize_t read_ret; int segnum; for (segnum = 0; segnum < image->ehdr.e_phnum; ++segnum) { const Elf_Phdr *php = &image->phdrs[segnum]; Elf_Addr vaddr = php->p_vaddr & ~(NACL_MAP_PAGESIZE - 1); Elf_Off offset = php->p_offset & ~(NACL_MAP_PAGESIZE - 1); Elf_Off filesz = php->p_offset + php->p_filesz - offset; Elf_Off memsz = php->p_offset + php->p_memsz - offset; int32_t result; /* * By checking if filesz is larger than memsz, we no longer run the risk of * a malicious ELF object overrunning into the trusted address space when * reading data of size "filez" into a buffer of size "memsz". */ if (filesz > memsz) { return LOAD_UNLOADABLE; } /* * We check for PT_LOAD directly rather than using the "loadable" * array because we are not using NaClElfImageValidateProgramHeaders() * to fill out the "loadable" array for this ELF object. This ELF * object does not have to fit such strict constraints (such as * having code at 0x20000), and safety checks are applied by * NaClTextDyncodeCreate() and NaClSysMmapIntern(). */ if (PT_LOAD != php->p_type) { continue; } if (0 != (php->p_flags & PF_X)) { /* Load code segment. */ /* * We make a copy of the code. This is not ideal given that this * code path is used only for loading the IRT, and we could assume * that the contents of the irt.nexe file will not change underneath * us. We should be able to mmap() the IRT's code segment instead of * copying it. * TODO(mseaborn): Reduce the amount of copying here. */ char *code_copy = malloc(filesz); if (NULL == code_copy) { NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: malloc failed\n"); return LOAD_NO_MEMORY; } read_ret = (*NACL_VTBL(NaClDesc, ndp)-> PRead)(ndp, code_copy, filesz, (nacl_off64_t) offset); if (NaClSSizeIsNegErrno(&read_ret) || (size_t) read_ret != filesz) { free(code_copy); NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: " "failed to read code segment\n"); return LOAD_READ_ERROR; } if (NULL != metadata) { metadata->code_offset = offset; } result = NaClTextDyncodeCreate(nap, (uint32_t) vaddr, code_copy, (uint32_t) filesz, metadata); free(code_copy); if (0 != result) { NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: " "failed to load code segment\n"); return LOAD_UNLOADABLE; } } else { /* Load data segment. */ void *paddr = (void *) NaClUserToSys(nap, vaddr); size_t mapping_size = NaClRoundAllocPage(memsz); /* * Note that we do not used NACL_ABI_MAP_FIXED because we do not * want to silently overwrite any existing mappings, such as the * user app's data segment or the stack. We detect overmapping * when mmap chooses not to use the preferred address we supply. * (Ideally mmap would provide a MAP_EXCL option for this * instead.) */ result = NaClSysMmapIntern( nap, (void *) (uintptr_t) vaddr, mapping_size, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE, -1, 0); if ((int32_t) vaddr != result) { NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: " "failed to map data segment\n"); return LOAD_UNLOADABLE; } read_ret = (*NACL_VTBL(NaClDesc, ndp)-> PRead)(ndp, paddr, filesz, (nacl_off64_t) offset); if (NaClSSizeIsNegErrno(&read_ret) || (size_t) read_ret != filesz) { NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: " "failed to read data segment\n"); return LOAD_READ_ERROR; } /* * Note that we do not need to zero the BSS (the region from * p_filesz to p_memsz) because it should already be zero * filled. This would not be the case if we were mapping the * data segment from the file. */ if (0 == (php->p_flags & PF_W)) { /* Handle read-only data segment. */ int rc = NaClMprotect(paddr, mapping_size, NACL_ABI_PROT_READ); if (0 != rc) { NaClLog(LOG_ERROR, "NaClElfImageLoadDynamically: " "failed to mprotect read-only data segment\n"); return LOAD_MPROTECT_FAIL; } NaClVmmapAddWithOverwrite(&nap->mem_map, vaddr >> NACL_PAGESHIFT, mapping_size >> NACL_PAGESHIFT, NACL_ABI_PROT_READ, NACL_ABI_MAP_PRIVATE, NULL, 0, 0); } } }
int main(int argc, char **argv) { char *nacl_file; struct NaClApp state; struct NaClApp *nap = &state; struct NaClAppThread nat, *natp = &nat; int errcode; uint32_t initial_addr; uint32_t addr; struct NaClVmmap *mem_map; struct NaClVmmapEntry *ent; char *nacl_verbosity = getenv("NACLVERBOSITY"); NaClHandleBootstrapArgs(&argc, &argv); if (argc < 2) { printf("No nexe file!\n\nFAIL\n"); } nacl_file = argv[1]; NaClAllModulesInit(); NaClLogSetVerbosity((NULL == nacl_verbosity) ? 0 : strtol(nacl_verbosity, (char **) 0, 0)); errcode = NaClAppCtor(&state); ASSERT_NE(errcode, 0); errcode = NaClAppLoadFileFromFilename(nap, nacl_file); ASSERT_EQ(errcode, LOAD_OK); InitThread(&state, natp); /* * Initial mappings: * 0. -- Zero page * 1. rx Static code segment * 2. r Read-only data segment * 3. rw Writable data segment * 4. rw Stack * There is no dynamic code area in this case. */ /* Check the initial mappings. */ mem_map = &state.mem_map; ASSERT_EQ(mem_map->nvalid, 5); CheckLowerMappings(mem_map); /* Allocate range */ addr = NaClSysMmapIntern(nap, 0, 3 * NACL_MAP_PAGESIZE, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE, -1, 0); printf("addr=0x%"NACL_PRIx32"\n", addr); initial_addr = addr; /* * The mappings have changed to become: * 0. -- Zero page * 1. rx Static code segment * 2. r Read-only data segment * 3. rw Writable data segment * 4. rw mmap()'d anonymous, 3 pages (new) * 5. rw Stack */ /* Map to overwrite the start of the previously allocated range */ addr = NaClSysMmapIntern(nap, (void *) (uintptr_t) initial_addr, 2 * NACL_MAP_PAGESIZE, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_FIXED, -1, 0); printf("addr=0x%"NACL_PRIx32"\n", addr); ASSERT_EQ(addr, initial_addr); /* * The mappings have changed to become: * 0. -- Zero page * 1. rx Static code segment * 2. r Read-only data segment * 3. rw Writable data segment * 4. rw mmap()'d anonymous, 2 pages (new) * 5. rw mmap()'d anonymous, 1 pages (previous) * 6. rw Stack */ /* Allocate new page */ addr = NaClSysMmapIntern(nap, 0, NACL_MAP_PAGESIZE, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE, -1, 0); printf("addr=0x%"NACL_PRIx32"\n", addr); /* * Our allocation strategy is to scan down from stack. This is an * implementation detail and not part of the guaranteed semantics, * but it is good to test that what we expect of our implementation * didn't change. */ ASSERT_EQ_MSG(addr, initial_addr - NACL_MAP_PAGESIZE, "Allocation strategy changed!"); /* * The mappings have changed to become: * 0. -- Zero page * 1. rx Static code segment * 2. r Read-only data segment * 3. rw Writable data segment * 4. rw mmap()'d anonymous, 1 pages (new) * 5. rw mmap()'d anonymous, 2 pages * 6. rw mmap()'d anonymous, 1 pages * 7. rw Stack */ NaClVmmapMakeSorted(mem_map); ASSERT_EQ(mem_map->nvalid, 8); CheckLowerMappings(mem_map); NaClVmmapDebug(mem_map, "After allocations"); /* Skip mappings 0, 1, 2 and 3. */ ASSERT_EQ(mem_map->vmentry[4]->page_num, (initial_addr - NACL_MAP_PAGESIZE) >> NACL_PAGESHIFT); ASSERT_EQ(mem_map->vmentry[4]->npages, NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[5]->page_num, initial_addr >> NACL_PAGESHIFT); ASSERT_EQ(mem_map->vmentry[5]->npages, 2 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[6]->page_num, (initial_addr + 2 * NACL_MAP_PAGESIZE) >> NACL_PAGESHIFT); ASSERT_EQ(mem_map->vmentry[6]->npages, NACL_PAGES_PER_MAP); /* * Undo effects of previous mmaps */ errcode = NaClSysMunmap(natp, (void *) (uintptr_t) (initial_addr - NACL_MAP_PAGESIZE), NACL_MAP_PAGESIZE * 4); ASSERT_EQ(errcode, 0); /* * Mappings return to being: * 0. -- Zero page * 1. rx Static code segment * 2. r Read-only data segment * 3. rw Writable data segment * 4. rw Stack */ ASSERT_EQ(mem_map->nvalid, 5); CheckLowerMappings(mem_map); /* Allocate range */ addr = NaClSysMmapIntern(nap, 0, 9 * NACL_MAP_PAGESIZE, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE, -1, 0); printf("addr=0x%"NACL_PRIx32"\n", addr); initial_addr = addr; /* * The mappings have changed to become: * 0. -- Zero page * 1. rx Static code segment * 2. r Read-only data segment * 3. rw Writable data segment * 4. rw mmap()'d anonymous, 9 pages (new) * 5. rw Stack */ /* Map into middle of previously allocated range */ addr = NaClSysMmapIntern(nap, (void *) (uintptr_t) (initial_addr + 2 * NACL_MAP_PAGESIZE), 3 * NACL_MAP_PAGESIZE, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_FIXED, -1, 0); printf("addr=0x%"NACL_PRIx32"\n", addr); ASSERT_EQ(addr, initial_addr + NACL_MAP_PAGESIZE * 2); /* * The mappings have changed to become: * 0. -- Zero page * 1. rx Static code segment * 2. r Read-only data segment * 3. rw Writable data segment * 4. rw mmap()'d anonymous, 2 pages (previous) * 5. rw mmap()'d anonymous, 3 pages (new) * 6. rw mmap()'d anonymous, 4 pages (previous) * 7. rw Stack */ NaClVmmapMakeSorted(mem_map); ASSERT_EQ(mem_map->nvalid, 8); CheckLowerMappings(mem_map); ASSERT_EQ(mem_map->vmentry[4]->page_num, initial_addr >> NACL_PAGESHIFT); ASSERT_EQ(mem_map->vmentry[4]->npages, 2 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[5]->page_num, (initial_addr + 2 * NACL_MAP_PAGESIZE) >> NACL_PAGESHIFT); ASSERT_EQ(mem_map->vmentry[5]->npages, 3 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[6]->page_num, (initial_addr + 5 * NACL_MAP_PAGESIZE) >> NACL_PAGESHIFT); ASSERT_EQ(mem_map->vmentry[6]->npages, 4 * NACL_PAGES_PER_MAP); /* Change the memory protection of previously allocated range */ errcode = NaClSysMprotectInternal(nap, (initial_addr + 1 * NACL_MAP_PAGESIZE), 5 * NACL_MAP_PAGESIZE, NACL_ABI_PROT_READ); ASSERT_EQ(errcode, 0); /* * The mappings have changed to become: * 0. -- Zero page * 1. rx Static code segment * 2. r Read-only data segment * 3. rw Writable data segment * 4. rw mmap()'d anonymous, 1 pages (previous) * 5. r mmap()'d anonymous, 1 pages (new) * 6. r mmap()'d anonymous, 3 pages (new) * 7. r mmap()'d anonymous, 1 pages (new) * 8. rw mmap()'d anonymous, 3 pages (previous) * 9. rw Stack */ NaClVmmapMakeSorted(mem_map); ASSERT_EQ(mem_map->nvalid, 10); CheckLowerMappings(mem_map); ASSERT_EQ(mem_map->vmentry[4]->npages, 1 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[4]->prot, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE); ASSERT_EQ(mem_map->vmentry[5]->npages, 1 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[5]->prot, NACL_ABI_PROT_READ); ASSERT_EQ(mem_map->vmentry[6]->npages, 3 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[6]->prot, NACL_ABI_PROT_READ); ASSERT_EQ(mem_map->vmentry[7]->npages, 1 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[7]->prot, NACL_ABI_PROT_READ); ASSERT_EQ(mem_map->vmentry[8]->npages, 3 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[8]->prot, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE); /* Change the memory protection of previously allocated range */ errcode = NaClSysMprotectInternal(nap, (initial_addr + 2 * NACL_MAP_PAGESIZE), 3 * NACL_MAP_PAGESIZE, NACL_ABI_PROT_NONE); ASSERT_EQ(errcode, 0); /* * The mappings have changed to become: * 0. -- Zero page * 1. rx Static code segment * 2. r Read-only data segment * 3. rw Writable data segment * 4. rw mmap()'d anonymous, 1 pages (previous) * 5. r mmap()'d anonymous, 1 pages (previous) * 6. -- mmap()'d anonymous, 3 pages (new) * 7. r mmap()'d anonymous, 1 pages (previous) * 8. rw mmap()'d anonymous, 3 pages (previous) * 9. rw Stack */ NaClVmmapMakeSorted(mem_map); ASSERT_EQ(mem_map->nvalid, 10); CheckLowerMappings(mem_map); ASSERT_EQ(mem_map->vmentry[4]->npages, 1 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[4]->prot, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE); ASSERT_EQ(mem_map->vmentry[5]->npages, 1 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[5]->prot, NACL_ABI_PROT_READ); ASSERT_EQ(mem_map->vmentry[6]->npages, 3 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[6]->prot, NACL_ABI_PROT_NONE); ASSERT_EQ(mem_map->vmentry[7]->npages, 1 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[7]->prot, NACL_ABI_PROT_READ); ASSERT_EQ(mem_map->vmentry[8]->npages, 3 * NACL_PAGES_PER_MAP); ASSERT_EQ(mem_map->vmentry[8]->prot, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE); /* * Undo effects of previous mmaps */ errcode = NaClSysMunmap(natp, (void *) (uintptr_t) initial_addr, 9 * NACL_MAP_PAGESIZE); ASSERT_EQ(errcode, 0); ASSERT_EQ(mem_map->nvalid, 5); CheckLowerMappings(mem_map); /* * Mappings return to being: * 0. -- Zero page * 1. rx Static code segment * 2. r Read-only data segment * 3. rw Writable data segment * 4. rw Stack */ /* * Check use of hint. */ addr = NaClSysMmapIntern(nap, (void *) (uintptr_t) initial_addr, NACL_MAP_PAGESIZE, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE, -1, 0); ASSERT_LE(addr, 0xffff0000u); printf("addr=0x%"NACL_PRIx32"\n", addr); ASSERT_LE_MSG(initial_addr, addr, "returned address not at or above hint"); errcode = NaClSysMunmap(natp, (void *) (uintptr_t) addr, NACL_MAP_PAGESIZE); ASSERT_EQ(errcode, 0); /* Check handling of zero-sized mappings. */ addr = NaClSysMmapIntern(nap, 0, 0, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE, -1, 0); ASSERT_EQ((int) addr, -NACL_ABI_EINVAL); errcode = NaClSysMunmap(natp, (void *) (uintptr_t) initial_addr, 0); ASSERT_EQ(errcode, -NACL_ABI_EINVAL); /* Check changing the memory protection of neighbouring mmaps */ addr = NaClSysMmapIntern(nap, 0, NACL_MAP_PAGESIZE, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE, -1, 0); printf("addr=0x%"NACL_PRIx32"\n", addr); initial_addr = addr; addr = NaClSysMmapIntern(nap, (void *) (uintptr_t) (initial_addr + NACL_MAP_PAGESIZE), NACL_MAP_PAGESIZE, NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE, NACL_ABI_MAP_ANONYMOUS | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_FIXED, -1, 0); printf("addr=0x%"NACL_PRIx32"\n", addr); ASSERT_EQ(addr, initial_addr + NACL_MAP_PAGESIZE); errcode = NaClSysMprotectInternal(nap, initial_addr, 2 * NACL_MAP_PAGESIZE, NACL_ABI_PROT_READ); ASSERT_EQ(errcode, 0); /* Undo effects of previous mmaps */ errcode = NaClSysMunmap(natp, (void *) (uintptr_t) initial_addr, 2 * NACL_MAP_PAGESIZE); ASSERT_EQ(errcode, 0); /* Check that we cannot make the read-only data segment writable */ ent = mem_map->vmentry[2]; errcode = NaClSysMprotectInternal(nap, (uint32_t) (ent->page_num << NACL_PAGESHIFT), ent->npages * NACL_MAP_PAGESIZE, NACL_ABI_PROT_WRITE); ASSERT_EQ(errcode, -NACL_ABI_EACCES); #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64 CheckForGuardRegion(nap->mem_start - ((size_t) 40 << 30), (size_t) 40 << 30); CheckForGuardRegion(nap->mem_start + ((size_t) 4 << 30), (size_t) 40 << 30); #endif NaClAddrSpaceFree(nap); printf("PASS\n"); return 0; }