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: I38e7f89c7280f89bc4166baebfca8a6967ec4e09
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>
GitOrigin-RevId: 0a8985cd20d3d287a4a425c091448e926896513a
diff --git a/snapshot/linux/process_reader_linux.cc b/snapshot/linux/process_reader_linux.cc
index ee246e8..a9098ac 100644
--- a/snapshot/linux/process_reader_linux.cc
+++ b/snapshot/linux/process_reader_linux.cc
@@ -125,7 +125,8 @@
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 @@
// 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;
}
}
diff --git a/snapshot/linux/process_reader_linux.h b/snapshot/linux/process_reader_linux.h
index 0eb1d40..f44e15f 100644
--- a/snapshot/linux/process_reader_linux.h
+++ b/snapshot/linux/process_reader_linux.h
@@ -123,7 +123,7 @@
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_; }
diff --git a/snapshot/linux/process_reader_linux_test.cc b/snapshot/linux/process_reader_linux_test.cc
index 250f12d..f3791f8 100644
--- a/snapshot/linux/process_reader_linux_test.cc
+++ b/snapshot/linux/process_reader_linux_test.cc
@@ -291,9 +291,12 @@
#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) {
diff --git a/test/linux/fake_ptrace_connection.cc b/test/linux/fake_ptrace_connection.cc
index 774a340..33a8f08 100644
--- a/test/linux/fake_ptrace_connection.cc
+++ b/test/linux/fake_ptrace_connection.cc
@@ -80,7 +80,7 @@
return LoggingReadEntireFile(path, contents);
}
-ProcessMemory* FakePtraceConnection::Memory() {
+ProcessMemoryLinux* FakePtraceConnection::Memory() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!memory_) {
memory_ = std::make_unique<ProcessMemoryLinux>(this);
diff --git a/test/linux/fake_ptrace_connection.h b/test/linux/fake_ptrace_connection.h
index bf09d95..f6ba7de 100644
--- a/test/linux/fake_ptrace_connection.h
+++ b/test/linux/fake_ptrace_connection.h
@@ -63,7 +63,7 @@
//! \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;
diff --git a/util/linux/direct_ptrace_connection.cc b/util/linux/direct_ptrace_connection.cc
index 5da0147..3cc41c3 100644
--- a/util/linux/direct_ptrace_connection.cc
+++ b/util/linux/direct_ptrace_connection.cc
@@ -73,7 +73,7 @@
return LoggingReadEntireFile(path, contents);
}
-ProcessMemory* DirectPtraceConnection::Memory() {
+ProcessMemoryLinux* DirectPtraceConnection::Memory() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!memory_) {
memory_ = std::make_unique<ProcessMemoryLinux>(this);
diff --git a/util/linux/direct_ptrace_connection.h b/util/linux/direct_ptrace_connection.h
index a120889..391495f 100644
--- a/util/linux/direct_ptrace_connection.h
+++ b/util/linux/direct_ptrace_connection.h
@@ -58,13 +58,13 @@
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_;
diff --git a/util/linux/memory_map.cc b/util/linux/memory_map.cc
index dabc114..e15a1c5 100644
--- a/util/linux/memory_map.cc
+++ b/util/linux/memory_map.cc
@@ -240,7 +240,7 @@
executable(false),
shareable(false) {}
-MemoryMap::MemoryMap() : mappings_(), initialized_() {}
+MemoryMap::MemoryMap() : mappings_(), connection_(nullptr), initialized_() {}
MemoryMap::~MemoryMap() {}
@@ -256,6 +256,7 @@
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 @@
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 @@
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) {
diff --git a/util/linux/memory_map.h b/util/linux/memory_map.h
index d43b7af..f6f7cc8 100644
--- a/util/linux/memory_map.h
+++ b/util/linux/memory_map.h
@@ -129,6 +129,7 @@
private:
std::vector<Mapping> mappings_;
+ PtraceConnection* connection_;
InitializationStateDcheck initialized_;
};
diff --git a/util/linux/ptrace_client.cc b/util/linux/ptrace_client.cc
index cb1a1a5..9a34722 100644
--- a/util/linux/ptrace_client.cc
+++ b/util/linux/ptrace_client.cc
@@ -248,7 +248,7 @@
return true;
}
-ProcessMemory* PtraceClient::Memory() {
+ProcessMemoryLinux* PtraceClient::Memory() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!memory_) {
memory_ = std::make_unique<ProcessMemoryLinux>(this);
diff --git a/util/linux/ptrace_client.h b/util/linux/ptrace_client.h
index 299aafc..cd40f6b 100644
--- a/util/linux/ptrace_client.h
+++ b/util/linux/ptrace_client.h
@@ -60,14 +60,14 @@
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_;
diff --git a/util/linux/ptrace_connection.h b/util/linux/ptrace_connection.h
index 9b31d52..15594f8 100644
--- a/util/linux/ptrace_connection.h
+++ b/util/linux/ptrace_connection.h
@@ -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 @@
//!
//! 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.
//!
diff --git a/util/process/process_memory_linux.cc b/util/process/process_memory_linux.cc
index 56ac0ec..b67b9c0 100644
--- a/util/process/process_memory_linux.cc
+++ b/util/process/process_memory_linux.cc
@@ -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() {}
+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
diff --git a/util/process/process_memory_linux.h b/util/process/process_memory_linux.h
index 496cf2d..f62f2b4 100644
--- a/util/process/process_memory_linux.h
+++ b/util/process/process_memory_linux.h
@@ -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 @@
~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