blob: db07b366d66e0ccf65daa17009a7c8e9d902de05 [file] [log] [blame]
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// 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 <stddef.h>
#include <string>
#include "base/strings/string16.h"
#include "gtest/gtest.h"
#include "util/file/file_writer.h"
#include "util/misc/implicit_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 gtest
//! assertion failure and returns `nullptr`.
//!
//! Do not call this function. Use the typed version, MinidumpWritableAtRVA<>(),
//! or another type-specific function.
const void* MinidumpWritableAtRVAInternal(const std::string& file_contents,
RVA rva) {
if (rva >= file_contents.size()) {
EXPECT_LT(rva, file_contents.size());
return nullptr;
}
return &file_contents[rva];
}
} // namespace
const void* MinidumpWritableAtLocationDescriptorInternal(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location,
size_t expected_size,
bool allow_oversized_data) {
if (location.DataSize == 0) {
EXPECT_EQ(location.Rva, 0u);
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;
}
RVA 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;
}
template <>
const IMAGE_DEBUG_MISC* MinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
const IMAGE_DEBUG_MISC* misc =
TMinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>(file_contents,
location);
if (!misc) {
return nullptr;
}
if (misc->DataType != IMAGE_DEBUG_MISC_EXENAME) {
EXPECT_EQ(misc->DataType,
implicit_cast<uint32_t>(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(base::char16) != 0) {
EXPECT_EQ(misc->Length % sizeof(base::char16), 0u);
return nullptr;
}
size_t string_length = (misc->Length - offsetof(IMAGE_DEBUG_MISC, Data)) /
sizeof(base::char16) -
1;
const base::char16* data16 =
reinterpret_cast<const base::char16*>(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<MINIDUMP_HEADER>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
const MINIDUMP_HEADER* header =
TMinidumpWritableAtLocationDescriptor<MINIDUMP_HEADER>(file_contents,
location);
if (!header) {
return nullptr;
}
if (header->Signature != MINIDUMP_SIGNATURE) {
EXPECT_EQ(header->Signature, implicit_cast<uint32_t>(MINIDUMP_SIGNATURE));
return nullptr;
}
if (header->Version != MINIDUMP_VERSION) {
EXPECT_EQ(header->Version, implicit_cast<uint32_t>(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 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<size_t>(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<size_t>(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 <typename T>
const typename T::ListType* MinidumpListAtLocationDescriptor(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
const typename T::ListType* list =
TMinidumpWritableAtLocationDescriptor<typename T::ListType>(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<MINIDUMP_MEMORY_LIST>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpMemoryListTraits>(
file_contents, location);
}
template <>
const MINIDUMP_MODULE_LIST*
MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpModuleListTraits>(
file_contents, location);
}
template <>
const MINIDUMP_UNLOADED_MODULE_LIST*
MinidumpWritableAtLocationDescriptor<MINIDUMP_UNLOADED_MODULE_LIST>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpUnloadedModuleListTraits>(
file_contents, location);
}
template <>
const MINIDUMP_THREAD_LIST*
MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpThreadListTraits>(
file_contents, location);
}
template <>
const MINIDUMP_HANDLE_DATA_STREAM*
MinidumpWritableAtLocationDescriptor<MINIDUMP_HANDLE_DATA_STREAM>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpHandleDataStreamTraits>(
file_contents, location);
}
template <>
const MINIDUMP_MEMORY_INFO_LIST*
MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpMemoryInfoListTraits>(
file_contents, location);
}
template <>
const MinidumpModuleCrashpadInfoList*
MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpModuleCrashpadInfoListTraits>(
file_contents, location);
}
template <>
const MinidumpSimpleStringDictionary*
MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<
MinidumpSimpleStringDictionaryListTraits>(file_contents, location);
}
template <>
const MinidumpAnnotationList*
MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpAnnotationListObjectsTraits>(
file_contents, location);
}
namespace {
template <typename T>
const T* MinidumpCVPDBAtLocationDescriptor(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
const T* cv_pdb =
TMinidumpWritableAtLocationDescriptor<T>(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<CodeViewRecordPDB20>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB20>(file_contents,
location);
}
template <>
const CodeViewRecordPDB70*
MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB70>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB70>(file_contents,
location);
}
template <>
const CodeViewRecordBuildID*
MinidumpWritableAtLocationDescriptor<CodeViewRecordBuildID>(
const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
const CodeViewRecordBuildID* cv =
reinterpret_cast<const CodeViewRecordBuildID*>(
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