mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-21 15:11:37 +08:00
a5d81370be
This fixes ProcessMemory for 32-bit processes. All ProcessMemory tests were failing on 32-bit ARM on Android like this: [ RUN ] ProcessMemory.ReadSelf [17345:17345:20170407,172222.579687:ERROR process_memory.cc:55] pread: Invalid argument (22) ../../../../util/linux/process_memory_test.cc:73: Failure Value of: memory.Read(address, region_size_, result.get()) Actual: false Expected: true [ FAILED ] ProcessMemory.ReadSelf (5 ms) Contemporary Linux doesn’t provide a pread() system call, it provides pread64(), which operates on off64_t. pread() is a user-space wrapper that accepts off_t. See Android 7.1.1 bionic/libc/bionic/legacy_32_bit_support.cpp pread(). Note that off_t is a signed type. With a 32-bit off_t, when the “offset” parameter to pread() has its high bit set, it will be sign-extended into the 64-bit off64_t, and when interpreted as a memory address by virtue of being used as an offset into /proc/pid/mem, the value will take on an incorrect meaning. In fact, the kernel will reject it outright for its negativity. See linux-4.9.20/fs/read_write.c [sys_]pread64(). Since ProcessMemory accepts its address parameter as a LinuxVMAddress, which is wisely a uint64_t, it converts to off64_t properly, retaining its original value. Note, however, that the pread64() mechanism evidently cannot read memory in the high half of a process’ address space even when pread64() is used throughout. Most importantly, the (pos < 0) check in the kernel will be tripped. Less importantly, the conversion of our unsigned LinuxVMAddress to pread64’s signed off64_t, with the high bit set, is not defined. This is not an immediate practical problem. With the exception of possible shared pages mapped from kernel space (I only see this for the vsyscall page on x86_64), Linux restricts 64-bit user process’ address space to at least the lower half of the addressable range, with the high bit clear. (The limit of the user address space is linux-4.9.20/arch/x86/include/asm/processor.h TASK_SIZE_MAX = 0x7ffffffff000 for x86_64 and linux-4.9.20/arch/arm64/include/asm/memory.h TASK_SIZE_64 = 0x1000000000000 at maximum for arm64.) The 32-bit off_t may be a surprise, because third_party/mini_chromium/mini_chromium/build/common.gypi sets _FILE_OFFSET_BITS=64. Altough this macro is considered in the NDK’s “unified headers”, in the classic NDK, this macro is never consulted. Instead, off_t is always “long”, and pread() always gets the compatibility shim in Bionic. Bug: crashpad:30 Change-Id: Id00c882a3d521a46ef3fc0060d03ea0ab9493175 Reviewed-on: https://chromium-review.googlesource.com/472048 Reviewed-by: Joshua Peraza <jperaza@chromium.org>
125 lines
3.4 KiB
C++
125 lines
3.4 KiB
C++
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
|
//
|
|
// 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 "util/linux/process_memory.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include "base/logging.h"
|
|
#include "base/posix/eintr_wrapper.h"
|
|
|
|
namespace crashpad {
|
|
|
|
ProcessMemory::ProcessMemory() : mem_fd_(), pid_(-1) {}
|
|
|
|
ProcessMemory::~ProcessMemory() {}
|
|
|
|
bool ProcessMemory::Initialize(pid_t pid) {
|
|
pid_ = pid;
|
|
char path[32];
|
|
snprintf(path, sizeof(path), "/proc/%d/mem", pid_);
|
|
mem_fd_.reset(HANDLE_EINTR(open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC)));
|
|
if (!mem_fd_.is_valid()) {
|
|
PLOG(ERROR) << "open";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ProcessMemory::Read(LinuxVMAddress address,
|
|
size_t size,
|
|
void* buffer) const {
|
|
DCHECK(mem_fd_.is_valid());
|
|
|
|
char* buffer_c = static_cast<char*>(buffer);
|
|
while (size > 0) {
|
|
ssize_t bytes_read =
|
|
HANDLE_EINTR(pread64(mem_fd_.get(), buffer_c, size, address));
|
|
if (bytes_read < 0) {
|
|
PLOG(ERROR) << "pread64";
|
|
return false;
|
|
}
|
|
if (bytes_read == 0) {
|
|
LOG(ERROR) << "unexpected eof";
|
|
return false;
|
|
}
|
|
DCHECK_LE(static_cast<size_t>(bytes_read), size);
|
|
size -= bytes_read;
|
|
address += bytes_read;
|
|
buffer_c += bytes_read;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ProcessMemory::ReadCString(LinuxVMAddress address,
|
|
std::string* string) const {
|
|
return ReadCStringInternal(address, false, 0, string);
|
|
}
|
|
|
|
bool ProcessMemory::ReadCStringSizeLimited(LinuxVMAddress address,
|
|
size_t size,
|
|
std::string* string) const {
|
|
return ReadCStringInternal(address, true, size, string);
|
|
}
|
|
|
|
bool ProcessMemory::ReadCStringInternal(LinuxVMAddress address,
|
|
bool has_size,
|
|
size_t size,
|
|
std::string* string) const {
|
|
DCHECK(mem_fd_.is_valid());
|
|
|
|
string->clear();
|
|
|
|
char buffer[4096];
|
|
do {
|
|
size_t read_size;
|
|
if (has_size) {
|
|
read_size = std::min(sizeof(buffer), size);
|
|
} else {
|
|
read_size = sizeof(buffer);
|
|
}
|
|
ssize_t bytes_read;
|
|
bytes_read =
|
|
HANDLE_EINTR(pread64(mem_fd_.get(), buffer, read_size, address));
|
|
if (bytes_read < 0) {
|
|
PLOG(ERROR) << "pread64";
|
|
return false;
|
|
}
|
|
if (bytes_read == 0) {
|
|
break;
|
|
}
|
|
DCHECK_LE(static_cast<size_t>(bytes_read), read_size);
|
|
|
|
char* nul = static_cast<char*>(memchr(buffer, '\0', bytes_read));
|
|
if (nul != nullptr) {
|
|
string->append(buffer, nul - buffer);
|
|
return true;
|
|
}
|
|
string->append(buffer, bytes_read);
|
|
|
|
address += bytes_read;
|
|
size -= bytes_read;
|
|
} while (!has_size || size > 0);
|
|
|
|
LOG(ERROR) << "unterminated string";
|
|
return false;
|
|
}
|
|
|
|
} // namespace crashpad
|