linux,arm: support memory tagging

64-bit ARM's Top-Byte-Ignore enables features such as memory tagging.
https://www.kernel.org/doc/html/latest/arm64/tagged-address-abi.html

Android 11 will start using memory tagging on some devices.
https://source.android.com/devices/tech/debug/tagged-pointers

Crashpad needs to remove the tags from pointers before comparing to
addresses or using with system calls.

Bug: crashpad:364
Change-Id: I67c6b9a4a86d090e1d139de727eb06d9e222cc25
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3078500
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Joshua Peraza 2021-10-12 15:55:03 -07:00 committed by Crashpad LUCI CQ
parent dd53970380
commit 0a8985cd20
14 changed files with 52 additions and 26 deletions

View File

@ -125,7 +125,8 @@ void ProcessReaderLinux::Thread::InitializeStackFromSP(
LOG(WARNING) << "no stack mapping";
return;
}
LinuxVMAddress stack_region_start = stack_pointer;
LinuxVMAddress stack_region_start =
reader->Memory()->PointerToAddress(stack_pointer);
// We've hit what looks like a guard page; skip to the end and check for a
// mapped stack region.
@ -177,11 +178,11 @@ void ProcessReaderLinux::Thread::InitializeStackFromSP(
// at the high-address end of the stack so we can try using that to shrink
// the stack region.
stack_region_size = stack_end - stack_region_address;
if (tid != reader->ProcessID() &&
thread_info.thread_specific_data_address > stack_region_address &&
thread_info.thread_specific_data_address < stack_end) {
stack_region_size =
thread_info.thread_specific_data_address - stack_region_address;
VMAddress tls_address = reader->Memory()->PointerToAddress(
thread_info.thread_specific_data_address);
if (tid != reader->ProcessID() && tls_address > stack_region_address &&
tls_address < stack_end) {
stack_region_size = tls_address - stack_region_address;
}
}

View File

@ -123,7 +123,7 @@ class ProcessReaderLinux {
pid_t ParentProcessID() const { return process_info_.ParentProcessID(); }
//! \brief Return a memory reader for the target process.
const ProcessMemory* Memory() const { return connection_->Memory(); }
const ProcessMemoryLinux* Memory() const { return connection_->Memory(); }
//! \brief Return a memory map of the target process.
MemoryMap* GetMemoryMap() { return &memory_map_; }

View File

@ -291,9 +291,12 @@ void ExpectThreads(const ThreadMap& thread_map,
#if !defined(ADDRESS_SANITIZER)
// AddressSanitizer causes stack variables to be stored separately from the
// call stack.
EXPECT_LE(thread.stack_region_address, iterator->second.stack_address);
EXPECT_GE(thread.stack_region_address + thread.stack_region_size,
iterator->second.stack_address);
EXPECT_LE(
thread.stack_region_address,
connection->Memory()->PointerToAddress(iterator->second.stack_address));
EXPECT_GE(
thread.stack_region_address + thread.stack_region_size,
connection->Memory()->PointerToAddress(iterator->second.stack_address));
#endif // !defined(ADDRESS_SANITIZER)
if (iterator->second.max_stack_size) {

View File

@ -80,7 +80,7 @@ bool FakePtraceConnection::ReadFileContents(const base::FilePath& path,
return LoggingReadEntireFile(path, contents);
}
ProcessMemory* FakePtraceConnection::Memory() {
ProcessMemoryLinux* FakePtraceConnection::Memory() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!memory_) {
memory_ = std::make_unique<ProcessMemoryLinux>(this);

View File

@ -63,7 +63,7 @@ class FakePtraceConnection : public PtraceConnection {
//! \brief Attempts to create a ProcessMemory when called, calling
//! ADD_FAILURE() and returning `nullptr` on failure.
ProcessMemory* Memory() override;
ProcessMemoryLinux* Memory() override;
//! \todo Not yet implemented.
bool Threads(std::vector<pid_t>* threads) override;

View File

@ -73,7 +73,7 @@ bool DirectPtraceConnection::ReadFileContents(const base::FilePath& path,
return LoggingReadEntireFile(path, contents);
}
ProcessMemory* DirectPtraceConnection::Memory() {
ProcessMemoryLinux* DirectPtraceConnection::Memory() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!memory_) {
memory_ = std::make_unique<ProcessMemoryLinux>(this);

View File

@ -58,13 +58,13 @@ class DirectPtraceConnection : public PtraceConnection {
bool GetThreadInfo(pid_t tid, ThreadInfo* info) override;
bool ReadFileContents(const base::FilePath& path,
std::string* contents) override;
ProcessMemory* Memory() override;
ProcessMemoryLinux* Memory() override;
bool Threads(std::vector<pid_t>* threads) override;
ssize_t ReadUpTo(VMAddress, size_t size, void* buffer) override;
private:
std::vector<std::unique_ptr<ScopedPtraceAttach>> attachments_;
std::unique_ptr<ProcessMemory> memory_;
std::unique_ptr<ProcessMemoryLinux> memory_;
pid_t pid_;
Ptracer ptracer_;
InitializationStateDcheck initialized_;

View File

@ -240,7 +240,7 @@ MemoryMap::Mapping::Mapping()
executable(false),
shareable(false) {}
MemoryMap::MemoryMap() : mappings_(), initialized_() {}
MemoryMap::MemoryMap() : mappings_(), connection_(nullptr), initialized_() {}
MemoryMap::~MemoryMap() {}
@ -256,6 +256,7 @@ bool MemoryMap::Mapping::Equals(const Mapping& other) const {
bool MemoryMap::Initialize(PtraceConnection* connection) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
connection_ = connection;
// If the maps file is not read atomically, entries can be read multiple times
// or missed entirely. The kernel reads entries from this file into a page
@ -268,8 +269,8 @@ bool MemoryMap::Initialize(PtraceConnection* connection) {
do {
std::string contents;
char path[32];
snprintf(path, sizeof(path), "/proc/%d/maps", connection->GetProcessID());
if (!connection->ReadFileContents(base::FilePath(path), &contents)) {
snprintf(path, sizeof(path), "/proc/%d/maps", connection_->GetProcessID());
if (!connection_->ReadFileContents(base::FilePath(path), &contents)) {
return false;
}
@ -298,6 +299,7 @@ bool MemoryMap::Initialize(PtraceConnection* connection) {
const MemoryMap::Mapping* MemoryMap::FindMapping(LinuxVMAddress address) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
address = connection_->Memory()->PointerToAddress(address);
for (const auto& mapping : mappings_) {
if (mapping.range.Base() <= address && mapping.range.End() > address) {

View File

@ -129,6 +129,7 @@ class MemoryMap {
private:
std::vector<Mapping> mappings_;
PtraceConnection* connection_;
InitializationStateDcheck initialized_;
};

View File

@ -248,7 +248,7 @@ bool PtraceClient::ReadFileContents(const base::FilePath& path,
return true;
}
ProcessMemory* PtraceClient::Memory() {
ProcessMemoryLinux* PtraceClient::Memory() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!memory_) {
memory_ = std::make_unique<ProcessMemoryLinux>(this);

View File

@ -60,14 +60,14 @@ class PtraceClient : public PtraceConnection {
bool GetThreadInfo(pid_t tid, ThreadInfo* info) override;
bool ReadFileContents(const base::FilePath& path,
std::string* contents) override;
ProcessMemory* Memory() override;
ProcessMemoryLinux* Memory() override;
bool Threads(std::vector<pid_t>* threads) override;
ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) override;
private:
bool SendFilePath(const char* path, size_t length);
std::unique_ptr<ProcessMemory> memory_;
std::unique_ptr<ProcessMemoryLinux> memory_;
int sock_;
pid_t pid_;
bool is_64_bit_;

View File

@ -22,7 +22,7 @@
#include "base/files/file_path.h"
#include "util/linux/thread_info.h"
#include "util/process/process_memory.h"
#include "util/process/process_memory_linux.h"
namespace crashpad {
@ -64,7 +64,7 @@ class PtraceConnection {
//!
//! The caller does not take ownership of the reader. The reader is valid for
//! the lifetime of the PtraceConnection that created it.
virtual ProcessMemory* Memory() = 0;
virtual ProcessMemoryLinux* Memory() = 0;
//! \brief Determines the thread IDs of the threads in the connected process.
//!

View File

@ -24,11 +24,20 @@
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "build/build_config.h"
#include "util/file/filesystem.h"
#include "util/linux/ptrace_connection.h"
namespace crashpad {
ProcessMemoryLinux::ProcessMemoryLinux(PtraceConnection* connection)
: ProcessMemory(), mem_fd_() {
: ProcessMemory(), mem_fd_(), ignore_top_byte_(false) {
#if defined(ARCH_CPU_ARM_FAMILY)
if (connection->Is64Bit()) {
ignore_top_byte_ = true;
}
#endif // ARCH_CPU_ARM_FAMILY
char path[32];
snprintf(path, sizeof(path), "/proc/%d/mem", connection->GetProcessID());
mem_fd_.reset(HANDLE_EINTR(open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC)));
@ -53,11 +62,15 @@ ProcessMemoryLinux::ProcessMemoryLinux(PtraceConnection* connection)
ProcessMemoryLinux::~ProcessMemoryLinux() {}
VMAddress ProcessMemoryLinux::PointerToAddress(VMAddress address) const {
return ignore_top_byte_ ? address & 0x00ffffffffffffff : address;
}
ssize_t ProcessMemoryLinux::ReadUpTo(VMAddress address,
size_t size,
void* buffer) const {
DCHECK_LE(size, size_t{std::numeric_limits<ssize_t>::max()});
return read_up_to_(address, size, buffer);
return read_up_to_(PointerToAddress(address), size, buffer);
}
} // namespace crashpad

View File

@ -21,12 +21,13 @@
#include <string>
#include "base/files/scoped_file.h"
#include "util/linux/ptrace_connection.h"
#include "util/misc/address_types.h"
#include "util/process/process_memory.h"
namespace crashpad {
class PtraceConnection;
//! \brief Accesses the memory of another Linux process.
class ProcessMemoryLinux final : public ProcessMemory {
public:
@ -37,11 +38,16 @@ class ProcessMemoryLinux final : public ProcessMemory {
~ProcessMemoryLinux();
//! \brief Returns the input pointer with any non-addressing bits, such as
//! tags removed.
VMAddress PointerToAddress(VMAddress address) const;
private:
ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;
std::function<ssize_t(VMAddress, size_t, void*)> read_up_to_;
base::ScopedFD mem_fd_;
bool ignore_top_byte_;
};
} // namespace crashpad