ios: Support intermediate dump extra_memory_ranges.

The extra_memory_ranges stored in the intermediate dump and used by
gwp-asan via the ProcessSnapshot are not needed in the minidump. If this
memory were stored in the minidump with gwp-asan, this would double the
size of every iOS minidump.

Instead, add a second bag that is stored in the intermediate dump but
not written to the minidump. Expose an iOS only ProcessSnapshot API that
can be used by the gwp-asan analyzer to use the extra_memory_ranges without increasing the minidump file size.

Change-Id: Ibf7b7fb89cda0a829727c02e5118dc2f6423769f
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/6351477
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
Commit-Queue: Justin Cohen <justincohen@chromium.org>
This commit is contained in:
Justin Cohen 2025-03-13 22:18:43 -04:00 committed by Crashpad LUCI CQ
parent 342bd4df0e
commit c405b3e9a0
12 changed files with 231 additions and 5 deletions

View File

@ -226,6 +226,10 @@ source_set("client_test") {
"../handler/win/wer:crashpad_wer_handler",
]
}
if (crashpad_is_ios) {
deps += [ "//minidump" ]
}
}
if (crashpad_is_linux || crashpad_is_android) {

View File

@ -137,7 +137,13 @@ CrashpadInfo::CrashpadInfo()
extra_memory_ranges_(nullptr),
simple_annotations_(nullptr),
user_data_minidump_stream_head_(nullptr),
annotations_list_(nullptr) {}
annotations_list_(nullptr)
#if BUILDFLAG(IS_IOS)
,
intermediate_dump_extra_memory_ranges_(nullptr)
#endif
{
}
UserDataMinidumpStreamHandle* CrashpadInfo::AddUserDataMinidumpStream(
uint32_t stream_type,

View File

@ -100,6 +100,35 @@ struct CrashpadInfo {
return extra_memory_ranges_;
}
#if BUILDFLAG(IS_IOS)
//! \brief Sets the bag of extra memory ranges to be included in the iOS
//! intermediate dump. This memory is not included in the minidump.
//!
//! Extra memory ranges may exist in \a address_range_bag at the time that
//! this method is called, or they may be added, removed, or modified in \a
//! address_range_bag after this method is called.
//!
//! This is only supported on iOS.
//!
//! \param[in] address_range_bag A bag of address ranges. The CrashpadInfo
//! object does not take ownership of the SimpleAddressRangeBag object.
//! It is the callers responsibility to ensure that this pointer remains
//! valid while it is in effect for a CrashpadInfo object.
//!
//! \sa extra_memory_ranges()
void set_intermediate_dump_extra_memory_ranges(
SimpleAddressRangeBag* address_range_bag) {
intermediate_dump_extra_memory_ranges_ = address_range_bag;
}
//! \return The simple extra memory ranges SimpleAddressRangeBag object.
//!
//! \sa set_extra_memory_ranges()
SimpleAddressRangeBag* intermediate_dump_extra_memory_ranges() const {
return intermediate_dump_extra_memory_ranges_;
}
#endif
//! \brief Sets the simple annotations dictionary.
//!
//! Simple annotations set on a CrashpadInfo structure are interpreted by
@ -305,6 +334,9 @@ struct CrashpadInfo {
SimpleStringDictionary* simple_annotations_; // weak
internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_;
AnnotationList* annotations_list_; // weak
#if BUILDFLAG(IS_IOS)
SimpleAddressRangeBag* intermediate_dump_extra_memory_ranges_; // weak
#endif
// Its generally safe to add new fields without changing
// kCrashpadInfoVersion, because readers should check size_ and ignore fields

View File

@ -1145,6 +1145,8 @@ void InProcessIntermediateDumpHandler::WriteDataSegmentAnnotations(
WriteCrashpadAnnotationsList(writer, crashpad_info.get());
WriteCrashpadSimpleAnnotationsDictionary(writer, crashpad_info.get());
WriteCrashpadExtraMemoryRanges(writer, crashpad_info.get());
WriteCrashpadIntermediateDumpExtraMemoryRanges(writer,
crashpad_info.get());
}
} else if (strcmp(section_vm_read_ptr->sectname, "__crash_info") == 0) {
ScopedVMRead<crashreporter_annotations_t> crash_info;
@ -1288,5 +1290,42 @@ void InProcessIntermediateDumpHandler::WriteCrashpadExtraMemoryRanges(
}
}
void InProcessIntermediateDumpHandler::
WriteCrashpadIntermediateDumpExtraMemoryRanges(
IOSIntermediateDumpWriter* writer,
CrashpadInfo* crashpad_info) {
if (!crashpad_info->intermediate_dump_extra_memory_ranges()) {
return;
}
ScopedVMRead<SimpleAddressRangeBag> intermediate_dump_extra_memory;
if (!intermediate_dump_extra_memory.Read(
crashpad_info->intermediate_dump_extra_memory_ranges())) {
CRASHPAD_RAW_LOG(
"Unable to read intermediate dump extra memory ranges object");
return;
}
IOSIntermediateDumpWriter::ScopedArray module_extra_memory_regions_array(
writer, IntermediateDumpKey::kModuleIntermediateDumpExtraMemoryRegions);
SimpleAddressRangeBag::Iterator iterator(
*(intermediate_dump_extra_memory.get()));
while (const SimpleAddressRangeBag::Entry* entry = iterator.Next()) {
const uint64_t& address = entry->base;
const uint64_t& size = entry->size;
IOSIntermediateDumpWriter::ScopedArrayMap memory_region_map(writer);
WriteProperty(
writer,
IntermediateDumpKey::kModuleIntermediateDumpExtraMemoryRegionAddress,
&address);
WritePropertyBytes(
writer,
IntermediateDumpKey::kModuleIntermediateDumpExtraMemoryRegionData,
reinterpret_cast<const void*>(address),
size);
}
}
} // namespace internal
} // namespace crashpad

View File

@ -156,6 +156,11 @@ class InProcessIntermediateDumpHandler final {
//! \brief Write Crashpad extra memory data.
static void WriteCrashpadExtraMemoryRanges(IOSIntermediateDumpWriter* writer,
CrashpadInfo* crashpad_info);
//! \brief Write Crashpad intermediate dump extra memory data.
static void WriteCrashpadIntermediateDumpExtraMemoryRanges(
IOSIntermediateDumpWriter* writer,
CrashpadInfo* crashpad_info);
};
} // namespace internal

View File

@ -25,7 +25,9 @@
#include "client/crashpad_info.h"
#include "client/simple_string_dictionary.h"
#include "gtest/gtest.h"
#include "minidump/minidump_file_writer.h"
#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h"
#include "snapshot/minidump/process_snapshot_minidump.h"
#include "test/scoped_set_thread_name.h"
#include "test/scoped_temp_dir.h"
#include "test/test_paths.h"
@ -251,12 +253,14 @@ TEST_F(InProcessIntermediateDumpHandlerTest, TestExtraMemoryRanges) {
}
#endif
constexpr char kSomeExtraMemoryString[] = "extra memory range";
// Put the string on the heap so the memory doesn't coalesce with the stack.
std::unique_ptr<std::string> someExtraMemoryString(
new std::string("extra memory range"));
crashpad::SimpleAddressRangeBag* ios_extra_ranges =
new crashpad::SimpleAddressRangeBag();
crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(
ios_extra_ranges);
ios_extra_ranges->Insert((void*)kSomeExtraMemoryString, 18);
ios_extra_ranges->Insert((void*)someExtraMemoryString->c_str(), 18);
WriteReportAndCloseWriter();
crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(nullptr);
internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
@ -264,11 +268,78 @@ TEST_F(InProcessIntermediateDumpHandlerTest, TestExtraMemoryRanges) {
ASSERT_EQ(process_snapshot.ExtraMemory().size(), 1LU);
auto memory = process_snapshot.ExtraMemory()[0];
EXPECT_EQ(memory->Address(),
reinterpret_cast<uint64_t>(kSomeExtraMemoryString));
reinterpret_cast<uint64_t>(someExtraMemoryString->c_str()));
EXPECT_EQ(memory->Size(), 18LU);
ReadToString delegate;
ASSERT_TRUE(memory->Read(&delegate));
EXPECT_EQ(delegate.result, kSomeExtraMemoryString);
EXPECT_EQ(delegate.result, someExtraMemoryString->c_str());
StringFile string_file;
MinidumpFileWriter minidump_file_writer;
minidump_file_writer.InitializeFromSnapshot(&process_snapshot);
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
ProcessSnapshotMinidump process_snapshot_minidump;
EXPECT_TRUE(process_snapshot_minidump.Initialize(&string_file));
bool found;
for (auto minidump_memory : process_snapshot_minidump.ExtraMemory()) {
if (minidump_memory->Address() ==
reinterpret_cast<uint64_t>(someExtraMemoryString->c_str()) &&
minidump_memory->Size() == 18LU) {
found = true;
break;
}
}
EXPECT_TRUE(found);
}
TEST_F(InProcessIntermediateDumpHandlerTest,
TestIntermediateDumpExtraMemoryRanges) {
#if TARGET_OS_SIMULATOR
// This test will fail on older (<iOS17 simulators) when running on macOS 14.3
// or newer due to a bug in Simulator. crbug.com/328282286
if (IsMacOSVersion143OrGreaterAndiOS16OrLess()) {
// For TearDown.
ASSERT_TRUE(LoggingRemoveFile(path()));
return;
}
#endif
// Put the string on the heap so the memory doesn't coalesce with the stack.
std::unique_ptr<std::string> someExtraMemoryString(
new std::string("extra memory range"));
crashpad::SimpleAddressRangeBag* ios_extra_ranges =
new crashpad::SimpleAddressRangeBag();
crashpad::CrashpadInfo::GetCrashpadInfo()
->set_intermediate_dump_extra_memory_ranges(ios_extra_ranges);
ios_extra_ranges->Insert((void*)someExtraMemoryString->c_str(), 18);
WriteReportAndCloseWriter();
crashpad::CrashpadInfo::GetCrashpadInfo()
->set_intermediate_dump_extra_memory_ranges(nullptr);
internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));
ASSERT_EQ(process_snapshot.IntermediateDumpExtraMemory().size(), 1LU);
auto memory = process_snapshot.IntermediateDumpExtraMemory()[0];
EXPECT_EQ(memory->Address(),
reinterpret_cast<uint64_t>(someExtraMemoryString->c_str()));
EXPECT_EQ(memory->Size(), 18LU);
ReadToString delegate;
ASSERT_TRUE(memory->Read(&delegate));
EXPECT_EQ(delegate.result, someExtraMemoryString->c_str());
StringFile string_file;
MinidumpFileWriter minidump_file_writer;
minidump_file_writer.InitializeFromSnapshot(&process_snapshot);
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
ProcessSnapshotMinidump process_snapshot_minidump;
EXPECT_TRUE(process_snapshot_minidump.Initialize(&string_file));
for (auto minidump_memory : process_snapshot_minidump.ExtraMemory()) {
EXPECT_FALSE(
minidump_memory->Address() ==
reinterpret_cast<uint64_t>(someExtraMemoryString->c_str()) &&
minidump_memory->Size() == 18LU);
}
}
TEST_F(InProcessIntermediateDumpHandlerTest, TestThreads) {

View File

@ -53,6 +53,9 @@ struct TestCrashpadInfo {
#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL)
void* user_data_minidump_stream_head_;
void* annotations_list_;
#if BUILDFLAG(IS_IOS)
void* intermediate_dump_extra_memory_ranges_;
#endif // BUILDFLAG(IS_IOS)
#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL
#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE)
uint8_t trailer_[64 * 1024];
@ -95,6 +98,10 @@ TestCrashpadInfo g_test_crashpad_info = {'CPad',
#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL)
nullptr,
nullptr,
#if BUILDFLAG(IS_IOS)
nullptr,
#endif // BUILDFLAG(IS_IOS)
#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL
#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE)
{}

View File

@ -154,6 +154,36 @@ bool ModuleSnapshotIOSIntermediateDump::Initialize(
}
}
const IOSIntermediateDumpList* intermediate_dump_extra_memory_regions_array =
image_data->GetAsList(
IntermediateDumpKey::kModuleIntermediateDumpExtraMemoryRegions);
if (intermediate_dump_extra_memory_regions_array) {
for (auto& region : *intermediate_dump_extra_memory_regions_array) {
vm_address_t address;
const IOSIntermediateDumpData* region_data =
region->GetAsData(Key::kModuleIntermediateDumpExtraMemoryRegionData);
if (!region_data)
continue;
if (GetDataValueFromMap(
region.get(),
Key::kModuleIntermediateDumpExtraMemoryRegionAddress,
&address)) {
const std::vector<uint8_t>& bytes = region_data->bytes();
vm_size_t data_size = bytes.size();
if (data_size == 0)
continue;
const vm_address_t data =
reinterpret_cast<const vm_address_t>(bytes.data());
auto memory =
std::make_unique<internal::MemorySnapshotIOSIntermediateDump>();
memory->Initialize(address, data, data_size);
intermediate_dump_extra_memory_.push_back(std::move(memory));
}
}
}
const IOSIntermediateDumpMap* crash_info_dump =
image_data->GetAsMap(IntermediateDumpKey::kAnnotationsCrashInfo);
if (crash_info_dump) {
@ -302,6 +332,15 @@ ModuleSnapshotIOSIntermediateDump::ExtraMemory() const {
return extra_memory;
}
std::vector<const MemorySnapshot*>
ModuleSnapshotIOSIntermediateDump::IntermediateDumpExtraMemory() const {
std::vector<const MemorySnapshot*> intermediate_dump_extra_memory;
for (const auto& memory : intermediate_dump_extra_memory_) {
intermediate_dump_extra_memory.push_back(memory.get());
}
return intermediate_dump_extra_memory;
}
std::vector<const UserMinidumpStream*>
ModuleSnapshotIOSIntermediateDump::CustomMinidumpStreams() const {
return std::vector<const UserMinidumpStream*>();

View File

@ -78,6 +78,7 @@ class ModuleSnapshotIOSIntermediateDump final : public ModuleSnapshot {
// Used by ProcessSnapshot
std::vector<const MemorySnapshot*> ExtraMemory() const;
std::vector<const MemorySnapshot*> IntermediateDumpExtraMemory() const;
private:
std::string name_;
@ -93,6 +94,8 @@ class ModuleSnapshotIOSIntermediateDump final : public ModuleSnapshot {
std::vector<AnnotationSnapshot> annotation_objects_;
std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>
extra_memory_;
std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>
intermediate_dump_extra_memory_;
InitializationStateDcheck initialized_;
};

View File

@ -313,6 +313,18 @@ ProcessSnapshotIOSIntermediateDump::ExtraMemory() const {
return extra_memory;
}
std::vector<const MemorySnapshot*>
ProcessSnapshotIOSIntermediateDump::IntermediateDumpExtraMemory() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::vector<const MemorySnapshot*> intermediate_dump_extra_memory;
for (const auto& module : modules_) {
for (const auto& memory : module->IntermediateDumpExtraMemory()) {
intermediate_dump_extra_memory.push_back(memory);
}
}
return intermediate_dump_extra_memory;
}
const ProcessMemory* ProcessSnapshotIOSIntermediateDump::Memory() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return nullptr;

View File

@ -99,6 +99,11 @@ class ProcessSnapshotIOSIntermediateDump final : public ProcessSnapshot {
std::vector<const MemorySnapshot*> ExtraMemory() const override;
const ProcessMemory* Memory() const override;
// Returns ModuleSnapshotIOSIntermediateDump::IntermediateDumpExtraMemory
// stored in the intermediate dump. This is used by UserExtensionStreams for
// processing the snapshot. This memory will not be written to a minidump
std::vector<const MemorySnapshot*> IntermediateDumpExtraMemory() const;
private:
// Retain the reader for the lifetime of the ProcessSnapshot so large chunks
// of data do not need to be copied around (such as MemorySnapshot

View File

@ -59,6 +59,9 @@ namespace internal {
TD(kModuleExtraMemoryRegions, 3019) \
TD(kModuleExtraMemoryRegionAddress, 3020) \
TD(kModuleExtraMemoryRegionData, 3021) \
TD(kModuleIntermediateDumpExtraMemoryRegions, 3022) \
TD(kModuleIntermediateDumpExtraMemoryRegionAddress, 3023) \
TD(kModuleIntermediateDumpExtraMemoryRegionData, 3024) \
TD(kProcessInfo, 4000) \
TD(kParentPID, 4001) \
TD(kPID, 4002) \