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", "../handler/win/wer:crashpad_wer_handler",
] ]
} }
if (crashpad_is_ios) {
deps += [ "//minidump" ]
}
} }
if (crashpad_is_linux || crashpad_is_android) { if (crashpad_is_linux || crashpad_is_android) {

View File

@ -137,7 +137,13 @@ CrashpadInfo::CrashpadInfo()
extra_memory_ranges_(nullptr), extra_memory_ranges_(nullptr),
simple_annotations_(nullptr), simple_annotations_(nullptr),
user_data_minidump_stream_head_(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( UserDataMinidumpStreamHandle* CrashpadInfo::AddUserDataMinidumpStream(
uint32_t stream_type, uint32_t stream_type,

View File

@ -100,6 +100,35 @@ struct CrashpadInfo {
return extra_memory_ranges_; 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. //! \brief Sets the simple annotations dictionary.
//! //!
//! Simple annotations set on a CrashpadInfo structure are interpreted by //! Simple annotations set on a CrashpadInfo structure are interpreted by
@ -305,6 +334,9 @@ struct CrashpadInfo {
SimpleStringDictionary* simple_annotations_; // weak SimpleStringDictionary* simple_annotations_; // weak
internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_; internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_;
AnnotationList* annotations_list_; // weak 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 // Its generally safe to add new fields without changing
// kCrashpadInfoVersion, because readers should check size_ and ignore fields // kCrashpadInfoVersion, because readers should check size_ and ignore fields

View File

@ -1145,6 +1145,8 @@ void InProcessIntermediateDumpHandler::WriteDataSegmentAnnotations(
WriteCrashpadAnnotationsList(writer, crashpad_info.get()); WriteCrashpadAnnotationsList(writer, crashpad_info.get());
WriteCrashpadSimpleAnnotationsDictionary(writer, crashpad_info.get()); WriteCrashpadSimpleAnnotationsDictionary(writer, crashpad_info.get());
WriteCrashpadExtraMemoryRanges(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) { } else if (strcmp(section_vm_read_ptr->sectname, "__crash_info") == 0) {
ScopedVMRead<crashreporter_annotations_t> crash_info; 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 internal
} // namespace crashpad } // namespace crashpad

View File

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

View File

@ -25,7 +25,9 @@
#include "client/crashpad_info.h" #include "client/crashpad_info.h"
#include "client/simple_string_dictionary.h" #include "client/simple_string_dictionary.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "minidump/minidump_file_writer.h"
#include "snapshot/ios/process_snapshot_ios_intermediate_dump.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_set_thread_name.h"
#include "test/scoped_temp_dir.h" #include "test/scoped_temp_dir.h"
#include "test/test_paths.h" #include "test/test_paths.h"
@ -251,12 +253,14 @@ TEST_F(InProcessIntermediateDumpHandlerTest, TestExtraMemoryRanges) {
} }
#endif #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 = crashpad::SimpleAddressRangeBag* ios_extra_ranges =
new crashpad::SimpleAddressRangeBag(); new crashpad::SimpleAddressRangeBag();
crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges( crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(
ios_extra_ranges); ios_extra_ranges);
ios_extra_ranges->Insert((void*)kSomeExtraMemoryString, 18); ios_extra_ranges->Insert((void*)someExtraMemoryString->c_str(), 18);
WriteReportAndCloseWriter(); WriteReportAndCloseWriter();
crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(nullptr); crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(nullptr);
internal::ProcessSnapshotIOSIntermediateDump process_snapshot; internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
@ -264,11 +268,78 @@ TEST_F(InProcessIntermediateDumpHandlerTest, TestExtraMemoryRanges) {
ASSERT_EQ(process_snapshot.ExtraMemory().size(), 1LU); ASSERT_EQ(process_snapshot.ExtraMemory().size(), 1LU);
auto memory = process_snapshot.ExtraMemory()[0]; auto memory = process_snapshot.ExtraMemory()[0];
EXPECT_EQ(memory->Address(), EXPECT_EQ(memory->Address(),
reinterpret_cast<uint64_t>(kSomeExtraMemoryString)); reinterpret_cast<uint64_t>(someExtraMemoryString->c_str()));
EXPECT_EQ(memory->Size(), 18LU); EXPECT_EQ(memory->Size(), 18LU);
ReadToString delegate; ReadToString delegate;
ASSERT_TRUE(memory->Read(&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) { TEST_F(InProcessIntermediateDumpHandlerTest, TestThreads) {

View File

@ -53,6 +53,9 @@ struct TestCrashpadInfo {
#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL) #if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL)
void* user_data_minidump_stream_head_; void* user_data_minidump_stream_head_;
void* annotations_list_; void* annotations_list_;
#if BUILDFLAG(IS_IOS)
void* intermediate_dump_extra_memory_ranges_;
#endif // BUILDFLAG(IS_IOS)
#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL #endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL
#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE) #if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE)
uint8_t trailer_[64 * 1024]; uint8_t trailer_[64 * 1024];
@ -95,6 +98,10 @@ TestCrashpadInfo g_test_crashpad_info = {'CPad',
#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL) #if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL)
nullptr, nullptr,
nullptr, nullptr,
#if BUILDFLAG(IS_IOS)
nullptr,
#endif // BUILDFLAG(IS_IOS)
#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL #endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL
#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE) #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 = const IOSIntermediateDumpMap* crash_info_dump =
image_data->GetAsMap(IntermediateDumpKey::kAnnotationsCrashInfo); image_data->GetAsMap(IntermediateDumpKey::kAnnotationsCrashInfo);
if (crash_info_dump) { if (crash_info_dump) {
@ -302,6 +332,15 @@ ModuleSnapshotIOSIntermediateDump::ExtraMemory() const {
return extra_memory; 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*> std::vector<const UserMinidumpStream*>
ModuleSnapshotIOSIntermediateDump::CustomMinidumpStreams() const { ModuleSnapshotIOSIntermediateDump::CustomMinidumpStreams() const {
return std::vector<const UserMinidumpStream*>(); return std::vector<const UserMinidumpStream*>();

View File

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

View File

@ -313,6 +313,18 @@ ProcessSnapshotIOSIntermediateDump::ExtraMemory() const {
return extra_memory; 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 { const ProcessMemory* ProcessSnapshotIOSIntermediateDump::Memory() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return nullptr; return nullptr;

View File

@ -99,6 +99,11 @@ class ProcessSnapshotIOSIntermediateDump final : public ProcessSnapshot {
std::vector<const MemorySnapshot*> ExtraMemory() const override; std::vector<const MemorySnapshot*> ExtraMemory() const override;
const ProcessMemory* Memory() 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: private:
// Retain the reader for the lifetime of the ProcessSnapshot so large chunks // Retain the reader for the lifetime of the ProcessSnapshot so large chunks
// of data do not need to be copied around (such as MemorySnapshot // of data do not need to be copied around (such as MemorySnapshot

View File

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