Add a mask to MinidumpCrashpadInfo to indicate valid pointer addresses.

ARM64 supports storing pointer authentication codes in the upper bits of
a pointer. This mask can be used by LLDB to mimic ptrauth_strip and
strip the pointer authentication codes. To recover an address from
pointer with an authentication code, `AND` this mask with the pointer.

If the platform does not support pointer authentication, or the range of
valid addressees for a pointer was unaccessible, this field will be 0
and should be ignored.

Change-Id: Ie5cef90802dd1e892d456195ab8874223eac6a1b
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2773358
Commit-Queue: Justin Cohen <justincohen@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Justin Cohen 2023-01-06 13:45:52 -05:00 committed by Crashpad LUCI CQ
parent 85b7d3dd6f
commit c11d49db88
22 changed files with 237 additions and 7 deletions

View File

@ -672,6 +672,8 @@ void InProcessIntermediateDumpHandler::WriteSystemInfo(
IntermediateDumpKey::kDaylightName,
daylight_name.c_str(),
daylight_name.length());
uint64_t address_mask = system_data.AddressMask();
WriteProperty(writer, IntermediateDumpKey::kAddressMask, &address_mask);
vm_size_t page_size;
host_page_size(mach_host_self(), &page_size);

View File

@ -20,6 +20,7 @@
#include "minidump/minidump_module_crashpad_info_writer.h"
#include "minidump/minidump_simple_string_dictionary_writer.h"
#include "snapshot/process_snapshot.h"
#include "snapshot/system_snapshot.h"
#include "util/file/file_writer.h"
namespace crashpad {
@ -30,6 +31,7 @@ MinidumpCrashpadInfoWriter::MinidumpCrashpadInfoWriter()
simple_annotations_(nullptr),
module_list_(nullptr) {
crashpad_info_.version = MinidumpCrashpadInfo::kVersion;
crashpad_info_.reserved = 0;
}
MinidumpCrashpadInfoWriter::~MinidumpCrashpadInfoWriter() {
@ -56,6 +58,10 @@ void MinidumpCrashpadInfoWriter::InitializeFromSnapshot(
SetSimpleAnnotations(std::move(simple_annotations));
}
if (process_snapshot->System()) {
SetAddressMask(process_snapshot->System()->AddressMask());
}
auto modules = std::make_unique<MinidumpModuleCrashpadInfoListWriter>();
modules->InitializeFromSnapshot(process_snapshot->Modules());
@ -90,6 +96,10 @@ void MinidumpCrashpadInfoWriter::SetModuleList(
module_list_ = std::move(module_list);
}
void MinidumpCrashpadInfoWriter::SetAddressMask(uint64_t mask) {
crashpad_info_.address_mask = mask;
}
bool MinidumpCrashpadInfoWriter::Freeze() {
DCHECK_EQ(state(), kStateMutable);

View File

@ -86,6 +86,9 @@ class MinidumpCrashpadInfoWriter final : public internal::MinidumpStreamWriter {
void SetModuleList(
std::unique_ptr<MinidumpModuleCrashpadInfoListWriter> module_list);
//! \brief Sets MinidumpCrashpadInfo::address_mask.
void SetAddressMask(uint64_t mask);
//! \brief Determines whether the object is useful.
//!
//! A useful object is one that carries data that makes a meaningful

View File

@ -122,6 +122,59 @@ TEST(MinidumpCrashpadInfoWriter, ReportAndClientID) {
EXPECT_FALSE(module_list);
}
TEST(MinidumpCrashpadInfoWriter, AddressMask) {
MinidumpFileWriter minidump_file_writer;
auto crashpad_info_writer = std::make_unique<MinidumpCrashpadInfoWriter>();
constexpr uint64_t mask = 0xFFFFFF8000000000;
crashpad_info_writer->SetAddressMask(mask);
ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer)));
StringFile string_file;
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
const MinidumpCrashpadInfo* crashpad_info = nullptr;
const MinidumpSimpleStringDictionary* simple_annotations = nullptr;
const MinidumpModuleCrashpadInfoList* module_list = nullptr;
ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(
string_file.string(), &crashpad_info, &simple_annotations, &module_list));
UUID empty_report_id;
ASSERT_TRUE(empty_report_id.InitializeFromString(
"00000000-0000-0000-0000-000000000000"));
UUID empty_client_id;
ASSERT_TRUE(empty_client_id.InitializeFromString(
"00000000-0000-0000-0000-000000000000"));
EXPECT_EQ(crashpad_info->version, MinidumpCrashpadInfo::kVersion);
EXPECT_EQ(crashpad_info->address_mask, mask);
EXPECT_EQ(crashpad_info->report_id, empty_report_id);
EXPECT_EQ(crashpad_info->client_id, empty_client_id);
EXPECT_FALSE(simple_annotations);
EXPECT_FALSE(module_list);
}
TEST(MinidumpCrashpadInfoWriter, EmptyAddressMask) {
MinidumpFileWriter minidump_file_writer;
auto crashpad_info_writer = std::make_unique<MinidumpCrashpadInfoWriter>();
ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer)));
StringFile string_file;
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
const MinidumpCrashpadInfo* crashpad_info = nullptr;
const MinidumpSimpleStringDictionary* simple_annotations = nullptr;
const MinidumpModuleCrashpadInfoList* module_list = nullptr;
ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(
string_file.string(), &crashpad_info, &simple_annotations, &module_list));
EXPECT_EQ(crashpad_info->address_mask, 0UL);
}
TEST(MinidumpCrashpadInfoWriter, SimpleAnnotations) {
MinidumpFileWriter minidump_file_writer;
auto crashpad_info_writer = std::make_unique<MinidumpCrashpadInfoWriter>();

View File

@ -444,8 +444,9 @@ struct ALIGNAS(4) PACKED MinidumpCrashpadInfo {
report_id(),
client_id(),
simple_annotations(),
module_list() {
}
module_list(),
reserved(),
address_mask() {}
//! \brief The structures currently-defined version number.
//!
@ -499,6 +500,28 @@ struct ALIGNAS(4) PACKED MinidumpCrashpadInfo {
//!
//! This field is present when #version is at least `1`.
MINIDUMP_LOCATION_DESCRIPTOR module_list;
//! \brief This field is always `0`.
uint32_t reserved;
//! \brief A mask indicating the range of valid addresses for a pointer.
//!
//! ARM64 supports MTE, TBI and PAC masking, generally in the upper bits of
//! a pointer. This mask can be used by LLDB to mimic ptrauth_strip and strip
//! the pointer authentication codes. To recover `pointer` in userland on
//! Darwin, `pointer & (~mask)`. In the case of code running in high memory,
//! where bit 55 is set (indicating that all of the high bits should be set
//! to 1), `pointer | mask`. See ABIMacOSX_arm64::FixAddress for more details
//! here:
//! https://github.com/llvm/llvm-project/blob/001d18664f8bcf63af64f10688809f7681dfbf0b/lldb/source/Plugins/ABI/AArch64/ABIMacOSX_arm64.cpp#L817-L830
//!
//! If the platform does not support pointer authentication, or the range of
//! valid addressees for a pointer was inaccessible, this field will be 0 and
//! should be ignored.
//!
//! This field is present when #version is at least `1`, if the size of the
//! structure is large enough to accommodate it.
uint64_t address_mask;
};
#if defined(COMPILER_MSVC)

View File

@ -73,6 +73,8 @@ class SystemSnapshotFuchsia final : public SystemSnapshot {
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name) const override;
uint64_t AddressMask() const override { return 0; }
private:
std::string os_version_full_;
const timeval* snapshot_time_; // weak

View File

@ -57,6 +57,7 @@ SystemSnapshotIOSIntermediateDump::SystemSnapshotIOSIntermediateDump()
daylight_offset_seconds_(0),
standard_name_(),
daylight_name_(),
address_mask_(0),
initialized_() {}
SystemSnapshotIOSIntermediateDump::~SystemSnapshotIOSIntermediateDump() {}
@ -97,6 +98,8 @@ void SystemSnapshotIOSIntermediateDump::Initialize(
dst_status_ = SystemSnapshot::kDoesNotObserveDaylightSavingTime;
}
GetDataValueFromMap(system_data, Key::kAddressMask, &address_mask_);
vm_size_t page_size;
if (GetDataValueFromMap(system_data, Key::kPageSize, &page_size)) {
const IOSIntermediateDumpMap* vm_stat =
@ -241,5 +244,10 @@ void SystemSnapshotIOSIntermediateDump::TimeZone(
daylight_name->assign(daylight_name_);
}
uint64_t SystemSnapshotIOSIntermediateDump::AddressMask() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return address_mask_;
}
} // namespace internal
} // namespace crashpad

View File

@ -73,6 +73,7 @@ class SystemSnapshotIOSIntermediateDump final : public SystemSnapshot {
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name) const override;
uint64_t AddressMask() const override;
private:
std::string os_version_build_;
@ -91,6 +92,7 @@ class SystemSnapshotIOSIntermediateDump final : public SystemSnapshot {
int daylight_offset_seconds_;
std::string standard_name_;
std::string daylight_name_;
uint64_t address_mask_;
InitializationStateDcheck initialized_;
};

View File

@ -89,6 +89,7 @@ class SystemSnapshotLinux final : public SystemSnapshot {
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name) const override;
uint64_t AddressMask() const override { return 0; }
private:
void ReadKernelVersion(const std::string& version_string);

View File

@ -390,5 +390,19 @@ void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status,
daylight_name);
}
uint64_t SystemSnapshotMac::AddressMask() const {
uint64_t mask = 0;
#if defined(ARCH_CPU_ARM64)
// `machdep.virtual_address_size` is the number of addressable bits in
// userspace virtual addresses
uint8_t addressable_bits =
CastIntSysctlByName<uint8_t>("machdep.virtual_address_size", 0);
if (addressable_bits) {
mask = ~((1UL << addressable_bits) - 1);
}
#endif
return mask;
}
} // namespace internal
} // namespace crashpad

View File

@ -83,6 +83,7 @@ class SystemSnapshotMac final : public SystemSnapshot {
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name) const override;
uint64_t AddressMask() const override;
private:
std::string os_version_full_;

View File

@ -266,7 +266,10 @@ bool ProcessSnapshotMinidump::InitializeCrashpadInfo() {
return true;
}
if (stream_it->second->DataSize < sizeof(crashpad_info_)) {
constexpr size_t crashpad_info_min_size =
offsetof(decltype(crashpad_info_), reserved);
size_t remaining_data_size = stream_it->second->DataSize;
if (remaining_data_size < crashpad_info_min_size) {
LOG(ERROR) << "crashpad_info size mismatch";
return false;
}
@ -275,9 +278,34 @@ bool ProcessSnapshotMinidump::InitializeCrashpadInfo() {
return false;
}
if (!file_reader_->ReadExactly(&crashpad_info_, sizeof(crashpad_info_))) {
if (!file_reader_->ReadExactly(&crashpad_info_, crashpad_info_min_size)) {
return false;
}
remaining_data_size -= crashpad_info_min_size;
// Read `reserved` if available.
size_t crashpad_reserved_size = sizeof(crashpad_info_.reserved);
if (remaining_data_size >= crashpad_reserved_size) {
if (!file_reader_->ReadExactly(&crashpad_info_.reserved,
crashpad_reserved_size)) {
return false;
}
remaining_data_size -= crashpad_reserved_size;
} else {
crashpad_info_.reserved = 0;
}
// Read `address_mask` if available.
size_t crashpad_address_mask_size = sizeof(crashpad_info_.address_mask);
if (remaining_data_size >= crashpad_address_mask_size) {
if (!file_reader_->ReadExactly(&crashpad_info_.address_mask,
crashpad_address_mask_size)) {
return false;
}
remaining_data_size -= crashpad_address_mask_size;
} else {
crashpad_info_.address_mask = 0;
}
if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) {
LOG(ERROR) << "crashpad_info version mismatch";

View File

@ -270,6 +270,47 @@ TEST(ProcessSnapshotMinidump, ClientID) {
EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty());
}
TEST(ProcessSnapshotMinidump, ReadOldCrashpadInfo) {
StringFile string_file;
MINIDUMP_HEADER header = {};
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
UUID client_id;
ASSERT_TRUE(
client_id.InitializeFromString("0001f4a9-d00d-5155-0a55-c0ffeec0ffee"));
MinidumpCrashpadInfo crashpad_info = {};
crashpad_info.version = MinidumpCrashpadInfo::kVersion;
crashpad_info.client_id = client_id;
MINIDUMP_DIRECTORY crashpad_info_directory = {};
crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;
crashpad_info_directory.Location.Rva =
static_cast<RVA>(string_file.SeekGet());
EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info) - 8));
crashpad_info_directory.Location.DataSize = sizeof(crashpad_info) - 8;
header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
EXPECT_TRUE(string_file.Write(&crashpad_info_directory,
sizeof(crashpad_info_directory)));
header.Signature = MINIDUMP_SIGNATURE;
header.Version = MINIDUMP_VERSION;
header.NumberOfStreams = 1;
EXPECT_TRUE(string_file.SeekSet(0));
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
ProcessSnapshotMinidump process_snapshot;
EXPECT_TRUE(process_snapshot.Initialize(&string_file));
UUID actual_client_id;
process_snapshot.ClientID(&actual_client_id);
EXPECT_EQ(actual_client_id, client_id);
EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty());
}
TEST(ProcessSnapshotMinidump, AnnotationsSimpleMap) {
StringFile string_file;

View File

@ -195,5 +195,11 @@ void SystemSnapshotMinidump::TimeZone(DaylightSavingTimeStatus* dst_status,
NOTREACHED(); // https://crashpad.chromium.org/bug/10
}
uint64_t SystemSnapshotMinidump::AddressMask() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://crashpad.chromium.org/bug/10
return 0;
}
} // namespace internal
} // namespace crashpad

View File

@ -74,6 +74,7 @@ class SystemSnapshotMinidump : public SystemSnapshot {
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name) const override;
uint64_t AddressMask() const override;
private:
MINIDUMP_SYSTEM_INFO minidump_system_info_;

View File

@ -275,6 +275,20 @@ class SystemSnapshot {
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name) const = 0;
//! \brief Returns a mask indicating the range of valid addresses for a
//! pointer.
//!
//! ARM64 supports storing pointer authentication codes in the upper bits of
//! a pointer. This mask is generated based on the number of bits in a pointer
//! reserved for the authentication codes. To recover an address from pointer
//! with an authentication code, `AND` this mask with the pointer. If the pac
//! sign extension bit is set, instead `~` and `OR` this mask with the
//! pointer.
//!
//! If the platform does not support pointer authentication, or the range of
//! valid addressees for a pointer was inaccessible, this field will be 0.
virtual uint64_t AddressMask() const = 0;
};
} // namespace crashpad

View File

@ -36,14 +36,14 @@ TestSystemSnapshot::TestSystemSnapshot()
os_version_bugfix_(0),
os_version_build_(),
os_version_full_(),
address_mask_(0),
nx_enabled_(false),
machine_description_(),
time_zone_dst_status_(kDoesNotObserveDaylightSavingTime),
time_zone_standard_offset_seconds_(0),
time_zone_daylight_offset_seconds_(0),
time_zone_standard_name_(),
time_zone_daylight_name_() {
}
time_zone_daylight_name_() {}
TestSystemSnapshot::~TestSystemSnapshot() {
}
@ -130,5 +130,9 @@ void TestSystemSnapshot::TimeZone(DaylightSavingTimeStatus* dst_status,
*daylight_name = time_zone_daylight_name_;
}
uint64_t TestSystemSnapshot::AddressMask() const {
return address_mask_;
}
} // namespace test
} // namespace crashpad

View File

@ -90,6 +90,8 @@ class TestSystemSnapshot final : public SystemSnapshot {
time_zone_daylight_name_ = daylight_name;
}
void SetAddressMask(uint64_t mask) { address_mask_ = mask; }
// SystemSnapshot:
CPUArchitecture GetCPUArchitecture() const override;
@ -114,6 +116,7 @@ class TestSystemSnapshot final : public SystemSnapshot {
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name) const override;
uint64_t AddressMask() const override;
private:
CPUArchitecture cpu_architecture_;
@ -134,6 +137,7 @@ class TestSystemSnapshot final : public SystemSnapshot {
int os_version_bugfix_;
std::string os_version_build_;
std::string os_version_full_;
uint64_t address_mask_;
bool nx_enabled_;
std::string machine_description_;
DaylightSavingTimeStatus time_zone_dst_status_;

View File

@ -79,6 +79,7 @@ class SystemSnapshotWin final : public SystemSnapshot {
int* daylight_offset_seconds,
std::string* standard_name,
std::string* daylight_name) const override;
uint64_t AddressMask() const override { return 0; }
private:
std::string os_version_full_;

View File

@ -85,6 +85,7 @@ namespace internal {
TD(kFree, 5017) \
TD(kInactive, 5018) \
TD(kWired, 5019) \
TD(kAddressMask, 5020) \
TD(kThreads, 6000) \
TD(kDebugState, 6001) \
TD(kFloatState, 6002) \
@ -103,7 +104,6 @@ namespace internal {
TD(kMaxValue, 65535) \
// clang-format on
//! \brief They key for items in the intermediate dump file.
//!
//! These values are persisted to the intermediate crash dump file. Entries

View File

@ -43,6 +43,7 @@ class IOSSystemDataCollector {
const std::string& StandardName() const { return standard_name_; }
const std::string& DaylightName() const { return daylight_name_; }
bool IsApplicationActive() const { return active_; }
uint64_t AddressMask() const { return address_mask_; }
// Currently unused by minidump.
int Orientation() const { return orientation_; }
@ -80,6 +81,7 @@ class IOSSystemDataCollector {
std::string standard_name_;
std::string daylight_name_;
ActiveApplicationCallback active_application_callback_;
uint64_t address_mask_;
};
} // namespace internal

View File

@ -102,6 +102,16 @@ IOSSystemDataCollector::IOSSystemDataCollector()
#if defined(ARCH_CPU_X86_64)
cpu_vendor_ = ReadStringSysctlByName("machdep.cpu.vendor");
#endif
uint32_t addressable_bits = 0;
size_t len = sizeof(uint32_t);
// `machdep.virtual_address_size` is the number of addressable bits in
// userspace virtual addresses
if (sysctlbyname(
"machdep.virtual_address_size", &addressable_bits, &len, NULL, 0) !=
0) {
addressable_bits = 0;
}
address_mask_ = ~((1UL << addressable_bits) - 1);
#if TARGET_OS_SIMULATOR
// TODO(justincohen): Consider adding board and model information to