Loading libunwindstack/ElfInterface.cpp +17 −17 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) { uint64_t offset = ehdr.e_phoff; for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) { PhdrType phdr; if (!memory_->Read(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) { return false; } Loading @@ -54,20 +54,20 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) { case PT_LOAD: { // Get the flags first, if this isn't an executable header, ignore it. if (!memory_->Read(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) { return false; } if ((phdr.p_flags & PF_X) == 0) { continue; } if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { return false; } if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { return false; } if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { return false; } pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr, Loading @@ -79,22 +79,22 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) { } case PT_GNU_EH_FRAME: if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { return false; } eh_frame_offset_ = phdr.p_offset; if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { return false; } eh_frame_size_ = phdr.p_memsz; break; case PT_DYNAMIC: if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { return false; } dynamic_offset_ = phdr.p_offset; if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { return false; } dynamic_size_ = phdr.p_memsz; Loading @@ -116,8 +116,8 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { ShdrType shdr; if (ehdr.e_shstrndx < ehdr.e_shnum) { uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize; if (memory_->Read(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->Read(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { if (memory_->ReadField(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->ReadField(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { sec_offset = shdr.sh_offset; sec_size = shdr.sh_size; } Loading @@ -125,27 +125,27 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { // Skip the first header, it's always going to be NULL. for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) { if (!memory_->Read(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) { if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) { return false; } if (shdr.sh_type == SHT_PROGBITS) { // Look for the .debug_frame and .gnu_debugdata. if (!memory_->Read(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) { if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) { return false; } if (shdr.sh_name < sec_size) { std::string name; if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) { if (name == ".debug_frame") { if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { debug_frame_offset_ = shdr.sh_offset; debug_frame_size_ = shdr.sh_size; } } else if (name == ".gnu_debugdata") { if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { gnu_debugdata_offset_ = shdr.sh_offset; gnu_debugdata_size_ = shdr.sh_size; } Loading libunwindstack/ElfInterfaceArm.cpp +7 −5 Original line number Diff line number Diff line Loading @@ -85,10 +85,10 @@ bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) { } Elf32_Phdr phdr; if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { return true; } if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { return true; } // The load_bias_ should always be set by this time. Loading @@ -98,13 +98,15 @@ bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) { } bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) { return StepExidx(pc, regs, process_memory) || ElfInterface32::Step(pc, regs, process_memory); // Dwarf unwind information is precise about whether a pc is covered or not, // but arm unwind information only has ranges of pc. In order to avoid // incorrectly doing a bad unwind using arm unwind information for a // different function, always try and unwind with the dwarf information first. return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory); } bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) { RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs); // First try arm, then try dwarf. uint64_t entry_offset; if (!FindEntry(pc, &entry_offset)) { return false; Loading libunwindstack/Memory.cpp +49 −10 Original line number Diff line number Diff line Loading @@ -96,10 +96,16 @@ bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t offset_ = offset & (getpagesize() - 1); uint64_t aligned_offset = offset & ~(getpagesize() - 1); if (aligned_offset > static_cast<uint64_t>(buf.st_size) || offset > static_cast<uint64_t>(buf.st_size)) { return false; } size_ = buf.st_size - aligned_offset; if (size < (UINT64_MAX - offset_) && size + offset_ < size_) { uint64_t max_size; if (!__builtin_add_overflow(size, offset_, &max_size) && max_size < size_) { // Truncate the mapped size. size_ = size + offset_; size_ = max_size; } void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset); if (map == MAP_FAILED) { Loading @@ -113,14 +119,15 @@ bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t } bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) { if (addr + size > size_) { uint64_t max_size; if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) { return false; } memcpy(dst, &data_[addr], size); return true; } static bool PtraceRead(pid_t pid, uint64_t addr, long* value) { bool MemoryRemote::PtraceRead(uint64_t addr, long* value) { #if !defined(__LP64__) // Cannot read an address greater than 32 bits. if (addr > UINT32_MAX) { Loading @@ -130,7 +137,7 @@ static bool PtraceRead(pid_t pid, uint64_t addr, long* value) { // 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<void*>(addr), nullptr); *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast<void*>(addr), nullptr); if (*value == -1 && errno) { return false; } Loading @@ -138,11 +145,17 @@ static bool PtraceRead(pid_t pid, uint64_t addr, long* value) { } 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(pid_, addr & ~(sizeof(long) - 1), &data)) { if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) { return false; } size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes); Loading @@ -154,7 +167,7 @@ bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) { } for (size_t i = 0; i < bytes / sizeof(long); i++) { if (!PtraceRead(pid_, addr, &data)) { if (!PtraceRead(addr, &data)) { return false; } memcpy(dst, &data, sizeof(long)); Loading @@ -165,7 +178,7 @@ bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) { size_t left_over = bytes & (sizeof(long) - 1); if (left_over) { if (!PtraceRead(pid_, addr, &data)) { if (!PtraceRead(addr, &data)) { return false; } memcpy(dst, &data, left_over); Loading @@ -175,7 +188,13 @@ bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) { } bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) { // The process_vm_readv call does will not always work on remote // Make sure that there is no overflow. uint64_t max_size; if (__builtin_add_overflow(addr, size, &max_size)) { return false; } // 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. Loading Loading @@ -208,9 +227,29 @@ bool MemoryOffline::Init(const std::string& file, uint64_t offset) { } bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) { if (addr < start_ || addr + size > start_ + offset_ + size_) { uint64_t max_size; if (__builtin_add_overflow(addr, size, &max_size)) { return false; } 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; } 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); } libunwindstack/Memory.h +18 −10 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #ifndef _LIBUNWINDSTACK_MEMORY_H #define _LIBUNWINDSTACK_MEMORY_H #include <assert.h> #include <stdint.h> #include <sys/types.h> #include <unistd.h> Loading @@ -33,9 +34,16 @@ class Memory { virtual bool Read(uint64_t addr, void* dst, size_t size) = 0; inline bool Read(uint64_t addr, void* start, void* field, size_t size) { return Read(addr + reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start), field, size); inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) { if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) { return false; } uint64_t offset = reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start); if (__builtin_add_overflow(addr, offset, &offset)) { return false; } // The read will check if offset + size overflows. return Read(offset, field, size); } inline bool Read32(uint64_t addr, uint32_t* dst) { Loading Loading @@ -103,6 +111,9 @@ class MemoryRemote : public Memory { pid_t pid() { return pid_; } protected: virtual bool PtraceRead(uint64_t addr, long* value); private: pid_t pid_; }; Loading @@ -118,15 +129,12 @@ class MemoryLocal : public Memory { class MemoryRange : public Memory { public: MemoryRange(Memory* memory, uint64_t begin, uint64_t end) : memory_(memory), begin_(begin), length_(end - begin_) {} : memory_(memory), begin_(begin), length_(end - begin) { assert(end > begin); } virtual ~MemoryRange() { delete memory_; } inline bool Read(uint64_t addr, void* dst, size_t size) override { if (addr + size <= length_) { return memory_->Read(addr + begin_, dst, size); } return false; } bool Read(uint64_t addr, void* dst, size_t size) override; private: Memory* memory_; Loading libunwindstack/tests/MemoryBuffer.cpp 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <vector> #include <gtest/gtest.h> #include "Memory.h" #include "LogFake.h" class MemoryBufferTest : public ::testing::Test { protected: void SetUp() override { ResetLogs(); memory_.reset(new MemoryBuffer); } std::unique_ptr<MemoryBuffer> memory_; }; TEST_F(MemoryBufferTest, empty) { ASSERT_EQ(0U, memory_->Size()); std::vector<uint8_t> buffer(1024); ASSERT_FALSE(memory_->Read(0, buffer.data(), 1)); ASSERT_EQ(nullptr, memory_->GetPtr(0)); ASSERT_EQ(nullptr, memory_->GetPtr(1)); } TEST_F(MemoryBufferTest, write_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<uint8_t> buffer(memory_->Size()); ASSERT_TRUE(memory_->Read(0, buffer.data(), buffer.size())); for (size_t i = 0; i < buffer.size(); i++) { ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i; } } TEST_F(MemoryBufferTest, read_failures) { memory_->Resize(100); std::vector<uint8_t> 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)); } TEST_F(MemoryBufferTest, read_failure_overflow) { memory_->Resize(100); std::vector<uint8_t> buffer(200); ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200)); } Loading
libunwindstack/ElfInterface.cpp +17 −17 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) { uint64_t offset = ehdr.e_phoff; for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) { PhdrType phdr; if (!memory_->Read(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) { return false; } Loading @@ -54,20 +54,20 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) { case PT_LOAD: { // Get the flags first, if this isn't an executable header, ignore it. if (!memory_->Read(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) { return false; } if ((phdr.p_flags & PF_X) == 0) { continue; } if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { return false; } if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { return false; } if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { return false; } pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr, Loading @@ -79,22 +79,22 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) { } case PT_GNU_EH_FRAME: if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { return false; } eh_frame_offset_ = phdr.p_offset; if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { return false; } eh_frame_size_ = phdr.p_memsz; break; case PT_DYNAMIC: if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { return false; } dynamic_offset_ = phdr.p_offset; if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { return false; } dynamic_size_ = phdr.p_memsz; Loading @@ -116,8 +116,8 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { ShdrType shdr; if (ehdr.e_shstrndx < ehdr.e_shnum) { uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize; if (memory_->Read(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->Read(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { if (memory_->ReadField(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->ReadField(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { sec_offset = shdr.sh_offset; sec_size = shdr.sh_size; } Loading @@ -125,27 +125,27 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { // Skip the first header, it's always going to be NULL. for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) { if (!memory_->Read(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) { if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) { return false; } if (shdr.sh_type == SHT_PROGBITS) { // Look for the .debug_frame and .gnu_debugdata. if (!memory_->Read(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) { if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) { return false; } if (shdr.sh_name < sec_size) { std::string name; if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) { if (name == ".debug_frame") { if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { debug_frame_offset_ = shdr.sh_offset; debug_frame_size_ = shdr.sh_size; } } else if (name == ".gnu_debugdata") { if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { gnu_debugdata_offset_ = shdr.sh_offset; gnu_debugdata_size_ = shdr.sh_size; } Loading
libunwindstack/ElfInterfaceArm.cpp +7 −5 Original line number Diff line number Diff line Loading @@ -85,10 +85,10 @@ bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) { } Elf32_Phdr phdr; if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { return true; } if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { return true; } // The load_bias_ should always be set by this time. Loading @@ -98,13 +98,15 @@ bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) { } bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) { return StepExidx(pc, regs, process_memory) || ElfInterface32::Step(pc, regs, process_memory); // Dwarf unwind information is precise about whether a pc is covered or not, // but arm unwind information only has ranges of pc. In order to avoid // incorrectly doing a bad unwind using arm unwind information for a // different function, always try and unwind with the dwarf information first. return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory); } bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) { RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs); // First try arm, then try dwarf. uint64_t entry_offset; if (!FindEntry(pc, &entry_offset)) { return false; Loading
libunwindstack/Memory.cpp +49 −10 Original line number Diff line number Diff line Loading @@ -96,10 +96,16 @@ bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t offset_ = offset & (getpagesize() - 1); uint64_t aligned_offset = offset & ~(getpagesize() - 1); if (aligned_offset > static_cast<uint64_t>(buf.st_size) || offset > static_cast<uint64_t>(buf.st_size)) { return false; } size_ = buf.st_size - aligned_offset; if (size < (UINT64_MAX - offset_) && size + offset_ < size_) { uint64_t max_size; if (!__builtin_add_overflow(size, offset_, &max_size) && max_size < size_) { // Truncate the mapped size. size_ = size + offset_; size_ = max_size; } void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset); if (map == MAP_FAILED) { Loading @@ -113,14 +119,15 @@ bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t } bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) { if (addr + size > size_) { uint64_t max_size; if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) { return false; } memcpy(dst, &data_[addr], size); return true; } static bool PtraceRead(pid_t pid, uint64_t addr, long* value) { bool MemoryRemote::PtraceRead(uint64_t addr, long* value) { #if !defined(__LP64__) // Cannot read an address greater than 32 bits. if (addr > UINT32_MAX) { Loading @@ -130,7 +137,7 @@ static bool PtraceRead(pid_t pid, uint64_t addr, long* value) { // 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<void*>(addr), nullptr); *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast<void*>(addr), nullptr); if (*value == -1 && errno) { return false; } Loading @@ -138,11 +145,17 @@ static bool PtraceRead(pid_t pid, uint64_t addr, long* value) { } 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(pid_, addr & ~(sizeof(long) - 1), &data)) { if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) { return false; } size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes); Loading @@ -154,7 +167,7 @@ bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) { } for (size_t i = 0; i < bytes / sizeof(long); i++) { if (!PtraceRead(pid_, addr, &data)) { if (!PtraceRead(addr, &data)) { return false; } memcpy(dst, &data, sizeof(long)); Loading @@ -165,7 +178,7 @@ bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) { size_t left_over = bytes & (sizeof(long) - 1); if (left_over) { if (!PtraceRead(pid_, addr, &data)) { if (!PtraceRead(addr, &data)) { return false; } memcpy(dst, &data, left_over); Loading @@ -175,7 +188,13 @@ bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) { } bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) { // The process_vm_readv call does will not always work on remote // Make sure that there is no overflow. uint64_t max_size; if (__builtin_add_overflow(addr, size, &max_size)) { return false; } // 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. Loading Loading @@ -208,9 +227,29 @@ bool MemoryOffline::Init(const std::string& file, uint64_t offset) { } bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) { if (addr < start_ || addr + size > start_ + offset_ + size_) { uint64_t max_size; if (__builtin_add_overflow(addr, size, &max_size)) { return false; } 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; } 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); }
libunwindstack/Memory.h +18 −10 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #ifndef _LIBUNWINDSTACK_MEMORY_H #define _LIBUNWINDSTACK_MEMORY_H #include <assert.h> #include <stdint.h> #include <sys/types.h> #include <unistd.h> Loading @@ -33,9 +34,16 @@ class Memory { virtual bool Read(uint64_t addr, void* dst, size_t size) = 0; inline bool Read(uint64_t addr, void* start, void* field, size_t size) { return Read(addr + reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start), field, size); inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) { if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) { return false; } uint64_t offset = reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start); if (__builtin_add_overflow(addr, offset, &offset)) { return false; } // The read will check if offset + size overflows. return Read(offset, field, size); } inline bool Read32(uint64_t addr, uint32_t* dst) { Loading Loading @@ -103,6 +111,9 @@ class MemoryRemote : public Memory { pid_t pid() { return pid_; } protected: virtual bool PtraceRead(uint64_t addr, long* value); private: pid_t pid_; }; Loading @@ -118,15 +129,12 @@ class MemoryLocal : public Memory { class MemoryRange : public Memory { public: MemoryRange(Memory* memory, uint64_t begin, uint64_t end) : memory_(memory), begin_(begin), length_(end - begin_) {} : memory_(memory), begin_(begin), length_(end - begin) { assert(end > begin); } virtual ~MemoryRange() { delete memory_; } inline bool Read(uint64_t addr, void* dst, size_t size) override { if (addr + size <= length_) { return memory_->Read(addr + begin_, dst, size); } return false; } bool Read(uint64_t addr, void* dst, size_t size) override; private: Memory* memory_; Loading
libunwindstack/tests/MemoryBuffer.cpp 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <vector> #include <gtest/gtest.h> #include "Memory.h" #include "LogFake.h" class MemoryBufferTest : public ::testing::Test { protected: void SetUp() override { ResetLogs(); memory_.reset(new MemoryBuffer); } std::unique_ptr<MemoryBuffer> memory_; }; TEST_F(MemoryBufferTest, empty) { ASSERT_EQ(0U, memory_->Size()); std::vector<uint8_t> buffer(1024); ASSERT_FALSE(memory_->Read(0, buffer.data(), 1)); ASSERT_EQ(nullptr, memory_->GetPtr(0)); ASSERT_EQ(nullptr, memory_->GetPtr(1)); } TEST_F(MemoryBufferTest, write_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<uint8_t> buffer(memory_->Size()); ASSERT_TRUE(memory_->Read(0, buffer.data(), buffer.size())); for (size_t i = 0; i < buffer.size(); i++) { ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i; } } TEST_F(MemoryBufferTest, read_failures) { memory_->Resize(100); std::vector<uint8_t> 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)); } TEST_F(MemoryBufferTest, read_failure_overflow) { memory_->Resize(100); std::vector<uint8_t> buffer(200); ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200)); }