mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-14 01:08:01 +08:00
linux: Add PtraceConnection::ReadFileContents
Some files, such as /proc/[pid]/maps, may not be accessible to the handler. This enables the handler access to the contents of those files via the broker. This change reads maps and auxv using ReadFileContents. Bug: crashpad:30 Change-Id: Ia19b498bae473c616ea794ab51c3f22afd5795be Reviewed-on: https://chromium-review.googlesource.com/989406 Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
246ecc6686
commit
d108fd04a5
@ -39,6 +39,7 @@
|
||||
|
||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
|
||||
#include "test/linux/fake_ptrace_connection.h"
|
||||
#include "util/linux/auxiliary_vector.h"
|
||||
#include "util/linux/memory_map.h"
|
||||
|
||||
@ -62,7 +63,6 @@ namespace {
|
||||
|
||||
void LocateExecutable(ProcessType process,
|
||||
ProcessMemory* memory,
|
||||
bool is_64_bit,
|
||||
VMAddress* elf_address) {
|
||||
uintptr_t debug_address;
|
||||
zx_status_t status = zx_object_get_property(process,
|
||||
@ -90,18 +90,17 @@ void LocateExecutable(ProcessType process,
|
||||
|
||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
|
||||
void LocateExecutable(ProcessType process,
|
||||
void LocateExecutable(PtraceConnection* connection,
|
||||
ProcessMemory* memory,
|
||||
bool is_64_bit,
|
||||
VMAddress* elf_address) {
|
||||
AuxiliaryVector aux;
|
||||
ASSERT_TRUE(aux.Initialize(process, is_64_bit));
|
||||
ASSERT_TRUE(aux.Initialize(connection));
|
||||
|
||||
VMAddress phdrs;
|
||||
ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs));
|
||||
|
||||
MemoryMap memory_map;
|
||||
ASSERT_TRUE(memory_map.Initialize(process));
|
||||
ASSERT_TRUE(memory_map.Initialize(connection));
|
||||
const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs);
|
||||
ASSERT_TRUE(phdr_mapping);
|
||||
const MemoryMap::Mapping* exe_mapping =
|
||||
@ -139,7 +138,13 @@ void ReadThisExecutableInTarget(ProcessType process,
|
||||
ASSERT_TRUE(range.Initialize(&memory, am_64_bit));
|
||||
|
||||
VMAddress elf_address;
|
||||
LocateExecutable(process, &memory, am_64_bit, &elf_address);
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
FakePtraceConnection connection;
|
||||
ASSERT_TRUE(connection.Initialize(process));
|
||||
LocateExecutable(&connection, &memory, &elf_address);
|
||||
#elif defined(OS_FUCHSIA)
|
||||
LocateExecutable(process, &memory, &elf_address);
|
||||
#endif
|
||||
ASSERT_NO_FATAL_FAILURE();
|
||||
|
||||
ElfImageReader reader;
|
||||
|
@ -27,9 +27,11 @@
|
||||
#include "build/build_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "snapshot/elf/elf_image_reader.h"
|
||||
#include "test/linux/fake_ptrace_connection.h"
|
||||
#include "test/multiprocess.h"
|
||||
#include "util/linux/address_types.h"
|
||||
#include "util/linux/auxiliary_vector.h"
|
||||
#include "util/linux/direct_ptrace_connection.h"
|
||||
#include "util/linux/memory_map.h"
|
||||
#include "util/process/process_memory_linux.h"
|
||||
#include "util/process/process_memory_range.h"
|
||||
@ -57,18 +59,18 @@ int AndroidRuntimeAPI() {
|
||||
}
|
||||
#endif // OS_ANDROID
|
||||
|
||||
void TestAgainstTarget(pid_t pid, bool is_64_bit) {
|
||||
void TestAgainstTarget(PtraceConnection* connection) {
|
||||
// Use ElfImageReader on the main executable which can tell us the debug
|
||||
// address. glibc declares the symbol _r_debug in link.h which we can use to
|
||||
// get the address, but Android does not.
|
||||
AuxiliaryVector aux;
|
||||
ASSERT_TRUE(aux.Initialize(pid, is_64_bit));
|
||||
ASSERT_TRUE(aux.Initialize(connection));
|
||||
|
||||
LinuxVMAddress phdrs;
|
||||
ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs));
|
||||
|
||||
MemoryMap mappings;
|
||||
ASSERT_TRUE(mappings.Initialize(pid));
|
||||
ASSERT_TRUE(mappings.Initialize(connection));
|
||||
|
||||
const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs);
|
||||
ASSERT_TRUE(phdr_mapping);
|
||||
@ -77,9 +79,9 @@ void TestAgainstTarget(pid_t pid, bool is_64_bit) {
|
||||
LinuxVMAddress elf_address = exe_mapping->range.Base();
|
||||
|
||||
ProcessMemoryLinux memory;
|
||||
ASSERT_TRUE(memory.Initialize(pid));
|
||||
ASSERT_TRUE(memory.Initialize(connection->GetProcessID()));
|
||||
ProcessMemoryRange range;
|
||||
ASSERT_TRUE(range.Initialize(&memory, is_64_bit));
|
||||
ASSERT_TRUE(range.Initialize(&memory, connection->Is64Bit()));
|
||||
|
||||
ElfImageReader exe_reader;
|
||||
ASSERT_TRUE(exe_reader.Initialize(range, elf_address));
|
||||
@ -107,7 +109,7 @@ void TestAgainstTarget(pid_t pid, bool is_64_bit) {
|
||||
// glibc's loader does not set the name for the executable.
|
||||
EXPECT_TRUE(debug.Executable()->name.empty());
|
||||
CheckedLinuxAddressRange exe_range(
|
||||
is_64_bit, exe_reader.Address(), exe_reader.Size());
|
||||
connection->Is64Bit(), exe_reader.Address(), exe_reader.Size());
|
||||
EXPECT_TRUE(exe_range.ContainsValue(debug.Executable()->dynamic_array));
|
||||
#endif // OS_ANDROID
|
||||
|
||||
@ -179,19 +181,16 @@ void TestAgainstTarget(pid_t pid, bool is_64_bit) {
|
||||
}
|
||||
|
||||
CheckedLinuxAddressRange module_range(
|
||||
is_64_bit, module_reader.Address(), module_reader.Size());
|
||||
connection->Is64Bit(), module_reader.Address(), module_reader.Size());
|
||||
EXPECT_TRUE(module_range.ContainsValue(module.dynamic_array));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DebugRendezvous, Self) {
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
constexpr bool is_64_bit = true;
|
||||
#else
|
||||
constexpr bool is_64_bit = false;
|
||||
#endif
|
||||
FakePtraceConnection connection;
|
||||
ASSERT_TRUE(connection.Initialize(getpid()));
|
||||
|
||||
TestAgainstTarget(getpid(), is_64_bit);
|
||||
TestAgainstTarget(&connection);
|
||||
}
|
||||
|
||||
class ChildTest : public Multiprocess {
|
||||
@ -201,13 +200,10 @@ class ChildTest : public Multiprocess {
|
||||
|
||||
private:
|
||||
void MultiprocessParent() {
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
constexpr bool is_64_bit = true;
|
||||
#else
|
||||
constexpr bool is_64_bit = false;
|
||||
#endif
|
||||
DirectPtraceConnection connection;
|
||||
ASSERT_TRUE(connection.Initialize(ChildPID()));
|
||||
|
||||
TestAgainstTarget(ChildPID(), is_64_bit);
|
||||
TestAgainstTarget(&connection);
|
||||
}
|
||||
|
||||
void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); }
|
||||
|
@ -199,11 +199,11 @@ bool ProcessReaderLinux::Initialize(PtraceConnection* connection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pid_t pid = connection->GetProcessID();
|
||||
if (!memory_map_.Initialize(pid)) {
|
||||
if (!memory_map_.Initialize(connection_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pid_t pid = connection->GetProcessID();
|
||||
if (!process_memory_.Initialize(pid)) {
|
||||
return false;
|
||||
}
|
||||
@ -332,7 +332,7 @@ void ProcessReaderLinux::InitializeModules() {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
|
||||
AuxiliaryVector aux;
|
||||
if (!aux.Initialize(ProcessID(), is_64_bit_)) {
|
||||
if (!aux.Initialize(connection_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -242,10 +242,11 @@ using ThreadMap = std::map<pid_t, TestThreadPool::ThreadExpectation>;
|
||||
|
||||
void ExpectThreads(const ThreadMap& thread_map,
|
||||
const std::vector<ProcessReaderLinux::Thread>& threads,
|
||||
const pid_t pid) {
|
||||
PtraceConnection* connection) {
|
||||
ASSERT_EQ(threads.size(), thread_map.size());
|
||||
|
||||
MemoryMap memory_map;
|
||||
ASSERT_TRUE(memory_map.Initialize(pid));
|
||||
ASSERT_TRUE(memory_map.Initialize(connection));
|
||||
|
||||
for (const auto& thread : threads) {
|
||||
SCOPED_TRACE(
|
||||
@ -306,7 +307,7 @@ class ChildThreadTest : public Multiprocess {
|
||||
ASSERT_TRUE(process_reader.Initialize(&connection));
|
||||
const std::vector<ProcessReaderLinux::Thread>& threads =
|
||||
process_reader.Threads();
|
||||
ExpectThreads(thread_map, threads, ChildPID());
|
||||
ExpectThreads(thread_map, threads, &connection);
|
||||
}
|
||||
|
||||
void MultiprocessChild() override {
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "build/build_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "util/file/file_io.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
@ -70,5 +71,11 @@ bool FakePtraceConnection::GetThreadInfo(pid_t tid, ThreadInfo* info) {
|
||||
return attached;
|
||||
}
|
||||
|
||||
bool FakePtraceConnection::ReadFileContents(const base::FilePath& path,
|
||||
std::string* contents) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return LoggingReadEntireFile(path, contents);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
@ -53,6 +53,9 @@ class FakePtraceConnection : public PtraceConnection {
|
||||
//! \brief Does not modify \a info.
|
||||
bool GetThreadInfo(pid_t tid, ThreadInfo* info) override;
|
||||
|
||||
bool ReadFileContents(const base::FilePath& path,
|
||||
std::string* contents) override;
|
||||
|
||||
private:
|
||||
std::set<pid_t> attachments_;
|
||||
pid_t pid_;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "util/file/file_reader.h"
|
||||
#include "util/file/string_file.h"
|
||||
#include "util/stdlib/map_insert.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -28,18 +29,22 @@ AuxiliaryVector::AuxiliaryVector() : values_() {}
|
||||
|
||||
AuxiliaryVector::~AuxiliaryVector() {}
|
||||
|
||||
bool AuxiliaryVector::Initialize(pid_t pid, bool is_64_bit) {
|
||||
return is_64_bit ? Read<uint64_t>(pid) : Read<uint32_t>(pid);
|
||||
bool AuxiliaryVector::Initialize(PtraceConnection* connection) {
|
||||
return connection->Is64Bit() ? Read<uint64_t>(connection)
|
||||
: Read<uint32_t>(connection);
|
||||
}
|
||||
|
||||
template <typename ULong>
|
||||
bool AuxiliaryVector::Read(pid_t pid) {
|
||||
bool AuxiliaryVector::Read(PtraceConnection* connection) {
|
||||
char path[32];
|
||||
snprintf(path, sizeof(path), "/proc/%d/auxv", pid);
|
||||
FileReader aux_file;
|
||||
if (!aux_file.Open(base::FilePath(path))) {
|
||||
snprintf(path, sizeof(path), "/proc/%d/auxv", connection->GetProcessID());
|
||||
|
||||
std::string contents;
|
||||
if (!connection->ReadFileContents(base::FilePath(path), &contents)) {
|
||||
return false;
|
||||
}
|
||||
StringFile aux_file;
|
||||
aux_file.SetString(contents);
|
||||
|
||||
ULong type;
|
||||
ULong value;
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "util/linux/ptrace_connection.h"
|
||||
#include "util/misc/reinterpret_bytes.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -32,16 +33,15 @@ class AuxiliaryVector {
|
||||
~AuxiliaryVector();
|
||||
|
||||
//! \brief Initializes this object with the auxiliary vector for the process
|
||||
//! with process ID \a pid.
|
||||
//! connected via \a connection.
|
||||
//!
|
||||
//! This method must be called successfully prior to calling any other method
|
||||
//! in this class.
|
||||
//!
|
||||
//! \param[in] pid The process ID of a target process.
|
||||
//! \param[in] is_64_bit Whether the target process is 64-bit.
|
||||
//! \param[in] connection A connection to the target process.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
bool Initialize(pid_t pid, bool is_64_bit);
|
||||
bool Initialize(PtraceConnection* connection);
|
||||
|
||||
//! \brief Retrieve a value from the vector.
|
||||
//!
|
||||
@ -64,7 +64,7 @@ class AuxiliaryVector {
|
||||
|
||||
private:
|
||||
template <typename ULong>
|
||||
bool Read(pid_t pid);
|
||||
bool Read(PtraceConnection* connection);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AuxiliaryVector);
|
||||
};
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "build/build_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/errors.h"
|
||||
#include "test/linux/fake_ptrace_connection.h"
|
||||
#include "test/multiprocess.h"
|
||||
#include "util/linux/address_types.h"
|
||||
#include "util/linux/memory_map.h"
|
||||
@ -45,16 +46,14 @@ namespace test {
|
||||
namespace {
|
||||
|
||||
void TestAgainstCloneOrSelf(pid_t pid) {
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
constexpr bool am_64_bit = true;
|
||||
#else
|
||||
constexpr bool am_64_bit = false;
|
||||
#endif
|
||||
FakePtraceConnection connection;
|
||||
ASSERT_TRUE(connection.Initialize(pid));
|
||||
|
||||
AuxiliaryVector aux;
|
||||
ASSERT_TRUE(aux.Initialize(pid, am_64_bit));
|
||||
ASSERT_TRUE(aux.Initialize(&connection));
|
||||
|
||||
MemoryMap mappings;
|
||||
ASSERT_TRUE(mappings.Initialize(pid));
|
||||
ASSERT_TRUE(mappings.Initialize(&connection));
|
||||
|
||||
LinuxVMAddress phdrs;
|
||||
ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs));
|
||||
@ -168,13 +167,11 @@ class AuxVecTester : public AuxiliaryVector {
|
||||
};
|
||||
|
||||
TEST(AuxiliaryVector, SignedBit) {
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
constexpr bool am_64_bit = true;
|
||||
#else
|
||||
constexpr bool am_64_bit = false;
|
||||
#endif
|
||||
FakePtraceConnection connection;
|
||||
ASSERT_TRUE(connection.Initialize(getpid()));
|
||||
|
||||
AuxVecTester aux;
|
||||
ASSERT_TRUE(aux.Initialize(getpid(), am_64_bit));
|
||||
ASSERT_TRUE(&connection);
|
||||
constexpr uint64_t type = 0x0000000012345678;
|
||||
|
||||
constexpr int32_t neg1_32 = -1;
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "util/file/file_io.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
DirectPtraceConnection::DirectPtraceConnection()
|
||||
@ -63,4 +65,10 @@ bool DirectPtraceConnection::GetThreadInfo(pid_t tid, ThreadInfo* info) {
|
||||
return ptracer_.GetThreadInfo(tid, info);
|
||||
}
|
||||
|
||||
bool DirectPtraceConnection::ReadFileContents(const base::FilePath& path,
|
||||
std::string* contents) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return LoggingReadEntireFile(path, contents);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -52,6 +52,8 @@ class DirectPtraceConnection : public PtraceConnection {
|
||||
bool Attach(pid_t tid) override;
|
||||
bool Is64Bit() override;
|
||||
bool GetThreadInfo(pid_t tid, ThreadInfo* info) override;
|
||||
bool ReadFileContents(const base::FilePath& path,
|
||||
std::string* contents) override;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<ScopedPtraceAttach>> attachments_;
|
||||
|
@ -221,7 +221,7 @@ bool MemoryMap::Mapping::Equals(const Mapping& other) const {
|
||||
shareable == other.shareable;
|
||||
}
|
||||
|
||||
bool MemoryMap::Initialize(pid_t pid) {
|
||||
bool MemoryMap::Initialize(PtraceConnection* connection) {
|
||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||
|
||||
// If the maps file is not read atomically, entries can be read multiple times
|
||||
@ -235,8 +235,8 @@ bool MemoryMap::Initialize(pid_t pid) {
|
||||
do {
|
||||
std::string contents;
|
||||
char path[32];
|
||||
snprintf(path, sizeof(path), "/proc/%d/maps", pid);
|
||||
if (!LoggingReadEntireFile(base::FilePath(path), &contents)) {
|
||||
snprintf(path, sizeof(path), "/proc/%d/maps", connection->GetProcessID());
|
||||
if (!connection->ReadFileContents(base::FilePath(path), &contents)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "util/linux/address_types.h"
|
||||
#include "util/linux/checked_linux_address_range.h"
|
||||
#include "util/linux/ptrace_connection.h"
|
||||
#include "util/misc/initialization_state_dcheck.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -54,15 +55,15 @@ class MemoryMap {
|
||||
~MemoryMap();
|
||||
|
||||
//! \brief Initializes this object with information about the mapped memory
|
||||
//! regions in the process whose ID is \a pid.
|
||||
//! regions in the process connected via \a connection.
|
||||
//!
|
||||
//! This method must be called successfully prior to calling any other method
|
||||
//! in this class. This method may only be called once.
|
||||
//!
|
||||
//! \param[in] pid The process ID to obtain information for.
|
||||
//! \param[in] connection A connection to the process create a map for.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
bool Initialize(pid_t pid);
|
||||
bool Initialize(PtraceConnection* connection);
|
||||
|
||||
//! \return The Mapping containing \a address or `nullptr` if no match is
|
||||
//! found. The caller does not take ownership of this object. It is scoped
|
||||
|
@ -26,10 +26,11 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/errors.h"
|
||||
#include "test/file.h"
|
||||
#include "test/linux/fake_ptrace_connection.h"
|
||||
#include "test/multiprocess.h"
|
||||
#include "test/scoped_temp_dir.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/linux/scoped_ptrace_attach.h"
|
||||
#include "util/linux/direct_ptrace_connection.h"
|
||||
#include "util/misc/clock.h"
|
||||
#include "util/misc/from_pointer_cast.h"
|
||||
#include "util/posix/scoped_mmap.h"
|
||||
@ -46,8 +47,12 @@ TEST(MemoryMap, SelfBasic) {
|
||||
MAP_SHARED | MAP_ANON,
|
||||
-1,
|
||||
0));
|
||||
|
||||
FakePtraceConnection connection;
|
||||
ASSERT_TRUE(connection.Initialize(getpid()));
|
||||
|
||||
MemoryMap map;
|
||||
ASSERT_TRUE(map.Initialize(getpid()));
|
||||
ASSERT_TRUE(map.Initialize(&connection));
|
||||
|
||||
auto stack_address = FromPointerCast<LinuxVMAddress>(&map);
|
||||
const MemoryMap::Mapping* mapping = map.FindMapping(stack_address);
|
||||
@ -118,11 +123,11 @@ class MapChildTest : public Multiprocess {
|
||||
std::string mapped_file_name(path_length, std::string::value_type());
|
||||
CheckedReadFileExactly(ReadPipeHandle(), &mapped_file_name[0], path_length);
|
||||
|
||||
ScopedPtraceAttach attachment;
|
||||
attachment.ResetAttach(ChildPID());
|
||||
DirectPtraceConnection connection;
|
||||
ASSERT_TRUE(connection.Initialize(ChildPID()));
|
||||
|
||||
MemoryMap map;
|
||||
ASSERT_TRUE(map.Initialize(ChildPID()));
|
||||
ASSERT_TRUE(map.Initialize(&connection));
|
||||
|
||||
const MemoryMap::Mapping* mapping = map.FindMapping(code_address);
|
||||
ASSERT_TRUE(mapping);
|
||||
@ -268,8 +273,11 @@ TEST(MemoryMap, SelfLargeMapFile) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
InitializeMappings(&mappings, kNumMappings, page_size));
|
||||
|
||||
FakePtraceConnection connection;
|
||||
ASSERT_TRUE(connection.Initialize(getpid()));
|
||||
|
||||
MemoryMap map;
|
||||
ASSERT_TRUE(map.Initialize(getpid()));
|
||||
ASSERT_TRUE(map.Initialize(&connection));
|
||||
|
||||
ExpectMappings(
|
||||
map, mappings.addr_as<LinuxVMAddress>(), kNumMappings, page_size);
|
||||
@ -292,11 +300,11 @@ class MapRunningChildTest : public Multiprocess {
|
||||
// Let the child get back to its work
|
||||
SleepNanoseconds(1000);
|
||||
|
||||
ScopedPtraceAttach attachment;
|
||||
attachment.ResetAttach(ChildPID());
|
||||
DirectPtraceConnection connection;
|
||||
ASSERT_TRUE(connection.Initialize(ChildPID()));
|
||||
|
||||
MemoryMap map;
|
||||
ASSERT_TRUE(map.Initialize(ChildPID()));
|
||||
ASSERT_TRUE(map.Initialize(&connection));
|
||||
|
||||
// We should at least find the original mappings. The extra mappings may
|
||||
// or not be found depending on scheduling.
|
||||
@ -349,8 +357,11 @@ TEST(MemoryMap, MapRunningChild) {
|
||||
// file. The second page should not.
|
||||
void ExpectFindFileMmapStart(LinuxVMAddress mapping_start,
|
||||
LinuxVMSize page_size) {
|
||||
FakePtraceConnection connection;
|
||||
ASSERT_TRUE(connection.Initialize(getpid()));
|
||||
|
||||
MemoryMap map;
|
||||
ASSERT_TRUE(map.Initialize(getpid()));
|
||||
ASSERT_TRUE(map.Initialize(&connection));
|
||||
|
||||
auto mapping1 = map.FindMapping(mapping_start);
|
||||
ASSERT_TRUE(mapping1);
|
||||
@ -391,8 +402,11 @@ TEST(MemoryMap, FindFileMmapStart) {
|
||||
|
||||
// Basic
|
||||
{
|
||||
FakePtraceConnection connection;
|
||||
ASSERT_TRUE(connection.Initialize(getpid()));
|
||||
|
||||
MemoryMap map;
|
||||
ASSERT_TRUE(map.Initialize(getpid()));
|
||||
ASSERT_TRUE(map.Initialize(&connection));
|
||||
|
||||
auto mapping1 = map.FindMapping(mapping_start);
|
||||
ASSERT_TRUE(mapping1);
|
||||
|
@ -14,15 +14,22 @@
|
||||
|
||||
#include "util/linux/ptrace_broker.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "util/file/file_io.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
PtraceBroker::PtraceBroker(int sock, bool is_64_bit)
|
||||
: ptracer_(is_64_bit, /* can_log= */ false),
|
||||
file_root_("/proc/"),
|
||||
attachments_(nullptr),
|
||||
attach_count_(0),
|
||||
attach_capacity_(0),
|
||||
@ -32,12 +39,40 @@ PtraceBroker::PtraceBroker(int sock, bool is_64_bit)
|
||||
|
||||
PtraceBroker::~PtraceBroker() = default;
|
||||
|
||||
void PtraceBroker::SetFileRoot(const char* new_root) {
|
||||
DCHECK_EQ(new_root[strlen(new_root) - 1], '/');
|
||||
file_root_ = new_root;
|
||||
}
|
||||
|
||||
int PtraceBroker::Run() {
|
||||
int result = RunImpl();
|
||||
ReleaseAttachments();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool PtraceBroker::AllocateAttachments() {
|
||||
constexpr size_t page_size = 4096;
|
||||
constexpr size_t alloc_size =
|
||||
(sizeof(ScopedPtraceAttach) + page_size - 1) & ~(page_size - 1);
|
||||
void* alloc = sbrk(alloc_size);
|
||||
if (reinterpret_cast<intptr_t>(alloc) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attachments_ == nullptr) {
|
||||
attachments_ = reinterpret_cast<ScopedPtraceAttach*>(alloc);
|
||||
}
|
||||
|
||||
attach_capacity_ += alloc_size / sizeof(ScopedPtraceAttach);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PtraceBroker::ReleaseAttachments() {
|
||||
for (size_t index = 0; index < attach_count_; ++index) {
|
||||
attachments_[index].Reset();
|
||||
}
|
||||
}
|
||||
|
||||
int PtraceBroker::RunImpl() {
|
||||
while (true) {
|
||||
Request request = {};
|
||||
@ -114,6 +149,24 @@ int PtraceBroker::RunImpl() {
|
||||
continue;
|
||||
}
|
||||
|
||||
case Request::kTypeReadFile: {
|
||||
ScopedFileHandle handle;
|
||||
int result = ReceiveAndOpenFilePath(request.path.path_length, &handle);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!handle.is_valid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result = SendFileContents(handle.get());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
case Request::kTypeReadMemory: {
|
||||
int result =
|
||||
SendMemory(request.tid, request.iov.base, request.iov.size);
|
||||
@ -132,6 +185,46 @@ int PtraceBroker::RunImpl() {
|
||||
}
|
||||
}
|
||||
|
||||
int PtraceBroker::SendError(Errno err) {
|
||||
return WriteFile(sock_, &err, sizeof(err)) ? 0 : errno;
|
||||
}
|
||||
|
||||
int PtraceBroker::SendReadError(Errno err) {
|
||||
int32_t rv = -1;
|
||||
if (!WriteFile(sock_, &rv, sizeof(rv))) {
|
||||
return errno;
|
||||
}
|
||||
return SendError(err);
|
||||
}
|
||||
|
||||
int PtraceBroker::SendOpenResult(OpenResult result) {
|
||||
return WriteFile(sock_, &result, sizeof(result)) ? 0 : errno;
|
||||
}
|
||||
|
||||
int PtraceBroker::SendFileContents(FileHandle handle) {
|
||||
char buffer[4096];
|
||||
int32_t rv;
|
||||
do {
|
||||
rv = ReadFile(handle, buffer, sizeof(buffer));
|
||||
|
||||
if (rv < 0) {
|
||||
return SendReadError(errno);
|
||||
}
|
||||
|
||||
if (!WriteFile(sock_, &rv, sizeof(rv))) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (rv > 0) {
|
||||
if (!WriteFile(sock_, buffer, static_cast<size_t>(rv))) {
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
} while (rv > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PtraceBroker::SendMemory(pid_t pid, VMAddress address, VMSize size) {
|
||||
char buffer[4096];
|
||||
while (size > 0) {
|
||||
@ -161,27 +254,31 @@ int PtraceBroker::SendMemory(pid_t pid, VMAddress address, VMSize size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool PtraceBroker::AllocateAttachments() {
|
||||
constexpr size_t page_size = 4096;
|
||||
constexpr size_t alloc_size =
|
||||
(sizeof(ScopedPtraceAttach) + page_size - 1) & ~(page_size - 1);
|
||||
void* alloc = sbrk(alloc_size);
|
||||
if (reinterpret_cast<intptr_t>(alloc) == -1) {
|
||||
return false;
|
||||
int PtraceBroker::ReceiveAndOpenFilePath(VMSize path_length,
|
||||
ScopedFileHandle* handle) {
|
||||
char path[std::max(4096, PATH_MAX)];
|
||||
|
||||
if (path_length >= sizeof(path)) {
|
||||
return SendOpenResult(kOpenResultTooLong);
|
||||
}
|
||||
|
||||
if (attachments_ == nullptr) {
|
||||
attachments_ = reinterpret_cast<ScopedPtraceAttach*>(alloc);
|
||||
if (!ReadFileExactly(sock_, path, path_length)) {
|
||||
return errno;
|
||||
}
|
||||
path[path_length] = '\0';
|
||||
|
||||
if (strncmp(path, file_root_, strlen(file_root_)) != 0) {
|
||||
return SendOpenResult(kOpenResultAccessDenied);
|
||||
}
|
||||
|
||||
attach_capacity_ += alloc_size / sizeof(ScopedPtraceAttach);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PtraceBroker::ReleaseAttachments() {
|
||||
for (size_t index = 0; index < attach_count_; ++index) {
|
||||
attachments_[index].Reset();
|
||||
ScopedFileHandle local_handle(
|
||||
HANDLE_EINTR(open(path, O_RDONLY | O_CLOEXEC | O_NOCTTY)));
|
||||
if (!local_handle.is_valid()) {
|
||||
return SendOpenResult(static_cast<OpenResult>(errno));
|
||||
}
|
||||
|
||||
handle->reset(local_handle.release());
|
||||
return SendOpenResult(kOpenResultSuccess);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/linux/exception_handler_protocol.h"
|
||||
#include "util/linux/ptrace_connection.h"
|
||||
#include "util/linux/ptracer.h"
|
||||
@ -69,6 +70,14 @@ class PtraceBroker {
|
||||
//! to zero, followed by an Errno.
|
||||
kTypeReadMemory,
|
||||
|
||||
//! \brief Read a file's contents. The data is returned in a series of
|
||||
//! messages. The first message is an OpenResult, indicating the
|
||||
//! validity of the received file path. If the OpenResult is
|
||||
//! kOpenResultSuccess, each subsequent message begins with an int32_t
|
||||
//! indicating the number of bytes read, 0 for end-of-file, or -1 for
|
||||
//! errors, followed by an Errno. On success, the bytes read follow.
|
||||
kTypeReadFile,
|
||||
|
||||
//! \brief Causes the broker to return from Run(), detaching all attached
|
||||
//! threads. Does not respond.
|
||||
kTypeExit
|
||||
@ -78,14 +87,41 @@ class PtraceBroker {
|
||||
//! kTypeGetThreadInfo, and kTypeReadMemory.
|
||||
pid_t tid;
|
||||
|
||||
//! \brief Specifies the memory region to read for a kTypeReadMemory request.
|
||||
struct {
|
||||
//! \brief The base address of the memory region.
|
||||
VMAddress base;
|
||||
union {
|
||||
//! \brief Specifies the memory region to read for a kTypeReadMemory
|
||||
//! request.
|
||||
struct {
|
||||
//! \brief The base address of the memory region.
|
||||
VMAddress base;
|
||||
|
||||
//! \brief The size of the memory region.
|
||||
VMSize size;
|
||||
} iov;
|
||||
//! \brief The size of the memory region.
|
||||
VMSize size;
|
||||
} iov;
|
||||
|
||||
// \brief Specifies the file path to read for a kTypeReadFile request.
|
||||
struct {
|
||||
//! \brief The number of bytes in #path. The path should not include a
|
||||
//! `NUL`-terminator.
|
||||
VMSize path_length;
|
||||
|
||||
//! \brief The file path to read.
|
||||
char path[];
|
||||
} path;
|
||||
};
|
||||
};
|
||||
|
||||
//! \brief A result used in operations that accept paths.
|
||||
//!
|
||||
//! Positive values of this enum are reserved for sending errno values.
|
||||
enum OpenResult : int32_t {
|
||||
//! \brief Access to the path is denied.
|
||||
kOpenResultAccessDenied = -2,
|
||||
|
||||
//! \brief The path name is too long.
|
||||
kOpenResultTooLong = -1,
|
||||
|
||||
//! \brief The file was successfully opened.
|
||||
kOpenResultSuccess = 0,
|
||||
};
|
||||
|
||||
//! \brief The response sent for a Request with type kTypeGetThreadInfo.
|
||||
@ -109,6 +145,18 @@ class PtraceBroker {
|
||||
|
||||
~PtraceBroker();
|
||||
|
||||
//! \brief Restricts the broker to serving the contents of files under \a
|
||||
//! root.
|
||||
//!
|
||||
//! If this method is not called, the broker defaults to only serving files
|
||||
//! under "/proc/".
|
||||
//!
|
||||
//! \param[in] root A NUL-terminated c-string containing the path to the new
|
||||
//! root. \a root must not be `nullptr`, must end in a '/', and the caller
|
||||
//! should ensure that \a root remains valid for the lifetime of the
|
||||
//! broker.
|
||||
void SetFileRoot(const char* root);
|
||||
|
||||
//! \brief Begin serving requests on the configured socket.
|
||||
//!
|
||||
//! This method returns when a PtraceBrokerRequest with type kTypeExit is
|
||||
@ -121,12 +169,18 @@ class PtraceBroker {
|
||||
int Run();
|
||||
|
||||
private:
|
||||
int RunImpl();
|
||||
int SendMemory(pid_t pid, VMAddress address, VMSize size);
|
||||
bool AllocateAttachments();
|
||||
void ReleaseAttachments();
|
||||
int RunImpl();
|
||||
int SendError(Errno err);
|
||||
int SendReadError(Errno err);
|
||||
int SendOpenResult(OpenResult result);
|
||||
int SendFileContents(FileHandle handle);
|
||||
int SendMemory(pid_t pid, VMAddress address, VMSize size);
|
||||
int ReceiveAndOpenFilePath(VMSize path_length, ScopedFileHandle* handle);
|
||||
|
||||
Ptracer ptracer_;
|
||||
const char* file_root_;
|
||||
ScopedPtraceAttach* attachments_;
|
||||
size_t attach_count_;
|
||||
size_t attach_capacity_;
|
||||
|
@ -23,8 +23,10 @@
|
||||
|
||||
#include "build/build_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/filesystem.h"
|
||||
#include "test/linux/get_tls.h"
|
||||
#include "test/multiprocess.h"
|
||||
#include "test/scoped_temp_dir.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/linux/ptrace_client.h"
|
||||
#include "util/posix/scoped_mmap.h"
|
||||
@ -143,6 +145,23 @@ class SameBitnessTest : public Multiprocess {
|
||||
ScopedFileHandle broker_sock(socks[0]);
|
||||
ScopedFileHandle client_sock(socks[1]);
|
||||
|
||||
ScopedTempDir temp_dir;
|
||||
base::FilePath file_path(temp_dir.path().Append("test_file"));
|
||||
std::string expected_file_contents;
|
||||
{
|
||||
expected_file_contents.resize(4097);
|
||||
for (size_t i = 0; i < expected_file_contents.size(); ++i) {
|
||||
expected_file_contents[i] = static_cast<char>(i % 256);
|
||||
}
|
||||
ScopedFileHandle handle(
|
||||
LoggingOpenFileForWrite(file_path,
|
||||
FileWriteMode::kCreateOrFail,
|
||||
FilePermissions::kWorldReadable));
|
||||
ASSERT_TRUE(LoggingWriteFile(handle.get(),
|
||||
expected_file_contents.data(),
|
||||
expected_file_contents.size()));
|
||||
}
|
||||
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
constexpr bool am_64_bit = true;
|
||||
#else
|
||||
@ -192,6 +211,17 @@ class SameBitnessTest : public Multiprocess {
|
||||
EXPECT_FALSE(client.Read(mapping_.addr_as<VMAddress>() + mapping_.len(),
|
||||
sizeof(unmapped),
|
||||
&unmapped));
|
||||
|
||||
std::string file_root = temp_dir.path().value() + '/';
|
||||
broker.SetFileRoot(file_root.c_str());
|
||||
std::string file_contents;
|
||||
ASSERT_TRUE(client.ReadFileContents(file_path, &file_contents));
|
||||
EXPECT_EQ(file_contents, expected_file_contents);
|
||||
|
||||
ScopedTempDir temp_dir2;
|
||||
base::FilePath test_file2(temp_dir2.path().Append("test_file2"));
|
||||
ASSERT_TRUE(CreateFile(test_file2));
|
||||
EXPECT_FALSE(client.ReadFileContents(test_file2, &file_contents));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,6 +101,42 @@ bool PtraceClient::Initialize(int sock, pid_t pid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PtraceClient::Read(VMAddress address, size_t size, void* buffer) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
char* buffer_c = reinterpret_cast<char*>(buffer);
|
||||
|
||||
PtraceBroker::Request request;
|
||||
request.type = PtraceBroker::Request::kTypeReadMemory;
|
||||
request.tid = pid_;
|
||||
request.iov.base = address;
|
||||
request.iov.size = size;
|
||||
|
||||
if (!LoggingWriteFile(sock_, &request, sizeof(request))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (size > 0) {
|
||||
VMSize bytes_read;
|
||||
if (!LoggingReadFileExactly(sock_, &bytes_read, sizeof(bytes_read))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bytes_read) {
|
||||
ReceiveAndLogError(sock_, "PtraceBroker ReadMemory");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LoggingReadFileExactly(sock_, buffer_c, bytes_read)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size -= bytes_read;
|
||||
buffer_c += bytes_read;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pid_t PtraceClient::GetProcessID() {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return pid_;
|
||||
@ -140,40 +176,77 @@ bool PtraceClient::GetThreadInfo(pid_t tid, ThreadInfo* info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PtraceClient::Read(VMAddress address, size_t size, void* buffer) {
|
||||
bool PtraceClient::ReadFileContents(const base::FilePath& path,
|
||||
std::string* contents) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
char* buffer_c = reinterpret_cast<char*>(buffer);
|
||||
|
||||
PtraceBroker::Request request;
|
||||
request.type = PtraceBroker::Request::kTypeReadMemory;
|
||||
request.tid = pid_;
|
||||
request.iov.base = address;
|
||||
request.iov.size = size;
|
||||
request.type = PtraceBroker::Request::kTypeReadFile;
|
||||
request.path.path_length = path.value().size();
|
||||
|
||||
if (!LoggingWriteFile(sock_, &request, sizeof(request))) {
|
||||
if (!LoggingWriteFile(sock_, &request, sizeof(request)) ||
|
||||
!SendFilePath(path.value().c_str(), request.path.path_length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (size > 0) {
|
||||
VMSize bytes_read;
|
||||
if (!LoggingReadFileExactly(sock_, &bytes_read, sizeof(bytes_read))) {
|
||||
std::string local_contents;
|
||||
int32_t read_result;
|
||||
do {
|
||||
if (!LoggingReadFileExactly(sock_, &read_result, sizeof(read_result))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bytes_read) {
|
||||
ReceiveAndLogError(sock_, "PtraceBroker ReadMemory");
|
||||
if (read_result < 0) {
|
||||
ReceiveAndLogError(sock_, "ReadFileContents");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LoggingReadFileExactly(sock_, buffer_c, bytes_read)) {
|
||||
return false;
|
||||
if (read_result > 0) {
|
||||
size_t old_length = local_contents.size();
|
||||
local_contents.resize(old_length + read_result);
|
||||
if (!LoggingReadFileExactly(
|
||||
sock_, &local_contents[old_length], read_result)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (read_result > 0);
|
||||
|
||||
size -= bytes_read;
|
||||
buffer_c += bytes_read;
|
||||
}
|
||||
|
||||
contents->swap(local_contents);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PtraceClient::SendFilePath(const char* path, size_t length) {
|
||||
if (!LoggingWriteFile(sock_, path, length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PtraceBroker::OpenResult result;
|
||||
if (!LoggingReadFileExactly(sock_, &result, sizeof(result))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case PtraceBroker::kOpenResultAccessDenied:
|
||||
LOG(ERROR) << "Broker Open: access denied";
|
||||
return false;
|
||||
|
||||
case PtraceBroker::kOpenResultTooLong:
|
||||
LOG(ERROR) << "Broker Open: path too long";
|
||||
return false;
|
||||
|
||||
case PtraceBroker::kOpenResultSuccess:
|
||||
return true;
|
||||
|
||||
default:
|
||||
if (result < 0) {
|
||||
LOG(ERROR) << "Broker Open: invalid result " << result;
|
||||
DCHECK(false);
|
||||
} else {
|
||||
errno = result;
|
||||
PLOG(ERROR) << "Broker Open";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -70,8 +70,12 @@ class PtraceClient : public PtraceConnection {
|
||||
bool Attach(pid_t tid) override;
|
||||
bool Is64Bit() override;
|
||||
bool GetThreadInfo(pid_t tid, ThreadInfo* info) override;
|
||||
bool ReadFileContents(const base::FilePath& path,
|
||||
std::string* contents) override;
|
||||
|
||||
private:
|
||||
bool SendFilePath(const char* path, size_t length);
|
||||
|
||||
int sock_;
|
||||
pid_t pid_;
|
||||
bool is_64_bit_;
|
||||
|
@ -17,6 +17,9 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "util/linux/thread_info.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -45,6 +48,15 @@ class PtraceConnection {
|
||||
//! \param[out] info Information about the thread.
|
||||
//! \return `true` on success. `false` on failure with a message logged.
|
||||
virtual bool GetThreadInfo(pid_t tid, ThreadInfo* info) = 0;
|
||||
|
||||
//! \brief Reads the entire contents of a file.
|
||||
//!
|
||||
//! \param[in] path The path of the file to read.
|
||||
//! \param[out] contents The file contents, valid if this method returns
|
||||
//! `true`.
|
||||
//! \return `true` on success. `false` on failure with a message logged.
|
||||
virtual bool ReadFileContents(const base::FilePath& path,
|
||||
std::string* contents) = 0;
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
Loading…
x
Reference in New Issue
Block a user