diff options
author | Josh Gao <jmgao@google.com> | 2017-09-26 14:11:37 -0700 |
---|---|---|
committer | Josh Gao <jmgao@google.com> | 2017-11-15 15:38:13 -0800 |
commit | 506e04cf5dc32bb58cda3aab1b9e2ca2fc2006c7 (patch) | |
tree | de0da6ace11ccac0cda6c58fe24c7f88b6c2fcd6 /libunwindstack/Memory.cpp | |
parent | 438b1e800338a69f9f1fbb9fc0870024cc1e684f (diff) | |
download | unwinding-506e04cf5dc32bb58cda3aab1b9e2ca2fc2006c7.tar.gz |
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
Diffstat (limited to 'libunwindstack/Memory.cpp')
-rw-r--r-- | libunwindstack/Memory.cpp | 226 |
1 files changed, 107 insertions, 119 deletions
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<void*>(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> Memory::CreateProcessMemory(pid_t pid) { return std::shared_ptr<Memory>(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<size_t>(addr); + const unsigned char* actual_base = static_cast<const unsigned char*>(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<size_t>(addr); + const unsigned char* actual_base = static_cast<const unsigned char*>(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<void*>(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<uint8_t*>(&data) + align_bytes, copy_bytes); - addr += copy_bytes; - dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + copy_bytes); - bytes -= copy_bytes; - bytes_read += copy_bytes; - } +MemoryRange::MemoryRange(const std::shared_ptr<Memory>& 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<void*>(reinterpret_cast<uintptr_t>(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<uint64_t>(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<void*>(static_cast<uintptr_t>(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<size_t>(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<MemoryFileAtOffset>(); + 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<MemoryRange>(memory_file, sizeof(start), size, start); return true; } -MemoryRange::MemoryRange(const std::shared_ptr<Memory>& 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 |