// Copyright 2014 The Crashpad Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "minidump/test/minidump_writable_test_util.h" #include #include #include "gtest/gtest.h" #include "util/file/file_writer.h" #include "util/misc/implicit_cast.h" #include "util/numeric/in_range_cast.h" namespace crashpad { namespace test { namespace { //! \brief Returns an untyped minidump object located within a minidump file’s //! contents, where the offset of the object is known. //! //! \param[in] file_contents The contents of the minidump file. //! \param[in] rva The offset within the minidump file of the desired object. //! //! \return If \a rva is within the range of \a file_contents, returns a pointer //! into \a file_contents at offset \a rva. Otherwise, raises a Google Test //! assertion failure and returns `nullptr`. //! //! Do not call this function. Use the typed version, MinidumpWritableAtRVA<>(), //! or another type-specific function. template const void* MinidumpWritableAtRVAInternal(const std::string& file_contents, RVAType rva) { const auto rva_offset = crashpad::InRangeCast(rva, file_contents.size()); if (rva_offset >= file_contents.size()) { EXPECT_LT(rva_offset, file_contents.size()); return nullptr; } return &file_contents[rva_offset]; } template const void* TMinidumpWritableAtLocationDescriptorInternal( const std::string& file_contents, const MinidumpLocationDescriptorType& location, size_t expected_size, bool allow_oversized_data) { if (location.DataSize == 0) { EXPECT_EQ(location.Rva, RVAType(0)); return nullptr; } if (allow_oversized_data) { if (location.DataSize < expected_size) { EXPECT_GE(location.DataSize, expected_size); return nullptr; } } else if (location.DataSize != expected_size) { EXPECT_EQ(location.DataSize, expected_size); return nullptr; } RVAType end = location.Rva + location.DataSize; if (end > file_contents.size()) { EXPECT_LE(end, file_contents.size()); return nullptr; } const void* rv = MinidumpWritableAtRVAInternal(file_contents, location.Rva); return rv; } } // namespace const void* MinidumpWritableAtLocationDescriptorInternal( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location, size_t expected_size, bool allow_oversized_data) { return TMinidumpWritableAtLocationDescriptorInternal< RVA, MINIDUMP_LOCATION_DESCRIPTOR>( file_contents, location, expected_size, allow_oversized_data); } const void* MinidumpWritableAtLocationDescriptorInternal( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR64& location, size_t expected_size, bool allow_oversized_data) { return TMinidumpWritableAtLocationDescriptorInternal< RVA64, MINIDUMP_LOCATION_DESCRIPTOR64>( file_contents, location, expected_size, allow_oversized_data); } template <> const IMAGE_DEBUG_MISC* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { const IMAGE_DEBUG_MISC* misc = TMinidumpWritableAtLocationDescriptor(file_contents, location); if (!misc) { return nullptr; } if (misc->DataType != IMAGE_DEBUG_MISC_EXENAME) { EXPECT_EQ(misc->DataType, implicit_cast(IMAGE_DEBUG_MISC_EXENAME)); return nullptr; } if (misc->Length != location.DataSize) { EXPECT_EQ(misc->Length, location.DataSize); return nullptr; } if (misc->Unicode == 0) { size_t string_length = misc->Length - offsetof(IMAGE_DEBUG_MISC, Data) - 1; if (misc->Data[string_length] != '\0') { EXPECT_EQ(misc->Data[string_length], '\0'); return nullptr; } } else if (misc->Unicode == 1) { if (misc->Length % sizeof(char16_t) != 0) { EXPECT_EQ(misc->Length % sizeof(char16_t), 0u); return nullptr; } size_t string_length = (misc->Length - offsetof(IMAGE_DEBUG_MISC, Data)) / sizeof(char16_t) - 1; const char16_t* data16 = reinterpret_cast(misc->Data); if (data16[string_length] != '\0') { EXPECT_EQ(data16[string_length], '\0'); return nullptr; } } else { ADD_FAILURE() << "misc->Unicode " << misc->Unicode; return nullptr; } return misc; } template <> const MINIDUMP_HEADER* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { const MINIDUMP_HEADER* header = TMinidumpWritableAtLocationDescriptor(file_contents, location); if (!header) { return nullptr; } if (header->Signature != MINIDUMP_SIGNATURE) { EXPECT_EQ(header->Signature, implicit_cast(MINIDUMP_SIGNATURE)); return nullptr; } if (header->Version != MINIDUMP_VERSION) { EXPECT_EQ(header->Version, implicit_cast(MINIDUMP_VERSION)); return nullptr; } return header; } namespace { struct MinidumpMemoryListTraits { using ListType = MINIDUMP_MEMORY_LIST; enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_DESCRIPTOR) }; static size_t ElementCount(const ListType* list) { return list->NumberOfMemoryRanges; } }; struct MinidumpModuleListTraits { using ListType = MINIDUMP_MODULE_LIST; enum : size_t { kElementSize = sizeof(MINIDUMP_MODULE) }; static size_t ElementCount(const ListType* list) { return list->NumberOfModules; } }; struct MinidumpUnloadedModuleListTraits { using ListType = MINIDUMP_UNLOADED_MODULE_LIST; enum : size_t { kElementSize = sizeof(MINIDUMP_UNLOADED_MODULE) }; static size_t ElementCount(const ListType* list) { return list->NumberOfEntries; } }; struct MinidumpThreadListTraits { using ListType = MINIDUMP_THREAD_LIST; enum : size_t { kElementSize = sizeof(MINIDUMP_THREAD) }; static size_t ElementCount(const ListType* list) { return list->NumberOfThreads; } }; struct MinidumpThreadNameListTraits { using ListType = MINIDUMP_THREAD_NAME_LIST; enum : size_t { kElementSize = sizeof(MINIDUMP_THREAD_NAME) }; static size_t ElementCount(const ListType* list) { return list->NumberOfThreadNames; } }; struct MinidumpHandleDataStreamTraits { using ListType = MINIDUMP_HANDLE_DATA_STREAM; enum : size_t { kElementSize = sizeof(MINIDUMP_HANDLE_DESCRIPTOR) }; static size_t ElementCount(const ListType* list) { return static_cast(list->NumberOfDescriptors); } }; struct MinidumpMemoryInfoListTraits { using ListType = MINIDUMP_MEMORY_INFO_LIST; enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_INFO) }; static size_t ElementCount(const ListType* list) { return static_cast(list->NumberOfEntries); } }; struct MinidumpModuleCrashpadInfoListTraits { using ListType = MinidumpModuleCrashpadInfoList; enum : size_t { kElementSize = sizeof(MinidumpModuleCrashpadInfoLink) }; static size_t ElementCount(const ListType* list) { return list->count; } }; struct MinidumpSimpleStringDictionaryListTraits { using ListType = MinidumpSimpleStringDictionary; enum : size_t { kElementSize = sizeof(MinidumpSimpleStringDictionaryEntry) }; static size_t ElementCount(const ListType* list) { return list->count; } }; struct MinidumpAnnotationListObjectsTraits { using ListType = MinidumpAnnotationList; enum : size_t { kElementSize = sizeof(MinidumpAnnotation) }; static size_t ElementCount(const ListType* list) { return list->count; } }; template const typename T::ListType* MinidumpListAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { const typename T::ListType* list = TMinidumpWritableAtLocationDescriptor(file_contents, location); if (!list) { return nullptr; } size_t expected_size = sizeof(typename T::ListType) + T::ElementCount(list) * T::kElementSize; if (location.DataSize != expected_size) { EXPECT_EQ(location.DataSize, expected_size); return nullptr; } return list; } } // namespace template <> const MINIDUMP_MEMORY_LIST* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpListAtLocationDescriptor( file_contents, location); } template <> const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpListAtLocationDescriptor( file_contents, location); } template <> const MINIDUMP_UNLOADED_MODULE_LIST* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpListAtLocationDescriptor( file_contents, location); } template <> const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpListAtLocationDescriptor( file_contents, location); } template <> const MINIDUMP_THREAD_NAME_LIST* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpListAtLocationDescriptor( file_contents, location); } template <> const MINIDUMP_HANDLE_DATA_STREAM* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpListAtLocationDescriptor( file_contents, location); } template <> const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpListAtLocationDescriptor( file_contents, location); } template <> const MinidumpModuleCrashpadInfoList* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpListAtLocationDescriptor( file_contents, location); } template <> const MinidumpSimpleStringDictionary* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpListAtLocationDescriptor< MinidumpSimpleStringDictionaryListTraits>(file_contents, location); } template <> const MinidumpAnnotationList* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpListAtLocationDescriptor( file_contents, location); } namespace { template const T* MinidumpCVPDBAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { const T* cv_pdb = TMinidumpWritableAtLocationDescriptor(file_contents, location); if (!cv_pdb) { return nullptr; } if (cv_pdb->signature != T::kSignature) { EXPECT_EQ(cv_pdb->signature, T::kSignature); return nullptr; } size_t string_length = location.DataSize - offsetof(T, pdb_name) - 1; if (cv_pdb->pdb_name[string_length] != '\0') { EXPECT_EQ(cv_pdb->pdb_name[string_length], '\0'); return nullptr; } return cv_pdb; } } // namespace template <> const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpCVPDBAtLocationDescriptor(file_contents, location); } template <> const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { return MinidumpCVPDBAtLocationDescriptor(file_contents, location); } template <> const CodeViewRecordBuildID* MinidumpWritableAtLocationDescriptor( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location) { const CodeViewRecordBuildID* cv = reinterpret_cast( MinidumpWritableAtLocationDescriptorInternal( file_contents, location, offsetof(CodeViewRecordBuildID, build_id), true)); if (!cv) { return nullptr; } if (cv->signature != CodeViewRecordBuildID::kSignature) { return nullptr; } return cv; } TestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value) : MinidumpWritable(), value_(value) {} TestUInt32MinidumpWritable::~TestUInt32MinidumpWritable() {} size_t TestUInt32MinidumpWritable::SizeOfObject() { return sizeof(value_); } bool TestUInt32MinidumpWritable::WriteObject(FileWriterInterface* file_writer) { return file_writer->Write(&value_, sizeof(value_)); } } // namespace test } // namespace crashpad