From 506e04cf5dc32bb58cda3aab1b9e2ca2fc2006c7 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Tue, 26 Sep 2017 14:11:37 -0700 Subject: unwindstack: add Memory::ReadPartially. Add a way to read while allowing for partial reads. Test: new tests added to libunwindstack_test, ran 32/64 on hikey960, sailfish Test: ran unwind on hikey960/sailfish Change-Id: I8b11d9230fcd3122148ef3f980863ac1404ad70a --- libunwindstack/Memory.cpp | 226 ++++++++++++++++++++++------------------------ 1 file changed, 107 insertions(+), 119 deletions(-) (limited to 'libunwindstack/Memory.cpp') diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp index 32753df..f27a350 100644 --- a/libunwindstack/Memory.cpp +++ b/libunwindstack/Memory.cpp @@ -32,8 +32,63 @@ #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::Read(uint64_t addr, void* dst, size_t size) { + size_t rc = ReadPartially(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; @@ -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::ReadPartially(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::ReadPartially(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::ReadPartially(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 MemoryLocal::ReadPartially(uint64_t addr, void* dst, size_t size) { + return ProcessVmRead(getpid(), dst, addr, size); +} - 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; - } +MemoryRange::MemoryRange(const std::shared_ptr& memory, uint64_t begin, uint64_t length, + uint64_t offset) + : memory_(memory), begin_(begin), length_(length), offset_(offset) {} - 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 MemoryRange::ReadPartially(uint64_t addr, void* dst, size_t size) { + if (addr < offset_) { + return 0; } - 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; + uint64_t read_offset = addr - offset_; + if (read_offset >= length_) { + return 0; } - return true; -} -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; + 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; } - // 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; - } - return static_cast(bytes_read) == size; + return memory_->ReadPartially(read_addr, dst, read_length); } bool MemoryOffline::Init(const std::string& file, uint64_t offset) { - if (!MemoryFileAtOffset::Init(file, offset)) { - return false; - } - // The first uint64_t value is the start of memory. - if (!MemoryFileAtOffset::Read(0, &start_, sizeof(start_))) { + auto memory_file = std::make_shared(); + if (!memory_file->Init(file, offset)) { return false; } - // Subtract the first 64 bit value from the total size. - size_ -= sizeof(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)) { + // The first uint64_t value is the start of memory. + uint64_t start; + if (!memory_file->Read(0, &start, sizeof(start))) { return false; } - uint64_t real_size; - if (__builtin_add_overflow(start_, offset_, &real_size) || - __builtin_add_overflow(real_size, size_, &real_size)) { + uint64_t size = memory_file->Size(); + if (__builtin_sub_overflow(size, sizeof(start), &size)) { return false; } - if (addr < start_ || max_size > real_size) { - return false; - } - memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size); + memory_ = std::make_unique(memory_file, sizeof(start), size, start); 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; +size_t MemoryOffline::ReadPartially(uint64_t addr, void* dst, size_t size) { + if (!memory_) { + return 0; } - // The check above guarantees that addr + begin_ will not overflow. - return memory_->Read(addr + begin_, dst, size); + + return memory_->ReadPartially(addr, dst, size); } } // namespace unwindstack -- cgit v1.2.3