ssize_t GioMemoryFileRead(struct Gio *vself, void *buf, size_t count) { struct GioMemoryFile *self = (struct GioMemoryFile *) vself; size_t remain; size_t newpos; /* 0 <= self->curpos && self->curpos <= self->len */ remain = self->len - self->curpos; /* 0 <= remain <= self->len */ if (count > remain) { count = remain; } /* 0 <= count && count <= remain */ if (0 == count) { return 0; } newpos = self->curpos + count; /* self->curpos <= newpos && newpos <= self->len */ /* Tell valgrind that this memory is accessible and undefined */ NACL_MAKE_MEM_UNDEFINED(buf, count); memcpy(buf, self->buffer + self->curpos, count); self->curpos = newpos; return count; }
NaClErrorCode NaClElfImageLoad(struct NaClElfImage *image, struct NaClDesc *ndp, struct NaClApp *nap) { int segnum; uintptr_t vaddr; uintptr_t paddr; uintptr_t end_vaddr; ssize_t read_ret; int safe_for_mmap; for (segnum = 0; segnum < image->ehdr.e_phnum; ++segnum) { const Elf_Phdr *php = &image->phdrs[segnum]; Elf_Off offset = (Elf_Off) NaClTruncAllocPage(php->p_offset); Elf_Off filesz = php->p_offset + php->p_filesz - offset; /* did we decide that we will load this segment earlier? */ if (!image->loadable[segnum]) { continue; } NaClLog(2, "loading segment %d\n", segnum); if (0 == php->p_filesz) { NaClLog(4, "zero-sized segment. ignoring...\n"); continue; } end_vaddr = php->p_vaddr + php->p_filesz; /* integer overflow? */ if (end_vaddr < php->p_vaddr) { NaClLog(LOG_FATAL, "parameter error should have been detected already\n"); } /* * is the end virtual address within the NaCl application's * address space? if it is, it implies that the start virtual * address is also. */ if (end_vaddr >= ((uintptr_t) 1U << nap->addr_bits)) { NaClLog(LOG_FATAL, "parameter error should have been detected already\n"); } vaddr = NaClTruncAllocPage(php->p_vaddr); paddr = NaClUserToSysAddr(nap, vaddr); CHECK(kNaClBadAddress != paddr); /* * Check NaClDescIsSafeForMmap(ndp) to see if it might be okay to * mmap. */ NaClLog(4, "NaClElfImageLoad: checking descriptor mmap safety\n"); safe_for_mmap = NaClDescIsSafeForMmap(ndp); if (safe_for_mmap) { NaClLog(4, "NaClElfImageLoad: safe-for-mmap\n"); } if (!safe_for_mmap && NACL_FI("ELF_LOAD_BYPASS_DESCRIPTOR_SAFETY_CHECK", 0, 1)) { NaClLog(LOG_WARNING, "WARNING: BYPASSING DESCRIPTOR SAFETY CHECK\n"); safe_for_mmap = 1; } if (safe_for_mmap) { NaClErrorCode map_status; NaClLog(4, "NaClElfImageLoad: safe-for-mmap\n"); map_status = NaClElfFileMapSegment(nap, ndp, php->p_flags, offset, filesz, vaddr, paddr); /* * NB: -Werror=switch-enum forces us to not use a switch. */ if (LOAD_OK == map_status) { /* Segment has been handled -- proceed to next segment */ continue; } else if (LOAD_STATUS_UNKNOWN != map_status) { /* * A real error! Return it so that this can be reported to * the embedding code (via start_module status). */ return map_status; } /* Fall through: pread-based fallback requested */ } NaClLog(4, "PReading %"NACL_PRIdElf_Xword" (0x%"NACL_PRIxElf_Xword") bytes to" " address 0x%"NACL_PRIxPTR", position %" NACL_PRIdElf_Off" (0x%"NACL_PRIxElf_Off")\n", filesz, filesz, paddr, offset, offset); /* * Tell valgrind that this memory is accessible and undefined. For more * details see * http://code.google.com/p/nativeclient/wiki/ValgrindMemcheck#Implementation_details */ NACL_MAKE_MEM_UNDEFINED((void *) paddr, filesz); read_ret = (*NACL_VTBL(NaClDesc, ndp)-> PRead)(ndp, (void *) paddr, filesz, (nacl_off64_t) offset); if (NaClSSizeIsNegErrno(&read_ret) || (size_t) read_ret != filesz) { NaClLog(LOG_ERROR, "load failure segment %d", segnum); return LOAD_SEGMENT_BAD_PARAM; } /* region from p_filesz to p_memsz should already be zero filled */ /* Tell Valgrind that we've mapped a segment of nacl_file. */ NaClFileMappingForValgrind(paddr, filesz, offset); } return LOAD_OK; }
/* * Attempt to map into the NaClApp object nap from the NaCl descriptor * ndp an ELF segment of type p_flags that start at file_offset for * segment_size bytes, to memory starting at paddr (system address). * If it is a code segment, make a scratch mapping and check * validation in readonly_text mode -- if it succeeds, we map into the * target address; if it fails, we return failure so that pread-based * loading can proceed. For rodata and data segments, less checking * is needed. In the text and data case, the end of the segment may * not land on a NACL_MAP_PAGESIZE boundary; when this occurs, we will * map in all whole NACL_MAP_PAGESIZE chunks, and pread in the tail * partial chunk. * * Returns: LOAD_OK, LOAD_STATUS_UNKNOWN, other error codes. * * LOAD_OK -- if the segment has been fully handled * LOAD_STATUS_UNKNOWN -- if pread-based fallback is required * other error codes -- if a fatal error occurs, and the caller * should propagate up * * See NaClSysMmapIntern in nacl_syscall_common.c for corresponding * mmap syscall where PROT_EXEC allows shared libraries to be mapped * into dynamic code space. */ static NaClErrorCode NaClElfFileMapSegment(struct NaClApp *nap, struct NaClDesc *ndp, Elf_Word p_flags, Elf_Off file_offset, Elf_Off segment_size, uintptr_t vaddr, uintptr_t paddr) { size_t rounded_filesz; /* 64k rounded */ int mmap_prot = 0; uintptr_t image_sys_addr; NaClValidationStatus validator_status = NaClValidationFailed; struct NaClValidationMetadata metadata; int read_last_page_if_partial_allocation_page = 1; ssize_t read_ret; struct NaClPerfCounter time_mmap_segment; NaClPerfCounterCtor(&time_mmap_segment, "NaClElfFileMapSegment"); rounded_filesz = NaClRoundAllocPage(segment_size); NaClLog(4, "NaClElfFileMapSegment: checking segment flags 0x%x" " to determine map checks\n", p_flags); /* * Is this the text segment? If so, map into scratch memory and * run validation (possibly cached result) with !stubout_mode, * readonly_text. If validator says it's okay, map directly into * target location with NACL_ABI_PROT_READ|_EXEC. If anything * failed, fall back to PRead. NB: the assumption is that there * is only one PT_LOAD with PF_R|PF_X segment; this assumption is * enforced by phdr seen_seg checks above in * NaClElfImageValidateProgramHeaders. * * After this function returns, we will be setting memory protection * in NaClMemoryProtection, so the actual memory protection used is * immaterial. * * For rodata and data/bss, we mmap with NACL_ABI_PROT_READ or * NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE as appropriate, * without doing validation. There is no fallback to PRead, since * we don't validate the contents. */ switch (p_flags) { case PF_R | PF_X: NaClLog(4, "NaClElfFileMapSegment: text segment and" " file is safe for mmap\n"); if (NACL_VTBL(NaClDesc, ndp)->typeTag != NACL_DESC_HOST_IO) { NaClLog(4, "NaClElfFileMapSegment: not supported type, got %d\n", NACL_VTBL(NaClDesc, ndp)->typeTag); return LOAD_STATUS_UNKNOWN; } /* * Unlike the mmap case, we do not re-run validation to * allow patching here; instead, we handle validation * failure by going to the pread_fallback case. In the * future, we should consider doing an in-place mapping and * allowing HLT patch validation, which should be cheaper * since those pages that do not require patching (hopefully * majority) will remain file-backed and not require swap * space, even if we had to fault in every page. */ NaClLog(1, "NaClElfFileMapSegment: mapping for validation\n"); NaClPerfCounterMark(&time_mmap_segment, "PreMap"); NaClPerfCounterIntervalLast(&time_mmap_segment); image_sys_addr = (*NACL_VTBL(NaClDesc, ndp)-> Map)(ndp, NaClDescEffectorTrustedMem(), (void *) NULL, rounded_filesz, NACL_ABI_PROT_READ, NACL_ABI_MAP_PRIVATE, file_offset); NaClPerfCounterMark(&time_mmap_segment, "MapForValidate"); NaClPerfCounterIntervalLast(&time_mmap_segment); if (NaClPtrIsNegErrno(&image_sys_addr)) { NaClLog(LOG_INFO, "NaClElfFileMapSegment: Could not make scratch mapping," " falling back to reading\n"); return LOAD_STATUS_UNKNOWN; } /* ask validator / validation cache */ NaClMetadataFromNaClDescCtor(&metadata, ndp); CHECK(segment_size == nap->static_text_end - NACL_TRAMPOLINE_END); validator_status = NACL_FI_VAL( "ELF_LOAD_FORCE_VALIDATION_STATUS", enum NaClValidationStatus, (*nap->validator-> Validate)(vaddr, (uint8_t *) image_sys_addr, segment_size, /* actual size */ 0, /* stubout_mode: no */ 1, /* readonly_text: yes */ nap->cpu_features, &metadata, nap->validation_cache)); NaClPerfCounterMark(&time_mmap_segment, "ValidateMapped"); NaClPerfCounterIntervalLast(&time_mmap_segment); NaClLog(3, "NaClElfFileMapSegment: validator_status %d\n", validator_status); NaClMetadataDtor(&metadata); /* * Remove scratch mapping, then map directly into untrusted * address space or pread. */ NaClDescUnmapUnsafe(ndp, (void *) image_sys_addr, rounded_filesz); NACL_MAKE_MEM_UNDEFINED((void *) paddr, rounded_filesz); if (NaClValidationSucceeded != validator_status) { NaClLog(3, ("NaClElfFileMapSegment: readonly_text validation for mmap" " failed. Will retry validation allowing HALT stubbing out" " of unsupported instruction extensions.\n")); return LOAD_STATUS_UNKNOWN; } NaClLog(1, "NaClElfFileMapSegment: mapping into code space\n"); /* * Windows appears to not allow RWX mappings. This interferes * with HALT_SLED and having to HALT pad the last page. We * allow partial code pages, so * read_last_page_if_partial_allocation_page will ensure that * the last page is writable, so we will be able to write HALT * instructions as needed. */ mmap_prot = NACL_ABI_PROT_READ | NACL_ABI_PROT_EXEC; /* * NB: the log string is used by tests/mmap_main_nexe/nacl.scons * and must be logged at a level that is less than or equal to * the requested verbosity level there. */ NaClLog(1, "NaClElfFileMapSegment: EXERCISING MMAP LOAD PATH\n"); nap->main_exe_prevalidated = 1; break; case PF_R | PF_W: /* read-write (initialized data) */ mmap_prot = NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE; /* * NB: the partial page processing will result in zeros * following the initialized data, so that the BSS will be zero. * On a typical system, this page is mapped in and the BSS * region is memset to zero, which means that this partial page * is faulted in. Rather than saving a syscall (pread) and * faulting it in, we just use the same code path as for code, * which is (slightly) simpler. */ break; case PF_R: /* read-only */ mmap_prot = NACL_ABI_PROT_READ; /* * For rodata, we allow mapping in "garbage" past a partial * page; this potentially eliminates a disk I/O operation * (if data section has no partial page), possibly delaying * disk spin-up if the code was in the validation cache. * And it saves another 64kB of swap. */ read_last_page_if_partial_allocation_page = 0; break; default: NaClLog(LOG_FATAL, "NaClElfFileMapSegment: unexpected p_flags %d\n", p_flags); } if (rounded_filesz != segment_size && read_last_page_if_partial_allocation_page) { uintptr_t tail_offset = rounded_filesz - NACL_MAP_PAGESIZE; size_t tail_size = segment_size - tail_offset; NaClLog(4, "NaClElfFileMapSegment: pread tail\n"); read_ret = (*NACL_VTBL(NaClDesc, ndp)-> PRead)(ndp, (void *) (paddr + tail_offset), tail_size, (nacl_off64_t) (file_offset + tail_offset)); NaClPerfCounterMark(&time_mmap_segment, "PRead tail"); NaClPerfCounterIntervalLast(&time_mmap_segment); if (NaClSSizeIsNegErrno(&read_ret) || (size_t) read_ret != tail_size) { NaClLog(LOG_ERROR, "NaClElfFileMapSegment: pread load of page tail failed\n"); return LOAD_SEGMENT_BAD_PARAM; } rounded_filesz -= NACL_MAP_PAGESIZE; } /* mmap in */ if (rounded_filesz == 0) { NaClLog(4, "NaClElfFileMapSegment: no pages to map, probably because" " the segment was a partial page, so it was processed by" " reading.\n"); } else { NaClLog(4, "NaClElfFileMapSegment: mapping %"NACL_PRIuS" (0x%" NACL_PRIxS") bytes to" " address 0x%"NACL_PRIxPTR", position %" NACL_PRIdElf_Off" (0x%"NACL_PRIxElf_Off")\n", rounded_filesz, rounded_filesz, paddr, file_offset, file_offset); image_sys_addr = (*NACL_VTBL(NaClDesc, ndp)-> Map)(ndp, nap->effp, (void *) paddr, rounded_filesz, mmap_prot, NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_FIXED, file_offset); NaClPerfCounterMark(&time_mmap_segment, "MapFinal"); NaClPerfCounterIntervalLast(&time_mmap_segment); if (image_sys_addr != paddr) { NaClLog(LOG_FATAL, ("NaClElfFileMapSegment: map to 0x%"NACL_PRIxPTR" (prot %x) " "failed: got 0x%"NACL_PRIxPTR"\n"), paddr, mmap_prot, image_sys_addr); } /* Tell Valgrind that we've mapped a segment of nacl_file. */ NaClFileMappingForValgrind(paddr, rounded_filesz, file_offset); } return LOAD_OK; }
NaClErrorCode NaClElfImageLoad(struct NaClElfImage *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; } NaClLog(2, "loading segment %d\n", segnum); if (0 == php->p_filesz) { NaClLog(4, "zero-sized segment. ignoring...\n"); continue; } end_vaddr = php->p_vaddr + php->p_filesz; /* integer overflow? */ if (end_vaddr < php->p_vaddr) { NaClLog(LOG_FATAL, "parameter error should have been detected already\n"); } /* * is the end virtual address within the NaCl application's * address space? if it is, it implies that the start virtual * address is also. */ if (end_vaddr >= ((uintptr_t) 1U << addr_bits)) { NaClLog(LOG_FATAL, "parameter error should have been detected already\n"); } paddr = mem_start + php->p_vaddr; NaClLog(4, "Seek to position %"NACL_PRIdElf_Off" (0x%"NACL_PRIxElf_Off").\n", 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. */ if ((*gp->vtbl->Seek)(gp, (off_t) php->p_offset, SEEK_SET) == (off_t) -1) { NaClLog(LOG_ERROR, "seek failure segment %d", segnum); return LOAD_SEGMENT_BAD_PARAM; } NaClLog(4, "Reading %"NACL_PRIdElf_Xword" (0x%"NACL_PRIxElf_Xword") bytes to" " address 0x%"NACL_PRIxPTR"\n", php->p_filesz, php->p_filesz, paddr); /* * Tell valgrind that this memory is accessible and undefined. For more * details see * http://code.google.com/p/nativeclient/wiki/ValgrindMemcheck#Implementation_details */ NACL_MAKE_MEM_UNDEFINED((void *) paddr, php->p_filesz); if ((Elf_Word) (*gp->vtbl->Read)(gp, (void *) paddr, php->p_filesz) != php->p_filesz) { NaClLog(LOG_ERROR, "load failure segment %d", segnum); return LOAD_SEGMENT_BAD_PARAM; } /* region from p_filesz to p_memsz should already be zero filled */ } return LOAD_OK; }
void NaClFillMemoryRegionWithHalt(void *start, size_t size) { CHECK(!(size % NACL_HALT_LEN)); /* Tell valgrind that this memory is accessible and undefined */ NACL_MAKE_MEM_UNDEFINED(start, size); memset(start, NACL_HALT_OPCODE, size); }