diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp index 901f49285..6ffdc0de1 100644 --- a/libunwindstack/DwarfMemory.cpp +++ b/libunwindstack/DwarfMemory.cpp @@ -27,7 +27,7 @@ namespace unwindstack { bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) { - if (!memory_->Read(cur_offset_, dst, num_bytes)) { + if (!memory_->ReadFully(cur_offset_, dst, num_bytes)) { return false; } cur_offset_ += num_bytes; diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp index b3fd0df91..3b3d340f4 100644 --- a/libunwindstack/DwarfOp.cpp +++ b/libunwindstack/DwarfOp.cpp @@ -141,7 +141,7 @@ bool DwarfOp::op_deref() { // Read the address and dereference it. AddressType addr = StackPop(); AddressType value; - if (!regular_memory()->Read(addr, &value, sizeof(value))) { + if (!regular_memory()->ReadFully(addr, &value, sizeof(value))) { last_error_ = DWARF_ERROR_MEMORY_INVALID; return false; } @@ -159,7 +159,7 @@ bool DwarfOp::op_deref_size() { // Read the address and dereference it. AddressType addr = StackPop(); AddressType value = 0; - if (!regular_memory()->Read(addr, &value, bytes_to_read)) { + if (!regular_memory()->ReadFully(addr, &value, bytes_to_read)) { last_error_ = DWARF_ERROR_MEMORY_INVALID; return false; } diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp index 805dcd34b..095418726 100644 --- a/libunwindstack/DwarfSection.cpp +++ b/libunwindstack/DwarfSection.cpp @@ -142,7 +142,7 @@ bool DwarfSectionImpl::Eval(const DwarfCie* cie, Memory* regular_me return false; } if (loc->type == DWARF_LOCATION_EXPRESSION) { - if (!regular_memory->Read(value, &cfa, sizeof(AddressType))) { + if (!regular_memory->ReadFully(value, &cfa, sizeof(AddressType))) { last_error_ = DWARF_ERROR_MEMORY_INVALID; return false; } @@ -175,7 +175,8 @@ bool DwarfSectionImpl::Eval(const DwarfCie* cie, Memory* regular_me const DwarfLocation* loc = &entry.second; switch (loc->type) { case DWARF_LOCATION_OFFSET: - if (!regular_memory->Read(cfa + loc->values[0], &(*cur_regs)[reg], sizeof(AddressType))) { + if (!regular_memory->ReadFully(cfa + loc->values[0], &(*cur_regs)[reg], + sizeof(AddressType))) { last_error_ = DWARF_ERROR_MEMORY_INVALID; return false; } @@ -210,7 +211,7 @@ bool DwarfSectionImpl::Eval(const DwarfCie* cie, Memory* regular_me return false; } if (loc->type == DWARF_LOCATION_EXPRESSION) { - if (!regular_memory->Read(value, &(*cur_regs)[reg], sizeof(AddressType))) { + if (!regular_memory->ReadFully(value, &(*cur_regs)[reg], sizeof(AddressType))) { last_error_ = DWARF_ERROR_MEMORY_INVALID; return false; } diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp index 48e33ee2b..97ade56fc 100644 --- a/libunwindstack/Elf.cpp +++ b/libunwindstack/Elf.cpp @@ -136,7 +136,7 @@ bool Elf::IsValidElf(Memory* memory) { // Verify that this is a valid elf file. uint8_t e_ident[SELFMAG + 1]; - if (!memory->Read(0, e_ident, SELFMAG)) { + if (!memory->ReadFully(0, e_ident, SELFMAG)) { return false; } @@ -156,7 +156,7 @@ void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) { // Now read the section header information. uint8_t class_type; - if (!memory->Read(EI_CLASS, &class_type, 1)) { + if (!memory->ReadFully(EI_CLASS, &class_type, 1)) { return; } if (class_type == ELFCLASS32) { @@ -174,12 +174,12 @@ ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) { } std::unique_ptr interface; - if (!memory->Read(EI_CLASS, &class_type_, 1)) { + if (!memory->ReadFully(EI_CLASS, &class_type_, 1)) { return nullptr; } if (class_type_ == ELFCLASS32) { Elf32_Half e_machine; - if (!memory->Read(EI_NIDENT + sizeof(Elf32_Half), &e_machine, sizeof(e_machine))) { + if (!memory->ReadFully(EI_NIDENT + sizeof(Elf32_Half), &e_machine, sizeof(e_machine))) { return nullptr; } @@ -200,7 +200,7 @@ ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) { } } else if (class_type_ == ELFCLASS64) { Elf64_Half e_machine; - if (!memory->Read(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) { + if (!memory->ReadFully(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) { return nullptr; } if (e_machine != EM_AARCH64 && e_machine != EM_X86_64) { diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp index d5d158f82..9bdb09402 100644 --- a/libunwindstack/ElfInterface.cpp +++ b/libunwindstack/ElfInterface.cpp @@ -53,7 +53,7 @@ Memory* ElfInterface::CreateGnuDebugdataMemory() { Crc64GenerateTable(); std::vector src(gnu_debugdata_size_); - if (!memory_->Read(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) { + if (!memory_->ReadFully(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) { gnu_debugdata_offset_ = 0; gnu_debugdata_size_ = static_cast(-1); return nullptr; @@ -131,7 +131,7 @@ void ElfInterface::InitHeadersWithTemplate() { template bool ElfInterface::ReadAllHeaders(uint64_t* load_bias) { EhdrType ehdr; - if (!memory_->Read(0, &ehdr, sizeof(ehdr))) { + if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) { return false; } @@ -242,7 +242,7 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { } if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) { - if (!memory_->Read(offset, &shdr, sizeof(shdr))) { + if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) { return false; } // Need to go get the information about the section that contains @@ -324,7 +324,7 @@ bool ElfInterface::GetSonameWithTemplate(std::string* soname) { uint64_t offset = dynamic_offset_; uint64_t max_offset = offset + dynamic_size_; for (uint64_t offset = dynamic_offset_; offset < max_offset; offset += sizeof(DynType)) { - if (!memory_->Read(offset, &dyn, sizeof(dyn))) { + if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) { return false; } @@ -388,7 +388,7 @@ bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* f template void ElfInterface::GetMaxSizeWithTemplate(Memory* memory, uint64_t* size) { EhdrType ehdr; - if (!memory->Read(0, &ehdr, sizeof(ehdr))) { + if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) { return; } if (ehdr.e_shnum == 0) { diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp index 541765992..8a7ad9cee 100644 --- a/libunwindstack/MapInfo.cpp +++ b/libunwindstack/MapInfo.cpp @@ -102,7 +102,7 @@ Memory* MapInfo::CreateMemory(const std::shared_ptr& process_memory) { if (!(flags & PROT_READ)) { return nullptr; } - return new MemoryRange(process_memory, start, end); + return new MemoryRange(process_memory, start, end - start, 0); } Elf* MapInfo::GetElf(const std::shared_ptr& process_memory, bool init_gnu_debugdata) { diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp index 32753df6f..247965525 100644 --- a/libunwindstack/Memory.cpp +++ b/libunwindstack/Memory.cpp @@ -32,14 +32,69 @@ #include "Check.h" +static size_t ProcessVmRead(pid_t pid, void* dst, uint64_t remote_src, size_t len) { + struct iovec dst_iov = { + .iov_base = dst, + .iov_len = len, + }; + + // Split up the remote read across page boundaries. + // From the manpage: + // A partial read/write may result if one of the remote_iov elements points to an invalid + // memory region in the remote process. + // + // Partial transfers apply at the granularity of iovec elements. These system calls won't + // perform a partial transfer that splits a single iovec element. + constexpr size_t kMaxIovecs = 64; + struct iovec src_iovs[kMaxIovecs]; + size_t iovecs_used = 0; + + uint64_t cur = remote_src; + while (len > 0) { + if (iovecs_used == kMaxIovecs) { + errno = EINVAL; + return 0; + } + + // struct iovec uses void* for iov_base. + if (cur >= UINTPTR_MAX) { + errno = EFAULT; + return 0; + } + + src_iovs[iovecs_used].iov_base = reinterpret_cast(cur); + + uintptr_t misalignment = cur & (getpagesize() - 1); + size_t iov_len = getpagesize() - misalignment; + iov_len = std::min(iov_len, len); + + len -= iov_len; + if (__builtin_add_overflow(cur, iov_len, &cur)) { + errno = EFAULT; + return 0; + } + + src_iovs[iovecs_used].iov_len = iov_len; + ++iovecs_used; + } + + ssize_t rc = process_vm_readv(pid, &dst_iov, 1, src_iovs, iovecs_used, 0); + return rc == -1 ? 0 : rc; +} + namespace unwindstack { +bool Memory::ReadFully(uint64_t addr, void* dst, size_t size) { + size_t rc = Read(addr, dst, size); + return rc == size; +} + bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) { string->clear(); uint64_t bytes_read = 0; while (bytes_read < max_read) { uint8_t value; - if (!Read(addr, &value, sizeof(value))) { + if (!ReadFully(addr, &value, sizeof(value))) { return false; } if (value == '\0') { @@ -59,16 +114,17 @@ std::shared_ptr Memory::CreateProcessMemory(pid_t pid) { return std::shared_ptr(new MemoryRemote(pid)); } -bool MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) { - uint64_t last_read_byte; - if (__builtin_add_overflow(size, addr, &last_read_byte)) { - return false; +size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) { + if (addr >= raw_.size()) { + return 0; } - if (last_read_byte > raw_.size()) { - return false; - } - memcpy(dst, &raw_[addr], size); - return true; + + size_t bytes_left = raw_.size() - static_cast(addr); + const unsigned char* actual_base = static_cast(raw_.data()) + addr; + size_t actual_len = std::min(bytes_left, size); + + memcpy(dst, actual_base, actual_len); + return actual_len; } uint8_t* MemoryBuffer::GetPtr(size_t offset) { @@ -129,145 +185,77 @@ bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t return true; } -bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) { - uint64_t max_size; - if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) { - return false; +size_t MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) { + if (addr >= size_) { + return 0; } - memcpy(dst, &data_[addr], size); - return true; + + size_t bytes_left = size_ - static_cast(addr); + const unsigned char* actual_base = static_cast(data_) + addr; + size_t actual_len = std::min(bytes_left, size); + + memcpy(dst, actual_base, actual_len); + return actual_len; } -bool MemoryRemote::PtraceRead(uint64_t addr, long* value) { -#if !defined(__LP64__) - // Cannot read an address greater than 32 bits. - if (addr > UINT32_MAX) { - return false; - } -#endif - // ptrace() returns -1 and sets errno when the operation fails. - // To disambiguate -1 from a valid result, we clear errno beforehand. - errno = 0; - *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast(addr), nullptr); - if (*value == -1 && errno) { - return false; - } - return true; +size_t MemoryRemote::Read(uint64_t addr, void* dst, size_t size) { + return ProcessVmRead(pid_, dst, addr, size); } -bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) { - // Make sure that there is no overflow. - uint64_t max_size; - if (__builtin_add_overflow(addr, bytes, &max_size)) { - return false; - } - - size_t bytes_read = 0; - long data; - size_t align_bytes = addr & (sizeof(long) - 1); - if (align_bytes != 0) { - if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) { - return false; - } - size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes); - memcpy(dst, reinterpret_cast(&data) + align_bytes, copy_bytes); - addr += copy_bytes; - dst = reinterpret_cast(reinterpret_cast(dst) + copy_bytes); - bytes -= copy_bytes; - bytes_read += copy_bytes; - } - - for (size_t i = 0; i < bytes / sizeof(long); i++) { - if (!PtraceRead(addr, &data)) { - return false; - } - memcpy(dst, &data, sizeof(long)); - dst = reinterpret_cast(reinterpret_cast(dst) + sizeof(long)); - addr += sizeof(long); - bytes_read += sizeof(long); - } - - size_t left_over = bytes & (sizeof(long) - 1); - if (left_over) { - if (!PtraceRead(addr, &data)) { - return false; - } - memcpy(dst, &data, left_over); - bytes_read += left_over; - } - return true; +size_t MemoryLocal::Read(uint64_t addr, void* dst, size_t size) { + return ProcessVmRead(getpid(), dst, addr, size); } -bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) { - // Make sure that there is no overflow. - uint64_t max_size; - if (__builtin_add_overflow(addr, size, &max_size)) { - return false; +MemoryRange::MemoryRange(const std::shared_ptr& memory, uint64_t begin, uint64_t length, + uint64_t offset) + : memory_(memory), begin_(begin), length_(length), offset_(offset) {} + +size_t MemoryRange::Read(uint64_t addr, void* dst, size_t size) { + if (addr < offset_) { + return 0; } - // The process_vm_readv call will not always work on remote - // processes, so only use it for reads from the current pid. - // Use this method to avoid crashes if an address is invalid since - // unwind data could try to access any part of the address space. - struct iovec local_io; - local_io.iov_base = dst; - local_io.iov_len = size; - - struct iovec remote_io; - remote_io.iov_base = reinterpret_cast(static_cast(addr)); - remote_io.iov_len = size; - - ssize_t bytes_read = process_vm_readv(getpid(), &local_io, 1, &remote_io, 1, 0); - if (bytes_read == -1) { - return false; + uint64_t read_offset = addr - offset_; + if (read_offset >= length_) { + return 0; } - return static_cast(bytes_read) == size; + + uint64_t read_length = std::min(static_cast(size), length_ - read_offset); + uint64_t read_addr; + if (__builtin_add_overflow(read_offset, begin_, &read_addr)) { + return 0; + } + + return memory_->Read(read_addr, dst, read_length); } bool MemoryOffline::Init(const std::string& file, uint64_t offset) { - if (!MemoryFileAtOffset::Init(file, offset)) { + auto memory_file = std::make_shared(); + if (!memory_file->Init(file, offset)) { return false; } + // The first uint64_t value is the start of memory. - if (!MemoryFileAtOffset::Read(0, &start_, sizeof(start_))) { + uint64_t start; + if (!memory_file->ReadFully(0, &start, sizeof(start))) { return false; } - // Subtract the first 64 bit value from the total size. - size_ -= sizeof(start_); + + uint64_t size = memory_file->Size(); + if (__builtin_sub_overflow(size, sizeof(start), &size)) { + return false; + } + + memory_ = std::make_unique(memory_file, sizeof(start), size, start); return true; } -bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) { - uint64_t max_size; - if (__builtin_add_overflow(addr, size, &max_size)) { - return false; +size_t MemoryOffline::Read(uint64_t addr, void* dst, size_t size) { + if (!memory_) { + return 0; } - uint64_t real_size; - if (__builtin_add_overflow(start_, offset_, &real_size) || - __builtin_add_overflow(real_size, size_, &real_size)) { - return false; - } - - if (addr < start_ || max_size > real_size) { - return false; - } - memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size); - return true; -} - -MemoryRange::MemoryRange(const std::shared_ptr& memory, uint64_t begin, uint64_t end) - : memory_(memory), begin_(begin), length_(end - begin) { - CHECK(end > begin); -} - -bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) { - uint64_t max_read; - if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) { - return false; - } - // The check above guarantees that addr + begin_ will not overflow. - return memory_->Read(addr + begin_, dst, size); + return memory_->Read(addr, dst, size); } } // namespace unwindstack diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp index 36b8e25f5..28a77dc16 100644 --- a/libunwindstack/Regs.cpp +++ b/libunwindstack/Regs.cpp @@ -58,7 +58,7 @@ uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { if (adjusted_rel_pc & 1) { // This is a thumb instruction, it could be 2 or 4 bytes. uint32_t value; - if (rel_pc < 5 || !elf->memory()->Read(adjusted_rel_pc - 5, &value, sizeof(value)) || + if (rel_pc < 5 || !elf->memory()->ReadFully(adjusted_rel_pc - 5, &value, sizeof(value)) || (value & 0xe000f000) != 0xe000f000) { return rel_pc - 2; } @@ -193,7 +193,7 @@ void RegsX86::SetFromRaw() { bool RegsX86::SetPcFromReturnAddress(Memory* process_memory) { // Attempt to get the return address from the top of the stack. uint32_t new_pc; - if (!process_memory->Read(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) { + if (!process_memory->ReadFully(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) { return false; } @@ -240,7 +240,7 @@ void RegsX86_64::SetFromRaw() { bool RegsX86_64::SetPcFromReturnAddress(Memory* process_memory) { // Attempt to get the return address from the top of the stack. uint64_t new_pc; - if (!process_memory->Read(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) { + if (!process_memory->ReadFully(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) { return false; } @@ -474,7 +474,7 @@ bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_mem Memory* elf_memory = elf->memory(); // Read from elf memory since it is usually more expensive to read from // process memory. - if (!elf_memory->Read(rel_pc, &data, sizeof(data))) { + if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) { return false; } @@ -493,7 +493,7 @@ bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_mem // Form 3 (thumb): // 0x77 0x27 movs r7, #77 // 0x00 0xdf svc 0 - if (!process_memory->Read(sp(), &data, sizeof(data))) { + if (!process_memory->ReadFully(sp(), &data, sizeof(data))) { return false; } if (data == 0x5ac3c35a) { @@ -517,7 +517,7 @@ bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_mem // Form 3 (thumb): // 0xad 0x27 movs r7, #ad // 0x00 0xdf svc 0 - if (!process_memory->Read(sp(), &data, sizeof(data))) { + if (!process_memory->ReadFully(sp(), &data, sizeof(data))) { return false; } if (data == sp() + 8) { @@ -532,7 +532,7 @@ bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_mem return false; } - if (!process_memory->Read(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) { + if (!process_memory->ReadFully(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) { return false; } SetFromRaw(); @@ -544,7 +544,7 @@ bool RegsArm64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_m Memory* elf_memory = elf->memory(); // Read from elf memory since it is usually more expensive to read from // process memory. - if (!elf_memory->Read(rel_pc, &data, sizeof(data))) { + if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) { return false; } @@ -557,8 +557,8 @@ bool RegsArm64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_m } // SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset. - if (!process_memory->Read(sp() + 0x80 + 0xb0 + 0x08, regs_.data(), - sizeof(uint64_t) * ARM64_REG_LAST)) { + if (!process_memory->ReadFully(sp() + 0x80 + 0xb0 + 0x08, regs_.data(), + sizeof(uint64_t) * ARM64_REG_LAST)) { return false; } @@ -571,7 +571,7 @@ bool RegsX86::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_mem Memory* elf_memory = elf->memory(); // Read from elf memory since it is usually more expensive to read from // process memory. - if (!elf_memory->Read(rel_pc, &data, sizeof(data))) { + if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) { return false; } @@ -587,7 +587,7 @@ bool RegsX86::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_mem // int signum // struct sigcontext (same format as mcontext) struct x86_mcontext_t context; - if (!process_memory->Read(sp() + 4, &context, sizeof(context))) { + if (!process_memory->ReadFully(sp() + 4, &context, sizeof(context))) { return false; } regs_[X86_REG_EBP] = context.ebp; @@ -613,12 +613,12 @@ bool RegsX86::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_mem // Get the location of the sigcontext data. uint32_t ptr; - if (!process_memory->Read(sp() + 8, &ptr, sizeof(ptr))) { + if (!process_memory->ReadFully(sp() + 8, &ptr, sizeof(ptr))) { return false; } // Only read the portion of the data structure we care about. x86_ucontext_t x86_ucontext; - if (!process_memory->Read(ptr + 0x14, &x86_ucontext.uc_mcontext, sizeof(x86_mcontext_t))) { + if (!process_memory->ReadFully(ptr + 0x14, &x86_ucontext.uc_mcontext, sizeof(x86_mcontext_t))) { return false; } SetFromUcontext(&x86_ucontext); @@ -632,12 +632,12 @@ bool RegsX86_64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_ Memory* elf_memory = elf->memory(); // Read from elf memory since it is usually more expensive to read from // process memory. - if (!elf_memory->Read(rel_pc, &data, sizeof(data)) || data != 0x0f0000000fc0c748) { + if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data)) || data != 0x0f0000000fc0c748) { return false; } uint16_t data2; - if (!elf_memory->Read(rel_pc + 8, &data2, sizeof(data2)) || data2 != 0x0f05) { + if (!elf_memory->ReadFully(rel_pc + 8, &data2, sizeof(data2)) || data2 != 0x0f05) { return false; } @@ -649,7 +649,8 @@ bool RegsX86_64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_ // Read the mcontext data from the stack. // sp points to the ucontext data structure, read only the mcontext part. x86_64_ucontext_t x86_64_ucontext; - if (!process_memory->Read(sp() + 0x28, &x86_64_ucontext.uc_mcontext, sizeof(x86_64_mcontext_t))) { + if (!process_memory->ReadFully(sp() + 0x28, &x86_64_ucontext.uc_mcontext, + sizeof(x86_64_mcontext_t))) { return false; } SetFromUcontext(&x86_64_ucontext); diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp index 42d816a02..b4b92d635 100644 --- a/libunwindstack/Symbols.cpp +++ b/libunwindstack/Symbols.cpp @@ -71,7 +71,7 @@ bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std bool return_value = false; while (cur_offset_ + entry_size_ <= end_) { SymType entry; - if (!elf_memory->Read(cur_offset_, &entry, sizeof(entry))) { + if (!elf_memory->ReadFully(cur_offset_, &entry, sizeof(entry))) { // Stop all processing, something looks like it is corrupted. cur_offset_ = UINT64_MAX; return false; diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h index 183b8993a..816315299 100644 --- a/libunwindstack/include/unwindstack/Memory.h +++ b/libunwindstack/include/unwindstack/Memory.h @@ -36,7 +36,9 @@ class Memory { virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX); - virtual bool Read(uint64_t addr, void* dst, size_t size) = 0; + virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0; + + bool ReadFully(uint64_t addr, void* dst, size_t size); inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) { if (reinterpret_cast(field) < reinterpret_cast(start)) { @@ -47,12 +49,16 @@ class Memory { return false; } // The read will check if offset + size overflows. - return Read(offset, field, size); + return ReadFully(offset, field, size); } - inline bool Read32(uint64_t addr, uint32_t* dst) { return Read(addr, dst, sizeof(uint32_t)); } + inline bool Read32(uint64_t addr, uint32_t* dst) { + return ReadFully(addr, dst, sizeof(uint32_t)); + } - inline bool Read64(uint64_t addr, uint64_t* dst) { return Read(addr, dst, sizeof(uint64_t)); } + inline bool Read64(uint64_t addr, uint64_t* dst) { + return ReadFully(addr, dst, sizeof(uint64_t)); + } }; class MemoryBuffer : public Memory { @@ -60,7 +66,7 @@ class MemoryBuffer : public Memory { MemoryBuffer() = default; virtual ~MemoryBuffer() = default; - bool Read(uint64_t addr, void* dst, size_t size) override; + size_t Read(uint64_t addr, void* dst, size_t size) override; uint8_t* GetPtr(size_t offset); @@ -79,7 +85,9 @@ class MemoryFileAtOffset : public Memory { bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX); - bool Read(uint64_t addr, void* dst, size_t size) override; + size_t Read(uint64_t addr, void* dst, size_t size) override; + + size_t Size() { return size_; } void Clear(); @@ -89,31 +97,15 @@ class MemoryFileAtOffset : public Memory { uint8_t* data_ = nullptr; }; -class MemoryOffline : public MemoryFileAtOffset { - public: - MemoryOffline() = default; - virtual ~MemoryOffline() = default; - - bool Init(const std::string& file, uint64_t offset); - - bool Read(uint64_t addr, void* dst, size_t size) override; - - private: - uint64_t start_; -}; - class MemoryRemote : public Memory { public: MemoryRemote(pid_t pid) : pid_(pid) {} virtual ~MemoryRemote() = default; - bool Read(uint64_t addr, void* dst, size_t size) override; + size_t Read(uint64_t addr, void* dst, size_t size) override; pid_t pid() { return pid_; } - protected: - virtual bool PtraceRead(uint64_t addr, long* value); - private: pid_t pid_; }; @@ -123,20 +115,38 @@ class MemoryLocal : public Memory { MemoryLocal() = default; virtual ~MemoryLocal() = default; - bool Read(uint64_t addr, void* dst, size_t size) override; + size_t Read(uint64_t addr, void* dst, size_t size) override; }; +// MemoryRange maps one address range onto another. +// The range [src_begin, src_begin + length) in the underlying Memory is mapped onto offset, +// such that range.read(offset) is equivalent to underlying.read(src_begin). class MemoryRange : public Memory { public: - MemoryRange(const std::shared_ptr& memory, uint64_t begin, uint64_t end); + MemoryRange(const std::shared_ptr& memory, uint64_t begin, uint64_t length, + uint64_t offset); virtual ~MemoryRange() = default; - bool Read(uint64_t addr, void* dst, size_t size) override; + size_t Read(uint64_t addr, void* dst, size_t size) override; private: std::shared_ptr memory_; uint64_t begin_; uint64_t length_; + uint64_t offset_; +}; + +class MemoryOffline : public Memory { + public: + MemoryOffline() = default; + virtual ~MemoryOffline() = default; + + bool Init(const std::string& file, uint64_t offset); + + size_t Read(uint64_t addr, void* dst, size_t size) override; + + private: + std::unique_ptr memory_; }; } // namespace unwindstack diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp index bdcb6526c..866b5b4ef 100644 --- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp +++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp @@ -120,14 +120,14 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) { // Read the entire file. std::vector buffer(1024); - ASSERT_TRUE(memory->Read(0, buffer.data(), 1024)); + ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 1024)); ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0); ASSERT_EQ(ELFCLASS32, buffer[EI_CLASS]); for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) { ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i; } - ASSERT_FALSE(memory->Read(1024, buffer.data(), 1)); + ASSERT_FALSE(memory->ReadFully(1024, buffer.data(), 1)); } // Verify that if the offset is non-zero and there is an elf at that @@ -141,14 +141,14 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) { // Read the valid part of the file. std::vector buffer(0x100); - ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100)); + ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100)); ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0); ASSERT_EQ(ELFCLASS64, buffer[EI_CLASS]); for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) { ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i; } - ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1)); + ASSERT_FALSE(memory->ReadFully(0x100, buffer.data(), 1)); } // Verify that if the offset is non-zero and there is an elf at that @@ -164,11 +164,11 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_e // Verify the memory is a valid elf. uint8_t e_ident[SELFMAG + 1]; - ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG)); + ASSERT_TRUE(memory->ReadFully(0, e_ident, SELFMAG)); ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG)); // Read past the end of what would normally be the size of the map. - ASSERT_TRUE(memory->Read(0x1000, e_ident, 1)); + ASSERT_TRUE(memory->ReadFully(0x1000, e_ident, 1)); } TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) { @@ -180,11 +180,11 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_e // Verify the memory is a valid elf. uint8_t e_ident[SELFMAG + 1]; - ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG)); + ASSERT_TRUE(memory->ReadFully(0, e_ident, SELFMAG)); ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG)); // Read past the end of what would normally be the size of the map. - ASSERT_TRUE(memory->Read(0x1000, e_ident, 1)); + ASSERT_TRUE(memory->ReadFully(0x1000, e_ident, 1)); } // Verify that device file names will never result in Memory object creation. @@ -221,13 +221,13 @@ TEST_F(MapInfoCreateMemoryTest, process_memory) { ASSERT_TRUE(memory.get() != nullptr); memset(buffer.data(), 0, buffer.size()); - ASSERT_TRUE(memory->Read(0, buffer.data(), buffer.size())); + ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size())); for (size_t i = 0; i < buffer.size(); i++) { ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i; } // Try to read outside of the map size. - ASSERT_FALSE(memory->Read(buffer.size(), buffer.data(), 1)); + ASSERT_FALSE(memory->ReadFully(buffer.size(), buffer.data(), 1)); } } // namespace unwindstack diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp index 997379480..948597b8e 100644 --- a/libunwindstack/tests/MapInfoGetElfTest.cpp +++ b/libunwindstack/tests/MapInfoGetElfTest.cpp @@ -216,13 +216,13 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) { // Read the entire file. memset(buffer.data(), 0, buffer.size()); - ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), buffer.size())); + ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), buffer.size())); ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr))); for (size_t i = sizeof(ehdr); i < buffer.size(); i++) { ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i; } - ASSERT_FALSE(elf->memory()->Read(buffer.size(), buffer.data(), 1)); + ASSERT_FALSE(elf->memory()->ReadFully(buffer.size(), buffer.data(), 1)); } // Verify that if the offset is non-zero and there is an elf at that @@ -244,13 +244,13 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) { ASSERT_EQ(0U, info.elf_offset); // Read the valid part of the file. - ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000)); + ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000)); ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr))); for (size_t i = sizeof(ehdr); i < 0x1000; i++) { ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i; } - ASSERT_FALSE(elf->memory()->Read(0x1000, buffer.data(), 1)); + ASSERT_FALSE(elf->memory()->ReadFully(0x1000, buffer.data(), 1)); } // Verify that if the offset is non-zero and there is an elf at that @@ -278,11 +278,11 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) // Verify the memory is a valid elf. memset(buffer.data(), 0, buffer.size()); - ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000)); + ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000)); ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr))); // Read past the end of what would normally be the size of the map. - ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1)); + ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1)); } TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) { @@ -306,11 +306,11 @@ TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) // Verify the memory is a valid elf. memset(buffer.data(), 0, buffer.size()); - ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000)); + ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000)); ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr))); // Read past the end of what would normally be the size of the map. - ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1)); + ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1)); } TEST_F(MapInfoGetElfTest, process_memory_not_read_only) { diff --git a/libunwindstack/tests/MemoryBufferTest.cpp b/libunwindstack/tests/MemoryBufferTest.cpp index 50a8a1b6c..28e0e76b9 100644 --- a/libunwindstack/tests/MemoryBufferTest.cpp +++ b/libunwindstack/tests/MemoryBufferTest.cpp @@ -36,7 +36,7 @@ class MemoryBufferTest : public ::testing::Test { TEST_F(MemoryBufferTest, empty) { ASSERT_EQ(0U, memory_->Size()); std::vector buffer(1024); - ASSERT_FALSE(memory_->Read(0, buffer.data(), 1)); + ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 1)); ASSERT_EQ(nullptr, memory_->GetPtr(0)); ASSERT_EQ(nullptr, memory_->GetPtr(1)); } @@ -55,7 +55,7 @@ TEST_F(MemoryBufferTest, write_read) { } std::vector buffer(memory_->Size()); - ASSERT_TRUE(memory_->Read(0, buffer.data(), buffer.size())); + ASSERT_TRUE(memory_->ReadFully(0, buffer.data(), buffer.size())); for (size_t i = 0; i < buffer.size(); i++) { ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i; } @@ -64,18 +64,38 @@ TEST_F(MemoryBufferTest, write_read) { TEST_F(MemoryBufferTest, read_failures) { memory_->Resize(100); std::vector buffer(200); - ASSERT_FALSE(memory_->Read(0, buffer.data(), 101)); - ASSERT_FALSE(memory_->Read(100, buffer.data(), 1)); - ASSERT_FALSE(memory_->Read(101, buffer.data(), 2)); - ASSERT_FALSE(memory_->Read(99, buffer.data(), 2)); - ASSERT_TRUE(memory_->Read(99, buffer.data(), 1)); + ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 101)); + ASSERT_FALSE(memory_->ReadFully(100, buffer.data(), 1)); + ASSERT_FALSE(memory_->ReadFully(101, buffer.data(), 2)); + ASSERT_FALSE(memory_->ReadFully(99, buffer.data(), 2)); + ASSERT_TRUE(memory_->ReadFully(99, buffer.data(), 1)); } TEST_F(MemoryBufferTest, read_failure_overflow) { memory_->Resize(100); std::vector buffer(200); - ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200)); + ASSERT_FALSE(memory_->ReadFully(UINT64_MAX - 100, buffer.data(), 200)); +} + +TEST_F(MemoryBufferTest, Read) { + memory_->Resize(256); + ASSERT_EQ(256U, memory_->Size()); + ASSERT_TRUE(memory_->GetPtr(0) != nullptr); + ASSERT_TRUE(memory_->GetPtr(1) != nullptr); + ASSERT_TRUE(memory_->GetPtr(255) != nullptr); + ASSERT_TRUE(memory_->GetPtr(256) == nullptr); + + uint8_t* data = memory_->GetPtr(0); + for (size_t i = 0; i < memory_->Size(); i++) { + data[i] = i; + } + + std::vector buffer(memory_->Size()); + ASSERT_EQ(128U, memory_->Read(128, buffer.data(), buffer.size())); + for (size_t i = 0; i < 128; i++) { + ASSERT_EQ(128 + i, buffer[i]) << "Failed at byte " << i; + } } } // namespace unwindstack diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp index 2026acc00..60936cd03 100644 --- a/libunwindstack/tests/MemoryFake.cpp +++ b/libunwindstack/tests/MemoryFake.cpp @@ -35,16 +35,16 @@ void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) { } } -bool MemoryFake::Read(uint64_t addr, void* memory, size_t size) { +size_t MemoryFake::Read(uint64_t addr, void* memory, size_t size) { uint8_t* dst = reinterpret_cast(memory); for (size_t i = 0; i < size; i++, addr++) { auto value = data_.find(addr); if (value == data_.end()) { - return false; + return i; } dst[i] = value->second; } - return true; + return size; } } // namespace unwindstack diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h index d37426155..764a6c33f 100644 --- a/libunwindstack/tests/MemoryFake.h +++ b/libunwindstack/tests/MemoryFake.h @@ -32,7 +32,7 @@ class MemoryFake : public Memory { MemoryFake() = default; virtual ~MemoryFake() = default; - bool Read(uint64_t addr, void* buffer, size_t size) override; + size_t Read(uint64_t addr, void* buffer, size_t size) override; void SetMemory(uint64_t addr, const void* memory, size_t length); @@ -71,21 +71,9 @@ class MemoryFakeAlwaysReadZero : public Memory { MemoryFakeAlwaysReadZero() = default; virtual ~MemoryFakeAlwaysReadZero() = default; - bool Read(uint64_t, void* buffer, size_t size) override { + size_t Read(uint64_t, void* buffer, size_t size) override { memset(buffer, 0, size); - return true; - } -}; - -class MemoryFakeRemote : public MemoryRemote { - public: - MemoryFakeRemote() : MemoryRemote(0) {} - virtual ~MemoryFakeRemote() = default; - - protected: - bool PtraceRead(uint64_t, long* value) override { - *value = 0; - return true; + return size; } }; diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp index a204baec1..d7d1ace64 100644 --- a/libunwindstack/tests/MemoryFileTest.cpp +++ b/libunwindstack/tests/MemoryFileTest.cpp @@ -49,7 +49,7 @@ TEST_F(MemoryFileTest, init_offset_0) { ASSERT_TRUE(memory_.Init(tf_->path, 0)); std::vector buffer(11); - ASSERT_TRUE(memory_.Read(0, buffer.data(), 10)); + ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10)); buffer[10] = '\0'; ASSERT_STREQ("0123456789", buffer.data()); } @@ -59,7 +59,7 @@ TEST_F(MemoryFileTest, init_offset_non_zero) { ASSERT_TRUE(memory_.Init(tf_->path, 10)); std::vector buffer(11); - ASSERT_TRUE(memory_.Read(0, buffer.data(), 10)); + ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10)); buffer[10] = '\0'; ASSERT_STREQ("abcdefghij", buffer.data()); } @@ -75,7 +75,7 @@ TEST_F(MemoryFileTest, init_offset_non_zero_larger_than_pagesize) { ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 15)); std::vector buffer(9); - ASSERT_TRUE(memory_.Read(0, buffer.data(), 8)); + ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 8)); buffer[8] = '\0'; ASSERT_STREQ("abcdefgh", buffer.data()); } @@ -91,7 +91,7 @@ TEST_F(MemoryFileTest, init_offset_pagesize_aligned) { ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize)); std::vector buffer(11); - ASSERT_TRUE(memory_.Read(0, buffer.data(), 10)); + ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10)); buffer[10] = '\0'; std::string expected_str; for (size_t i = 0; i < 5; i++) { @@ -112,7 +112,7 @@ TEST_F(MemoryFileTest, init_offset_pagesize_aligned_plus_extra) { ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize + 10)); std::vector buffer(11); - ASSERT_TRUE(memory_.Read(0, buffer.data(), 10)); + ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10)); buffer[10] = '\0'; std::string expected_str; for (size_t i = 0; i < 5; i++) { @@ -149,19 +149,19 @@ TEST_F(MemoryFileTest, read_error) { std::vector buffer(100); // Read before init. - ASSERT_FALSE(memory_.Read(0, buffer.data(), 10)); + ASSERT_FALSE(memory_.ReadFully(0, buffer.data(), 10)); ASSERT_TRUE(memory_.Init(tf_->path, 0)); - ASSERT_FALSE(memory_.Read(10000, buffer.data(), 10)); - ASSERT_FALSE(memory_.Read(5000, buffer.data(), 10)); - ASSERT_FALSE(memory_.Read(4990, buffer.data(), 11)); - ASSERT_TRUE(memory_.Read(4990, buffer.data(), 10)); - ASSERT_FALSE(memory_.Read(4999, buffer.data(), 2)); - ASSERT_TRUE(memory_.Read(4999, buffer.data(), 1)); + ASSERT_FALSE(memory_.ReadFully(10000, buffer.data(), 10)); + ASSERT_FALSE(memory_.ReadFully(5000, buffer.data(), 10)); + ASSERT_FALSE(memory_.ReadFully(4990, buffer.data(), 11)); + ASSERT_TRUE(memory_.ReadFully(4990, buffer.data(), 10)); + ASSERT_FALSE(memory_.ReadFully(4999, buffer.data(), 2)); + ASSERT_TRUE(memory_.ReadFully(4999, buffer.data(), 1)); // Check that overflow fails properly. - ASSERT_FALSE(memory_.Read(UINT64_MAX - 100, buffer.data(), 200)); + ASSERT_FALSE(memory_.ReadFully(UINT64_MAX - 100, buffer.data(), 200)); } TEST_F(MemoryFileTest, read_past_file_within_mapping) { @@ -178,7 +178,8 @@ TEST_F(MemoryFileTest, read_past_file_within_mapping) { for (size_t i = 0; i < 100; i++) { uint8_t value; - ASSERT_FALSE(memory_.Read(buffer.size() + i, &value, 1)) << "Should have failed at value " << i; + ASSERT_FALSE(memory_.ReadFully(buffer.size() + i, &value, 1)) + << "Should have failed at value " << i; } } @@ -195,8 +196,8 @@ TEST_F(MemoryFileTest, map_partial_offset_aligned) { std::vector read_buffer(pagesize * 2); // Make sure that reading after mapped data is a failure. - ASSERT_FALSE(memory_.Read(pagesize * 2, read_buffer.data(), 1)); - ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 2)); + ASSERT_FALSE(memory_.ReadFully(pagesize * 2, read_buffer.data(), 1)); + ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 2)); for (size_t i = 0; i < pagesize; i++) { ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i; } @@ -219,8 +220,8 @@ TEST_F(MemoryFileTest, map_partial_offset_unaligned) { std::vector read_buffer(pagesize * 2); // Make sure that reading after mapped data is a failure. - ASSERT_FALSE(memory_.Read(pagesize * 2, read_buffer.data(), 1)); - ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 2)); + ASSERT_FALSE(memory_.ReadFully(pagesize * 2, read_buffer.data(), 1)); + ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 2)); for (size_t i = 0; i < pagesize - 0x100; i++) { ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i; } @@ -245,8 +246,8 @@ TEST_F(MemoryFileTest, map_overflow) { ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 0x100, UINT64_MAX)); std::vector read_buffer(pagesize * 10); - ASSERT_FALSE(memory_.Read(pagesize * 9 - 0x100 + 1, read_buffer.data(), 1)); - ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 9 - 0x100)); + ASSERT_FALSE(memory_.ReadFully(pagesize * 9 - 0x100 + 1, read_buffer.data(), 1)); + ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 9 - 0x100)); } TEST_F(MemoryFileTest, init_reinit) { @@ -259,14 +260,14 @@ TEST_F(MemoryFileTest, init_reinit) { ASSERT_TRUE(memory_.Init(tf_->path, 0)); std::vector read_buffer(buffer.size()); - ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize)); + ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize)); for (size_t i = 0; i < pagesize; i++) { ASSERT_EQ(1, read_buffer[i]) << "Failed at byte " << i; } // Now reinit. ASSERT_TRUE(memory_.Init(tf_->path, pagesize)); - ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize)); + ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize)); for (size_t i = 0; i < pagesize; i++) { ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i; } diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp index 73eebdd8c..5a389d0a9 100644 --- a/libunwindstack/tests/MemoryLocalTest.cpp +++ b/libunwindstack/tests/MemoryLocalTest.cpp @@ -16,6 +16,7 @@ #include #include +#include #include @@ -32,14 +33,14 @@ TEST(MemoryLocalTest, read) { MemoryLocal local; std::vector dst(1024); - ASSERT_TRUE(local.Read(reinterpret_cast(src.data()), dst.data(), 1024)); + ASSERT_TRUE(local.ReadFully(reinterpret_cast(src.data()), dst.data(), 1024)); ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024)); for (size_t i = 0; i < 1024; i++) { ASSERT_EQ(0x4cU, dst[i]); } memset(src.data(), 0x23, 512); - ASSERT_TRUE(local.Read(reinterpret_cast(src.data()), dst.data(), 1024)); + ASSERT_TRUE(local.ReadFully(reinterpret_cast(src.data()), dst.data(), 1024)); ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024)); for (size_t i = 0; i < 512; i++) { ASSERT_EQ(0x23U, dst[i]); @@ -53,8 +54,8 @@ TEST(MemoryLocalTest, read_illegal) { MemoryLocal local; std::vector dst(100); - ASSERT_FALSE(local.Read(0, dst.data(), 1)); - ASSERT_FALSE(local.Read(0, dst.data(), 100)); + ASSERT_FALSE(local.ReadFully(0, dst.data(), 1)); + ASSERT_FALSE(local.ReadFully(0, dst.data(), 100)); } TEST(MemoryLocalTest, read_overflow) { @@ -64,7 +65,47 @@ TEST(MemoryLocalTest, read_overflow) { // version will always go through the overflow check. std::vector dst(100); uint64_t value; - ASSERT_FALSE(local.Read(reinterpret_cast(&value), dst.data(), SIZE_MAX)); + ASSERT_FALSE(local.ReadFully(reinterpret_cast(&value), dst.data(), SIZE_MAX)); +} + +TEST(MemoryLocalTest, Read) { + char* mapping = static_cast( + mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + + ASSERT_NE(MAP_FAILED, mapping); + + mprotect(mapping + getpagesize(), getpagesize(), PROT_NONE); + memset(mapping + getpagesize() - 1024, 0x4c, 1024); + + MemoryLocal local; + + std::vector dst(4096); + ASSERT_EQ(1024U, local.Read(reinterpret_cast(mapping + getpagesize() - 1024), + dst.data(), 4096)); + for (size_t i = 0; i < 1024; i++) { + ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i; + } + + ASSERT_EQ(0, munmap(mapping, 2 * getpagesize())); +} + +TEST(MemoryLocalTest, read_hole) { + void* mapping = + mmap(nullptr, 3 * 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + ASSERT_NE(MAP_FAILED, mapping); + memset(mapping, 0xFF, 3 * 4096); + mprotect(static_cast(mapping) + 4096, 4096, PROT_NONE); + + MemoryLocal local; + std::vector dst(4096 * 3, 0xCC); + ASSERT_EQ(4096U, local.Read(reinterpret_cast(mapping), dst.data(), 4096 * 3)); + for (size_t i = 0; i < 4096; ++i) { + ASSERT_EQ(0xFF, dst[i]); + } + for (size_t i = 4096; i < 4096 * 3; ++i) { + ASSERT_EQ(0xCC, dst[i]); + } + ASSERT_EQ(0, munmap(mapping, 3 * 4096)); } } // namespace unwindstack diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp index 680fae9c1..cb1a0c993 100644 --- a/libunwindstack/tests/MemoryRangeTest.cpp +++ b/libunwindstack/tests/MemoryRangeTest.cpp @@ -35,10 +35,10 @@ TEST(MemoryRangeTest, read) { std::shared_ptr process_memory(memory_fake); memory_fake->SetMemory(9001, src); - MemoryRange range(process_memory, 9001, 9001 + src.size()); + MemoryRange range(process_memory, 9001, src.size(), 0); std::vector dst(1024); - ASSERT_TRUE(range.Read(0, dst.data(), src.size())); + ASSERT_TRUE(range.ReadFully(0, dst.data(), src.size())); for (size_t i = 0; i < 1024; i++) { ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i; } @@ -51,29 +51,44 @@ TEST(MemoryRangeTest, read_near_limit) { std::shared_ptr process_memory(memory_fake); memory_fake->SetMemory(1000, src); - MemoryRange range(process_memory, 1000, 2024); + MemoryRange range(process_memory, 1000, 1024, 0); std::vector dst(1024); - ASSERT_TRUE(range.Read(1020, dst.data(), 4)); + ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4)); for (size_t i = 0; i < 4; i++) { ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i; } // Verify that reads outside of the range will fail. - ASSERT_FALSE(range.Read(1020, dst.data(), 5)); - ASSERT_FALSE(range.Read(1024, dst.data(), 1)); - ASSERT_FALSE(range.Read(1024, dst.data(), 1024)); + ASSERT_FALSE(range.ReadFully(1020, dst.data(), 5)); + ASSERT_FALSE(range.ReadFully(1024, dst.data(), 1)); + ASSERT_FALSE(range.ReadFully(1024, dst.data(), 1024)); // Verify that reading up to the end works. - ASSERT_TRUE(range.Read(1020, dst.data(), 4)); + ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4)); } TEST(MemoryRangeTest, read_overflow) { std::vector buffer(100); std::shared_ptr process_memory(new MemoryFakeAlwaysReadZero); - std::unique_ptr overflow(new MemoryRange(process_memory, 100, 200)); - ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100)); + std::unique_ptr overflow(new MemoryRange(process_memory, 100, 200, 0)); + ASSERT_FALSE(overflow->ReadFully(UINT64_MAX - 10, buffer.data(), 100)); +} + +TEST(MemoryRangeTest, Read) { + std::vector src(4096); + memset(src.data(), 0x4c, 4096); + MemoryFake* memory_fake = new MemoryFake; + std::shared_ptr process_memory(memory_fake); + memory_fake->SetMemory(1000, src); + + MemoryRange range(process_memory, 1000, 1024, 0); + std::vector dst(1024); + ASSERT_EQ(4U, range.Read(1020, dst.data(), 1024)); + for (size_t i = 0; i < 4; i++) { + ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i; + } } } // namespace unwindstack diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp index a66d0c550..8aa4c3f1b 100644 --- a/libunwindstack/tests/MemoryRemoteTest.cpp +++ b/libunwindstack/tests/MemoryRemoteTest.cpp @@ -71,7 +71,7 @@ TEST_F(MemoryRemoteTest, read) { MemoryRemote remote(pid); std::vector dst(1024); - ASSERT_TRUE(remote.Read(reinterpret_cast(src.data()), dst.data(), 1024)); + ASSERT_TRUE(remote.ReadFully(reinterpret_cast(src.data()), dst.data(), 1024)); for (size_t i = 0; i < 1024; i++) { ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i; } @@ -79,6 +79,39 @@ TEST_F(MemoryRemoteTest, read) { ASSERT_TRUE(Detach(pid)); } +TEST_F(MemoryRemoteTest, Read) { + char* mapping = static_cast( + mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + + ASSERT_NE(MAP_FAILED, mapping); + + mprotect(mapping + getpagesize(), getpagesize(), PROT_NONE); + memset(mapping + getpagesize() - 1024, 0x4c, 1024); + + pid_t pid; + if ((pid = fork()) == 0) { + while (true) + ; + exit(1); + } + ASSERT_LT(0, pid); + TestScopedPidReaper reap(pid); + + ASSERT_TRUE(Attach(pid)); + + MemoryRemote remote(pid); + + std::vector dst(4096); + ASSERT_EQ(1024U, remote.Read(reinterpret_cast(mapping + getpagesize() - 1024), + dst.data(), 4096)); + for (size_t i = 0; i < 1024; i++) { + ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i; + } + + ASSERT_TRUE(Detach(pid)); + ASSERT_EQ(0, munmap(mapping, 2 * getpagesize())); +} + TEST_F(MemoryRemoteTest, read_fail) { int pagesize = getpagesize(); void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0); @@ -101,17 +134,17 @@ TEST_F(MemoryRemoteTest, read_fail) { MemoryRemote remote(pid); std::vector dst(pagesize); - ASSERT_TRUE(remote.Read(reinterpret_cast(src), dst.data(), pagesize)); + ASSERT_TRUE(remote.ReadFully(reinterpret_cast(src), dst.data(), pagesize)); for (size_t i = 0; i < 1024; i++) { ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i; } - ASSERT_FALSE(remote.Read(reinterpret_cast(src) + pagesize, dst.data(), 1)); - ASSERT_TRUE(remote.Read(reinterpret_cast(src) + pagesize - 1, dst.data(), 1)); - ASSERT_FALSE(remote.Read(reinterpret_cast(src) + pagesize - 4, dst.data(), 8)); + ASSERT_FALSE(remote.ReadFully(reinterpret_cast(src) + pagesize, dst.data(), 1)); + ASSERT_TRUE(remote.ReadFully(reinterpret_cast(src) + pagesize - 1, dst.data(), 1)); + ASSERT_FALSE(remote.ReadFully(reinterpret_cast(src) + pagesize - 4, dst.data(), 8)); // Check overflow condition is caught properly. - ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200)); + ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200)); ASSERT_EQ(0, munmap(src, pagesize)); @@ -119,11 +152,24 @@ TEST_F(MemoryRemoteTest, read_fail) { } TEST_F(MemoryRemoteTest, read_overflow) { - MemoryFakeRemote remote; + pid_t pid; + if ((pid = fork()) == 0) { + while (true) + ; + exit(1); + } + ASSERT_LT(0, pid); + TestScopedPidReaper reap(pid); + + ASSERT_TRUE(Attach(pid)); + + MemoryRemote remote(pid); // Check overflow condition is caught properly. std::vector dst(200); - ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200)); + ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200)); + + ASSERT_TRUE(Detach(pid)); } TEST_F(MemoryRemoteTest, read_illegal) { @@ -140,10 +186,38 @@ TEST_F(MemoryRemoteTest, read_illegal) { MemoryRemote remote(pid); std::vector dst(100); - ASSERT_FALSE(remote.Read(0, dst.data(), 1)); - ASSERT_FALSE(remote.Read(0, dst.data(), 100)); + ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1)); + ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100)); ASSERT_TRUE(Detach(pid)); } +TEST_F(MemoryRemoteTest, read_hole) { + void* mapping = + mmap(nullptr, 3 * 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + ASSERT_NE(MAP_FAILED, mapping); + memset(mapping, 0xFF, 3 * 4096); + mprotect(static_cast(mapping) + 4096, 4096, PROT_NONE); + + pid_t pid; + if ((pid = fork()) == 0) { + while (true); + exit(1); + } + ASSERT_LT(0, pid); + TestScopedPidReaper reap(pid); + + ASSERT_TRUE(Attach(pid)); + + MemoryRemote remote(pid); + std::vector dst(4096 * 3, 0xCC); + ASSERT_EQ(4096U, remote.Read(reinterpret_cast(mapping), dst.data(), 4096 * 3)); + for (size_t i = 0; i < 4096; ++i) { + ASSERT_EQ(0xFF, dst[i]); + } + for (size_t i = 4096; i < 4096 * 3; ++i) { + ASSERT_EQ(0xCC, dst[i]); + } +} + } // namespace unwindstack diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp index 66c8ba6c8..b372fd0d4 100644 --- a/libunwindstack/tests/UnwindTest.cpp +++ b/libunwindstack/tests/UnwindTest.cpp @@ -174,7 +174,7 @@ void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* complete MemoryRemote memory(pid); // Read the remote value to see if we are ready. bool value; - if (memory.Read(addr, &value, sizeof(value)) && value) { + if (memory.ReadFully(addr, &value, sizeof(value)) && value) { *completed = true; } if (!*completed || !leave_attached) {