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"; LOG(WARNING) << "no stack mapping";
return; 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 // We've hit what looks like a guard page; skip to the end and check for a
// mapped stack region. // 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 // at the high-address end of the stack so we can try using that to shrink
// the stack region. // the stack region.
stack_region_size = stack_end - stack_region_address; stack_region_size = stack_end - stack_region_address;
if (tid != reader->ProcessID() && VMAddress tls_address = reader->Memory()->PointerToAddress(
thread_info.thread_specific_data_address > stack_region_address && thread_info.thread_specific_data_address);
thread_info.thread_specific_data_address < stack_end) { if (tid != reader->ProcessID() && tls_address > stack_region_address &&
stack_region_size = tls_address < stack_end) {
thread_info.thread_specific_data_address - stack_region_address; stack_region_size = tls_address - stack_region_address;
} }
} }

View File

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

View File

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

View File

@ -80,7 +80,7 @@ bool FakePtraceConnection::ReadFileContents(const base::FilePath& path,
return LoggingReadEntireFile(path, contents); return LoggingReadEntireFile(path, contents);
} }
ProcessMemory* FakePtraceConnection::Memory() { ProcessMemoryLinux* FakePtraceConnection::Memory() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!memory_) { if (!memory_) {
memory_ = std::make_unique<ProcessMemoryLinux>(this); 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 //! \brief Attempts to create a ProcessMemory when called, calling
//! ADD_FAILURE() and returning `nullptr` on failure. //! ADD_FAILURE() and returning `nullptr` on failure.
ProcessMemory* Memory() override; ProcessMemoryLinux* Memory() override;
//! \todo Not yet implemented. //! \todo Not yet implemented.
bool Threads(std::vector<pid_t>* threads) override; 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); return LoggingReadEntireFile(path, contents);
} }
ProcessMemory* DirectPtraceConnection::Memory() { ProcessMemoryLinux* DirectPtraceConnection::Memory() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!memory_) { if (!memory_) {
memory_ = std::make_unique<ProcessMemoryLinux>(this); 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 GetThreadInfo(pid_t tid, ThreadInfo* info) override;
bool ReadFileContents(const base::FilePath& path, bool ReadFileContents(const base::FilePath& path,
std::string* contents) override; std::string* contents) override;
ProcessMemory* Memory() override; ProcessMemoryLinux* Memory() override;
bool Threads(std::vector<pid_t>* threads) override; bool Threads(std::vector<pid_t>* threads) override;
ssize_t ReadUpTo(VMAddress, size_t size, void* buffer) override; ssize_t ReadUpTo(VMAddress, size_t size, void* buffer) override;
private: private:
std::vector<std::unique_ptr<ScopedPtraceAttach>> attachments_; std::vector<std::unique_ptr<ScopedPtraceAttach>> attachments_;
std::unique_ptr<ProcessMemory> memory_; std::unique_ptr<ProcessMemoryLinux> memory_;
pid_t pid_; pid_t pid_;
Ptracer ptracer_; Ptracer ptracer_;
InitializationStateDcheck initialized_; InitializationStateDcheck initialized_;

View File

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

View File

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

View File

@ -248,7 +248,7 @@ bool PtraceClient::ReadFileContents(const base::FilePath& path,
return true; return true;
} }
ProcessMemory* PtraceClient::Memory() { ProcessMemoryLinux* PtraceClient::Memory() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!memory_) { if (!memory_) {
memory_ = std::make_unique<ProcessMemoryLinux>(this); 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 GetThreadInfo(pid_t tid, ThreadInfo* info) override;
bool ReadFileContents(const base::FilePath& path, bool ReadFileContents(const base::FilePath& path,
std::string* contents) override; std::string* contents) override;
ProcessMemory* Memory() override; ProcessMemoryLinux* Memory() override;
bool Threads(std::vector<pid_t>* threads) override; bool Threads(std::vector<pid_t>* threads) override;
ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) override; ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) override;
private: private:
bool SendFilePath(const char* path, size_t length); bool SendFilePath(const char* path, size_t length);
std::unique_ptr<ProcessMemory> memory_; std::unique_ptr<ProcessMemoryLinux> memory_;
int sock_; int sock_;
pid_t pid_; pid_t pid_;
bool is_64_bit_; bool is_64_bit_;

View File

@ -22,7 +22,7 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "util/linux/thread_info.h" #include "util/linux/thread_info.h"
#include "util/process/process_memory.h" #include "util/process/process_memory_linux.h"
namespace crashpad { namespace crashpad {
@ -64,7 +64,7 @@ class PtraceConnection {
//! //!
//! The caller does not take ownership of the reader. The reader is valid for //! The caller does not take ownership of the reader. The reader is valid for
//! the lifetime of the PtraceConnection that created it. //! 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. //! \brief Determines the thread IDs of the threads in the connected process.
//! //!

View File

@ -24,11 +24,20 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/posix/eintr_wrapper.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 { namespace crashpad {
ProcessMemoryLinux::ProcessMemoryLinux(PtraceConnection* connection) 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]; char path[32];
snprintf(path, sizeof(path), "/proc/%d/mem", connection->GetProcessID()); snprintf(path, sizeof(path), "/proc/%d/mem", connection->GetProcessID());
mem_fd_.reset(HANDLE_EINTR(open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC))); mem_fd_.reset(HANDLE_EINTR(open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC)));
@ -53,11 +62,15 @@ ProcessMemoryLinux::ProcessMemoryLinux(PtraceConnection* connection)
ProcessMemoryLinux::~ProcessMemoryLinux() {} ProcessMemoryLinux::~ProcessMemoryLinux() {}
VMAddress ProcessMemoryLinux::PointerToAddress(VMAddress address) const {
return ignore_top_byte_ ? address & 0x00ffffffffffffff : address;
}
ssize_t ProcessMemoryLinux::ReadUpTo(VMAddress address, ssize_t ProcessMemoryLinux::ReadUpTo(VMAddress address,
size_t size, size_t size,
void* buffer) const { void* buffer) const {
DCHECK_LE(size, size_t{std::numeric_limits<ssize_t>::max()}); 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 } // namespace crashpad

View File

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