kicad/thirdparty/sentry-native/external/crashpad/snapshot/win/pe_image_reader.cc

448 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2015 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 "snapshot/win/pe_image_reader.h"
#include <stddef.h>
#include <string.h>
#include <algorithm>
#include <iterator>
#include <memory>
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "client/crashpad_info.h"
#include "snapshot/win/pe_image_resource_reader.h"
#include "util/misc/from_pointer_cast.h"
#include "util/misc/pdb_structures.h"
#include "util/win/process_structs.h"
namespace crashpad {
namespace {
// Map from Traits to an IMAGE_NT_HEADERSxx.
template <class Traits>
struct NtHeadersForTraits;
template <>
struct NtHeadersForTraits<process_types::internal::Traits32> {
using type = IMAGE_NT_HEADERS32;
};
template <>
struct NtHeadersForTraits<process_types::internal::Traits64> {
using type = IMAGE_NT_HEADERS64;
};
} // namespace
PEImageReader::PEImageReader()
: module_subrange_reader_(),
initialized_() {
}
PEImageReader::~PEImageReader() {
}
bool PEImageReader::Initialize(ProcessReaderWin* process_reader,
WinVMAddress address,
WinVMSize size,
const std::string& module_name) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
if (!module_subrange_reader_.Initialize(
process_reader, address, size, module_name)) {
return false;
}
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
bool PEImageReader::GetCrashpadInfoSection(WinVMAddress* address,
WinVMSize* size) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (module_subrange_reader_.Is64Bit()) {
return GetCrashpadInfoSectionInternal<process_types::internal::Traits64>(
address, size);
} else {
return GetCrashpadInfoSectionInternal<process_types::internal::Traits32>(
address, size);
}
}
template <class Traits>
bool PEImageReader::GetCrashpadInfo(
process_types::CrashpadInfo<Traits>* crashpad_info) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
IMAGE_SECTION_HEADER section;
if (!GetSectionByName<typename NtHeadersForTraits<Traits>::type>("CPADinfo",
&section)) {
return false;
}
if (section.Misc.VirtualSize <
offsetof(process_types::CrashpadInfo<Traits>, size) +
sizeof(crashpad_info->size)) {
LOG(WARNING) << "small crashpad info section size "
<< section.Misc.VirtualSize << ", "
<< module_subrange_reader_.name();
return false;
}
const WinVMAddress crashpad_info_address = Address() + section.VirtualAddress;
const WinVMSize crashpad_info_size =
std::min(static_cast<WinVMSize>(sizeof(*crashpad_info)),
static_cast<WinVMSize>(section.Misc.VirtualSize));
if (!module_subrange_reader_.ReadMemory(
crashpad_info_address, crashpad_info_size, crashpad_info)) {
LOG(WARNING) << "could not read crashpad info from "
<< module_subrange_reader_.name();
return false;
}
if (crashpad_info->size < sizeof(*crashpad_info)) {
// Zero out anything beyond the structures declared size.
memset(reinterpret_cast<char*>(crashpad_info) + crashpad_info->size,
0,
sizeof(*crashpad_info) - crashpad_info->size);
}
if (crashpad_info->signature != CrashpadInfo::kSignature ||
crashpad_info->version != 1) {
LOG(WARNING) << base::StringPrintf(
"unexpected crashpad info signature 0x%x, version %u in %s",
crashpad_info->signature,
crashpad_info->version,
module_subrange_reader_.name().c_str());
return false;
}
// Dont require strict equality, to leave wiggle room for sloppy linkers.
if (crashpad_info->size > section.Misc.VirtualSize) {
LOG(WARNING) << "crashpad info struct size " << crashpad_info->size
<< " large for section size " << section.Misc.VirtualSize
<< " in " << module_subrange_reader_.name();
return false;
}
if (crashpad_info->size > sizeof(*crashpad_info)) {
// This isnt strictly a problem, because unknown fields will simply be
// ignored, but it may be of diagnostic interest.
LOG(INFO) << "large crashpad info size " << crashpad_info->size << ", "
<< module_subrange_reader_.name();
}
return true;
}
bool PEImageReader::DebugDirectoryInformation(UUID* uuid,
DWORD* age,
std::string* pdbname) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
IMAGE_DATA_DIRECTORY data_directory;
if (!ImageDataDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG, &data_directory))
return false;
IMAGE_DEBUG_DIRECTORY debug_directory;
if (data_directory.Size % sizeof(debug_directory) != 0)
return false;
for (size_t offset = 0; offset < data_directory.Size;
offset += sizeof(debug_directory)) {
if (!module_subrange_reader_.ReadMemory(
Address() + data_directory.VirtualAddress + offset,
sizeof(debug_directory),
&debug_directory)) {
LOG(WARNING) << "could not read data directory from "
<< module_subrange_reader_.name();
return false;
}
if (debug_directory.Type != IMAGE_DEBUG_TYPE_CODEVIEW)
continue;
if (debug_directory.AddressOfRawData) {
if (debug_directory.SizeOfData < sizeof(CodeViewRecordPDB70)) {
LOG(WARNING) << "CodeView debug entry of unexpected size in "
<< module_subrange_reader_.name();
continue;
}
std::unique_ptr<char[]> data(new char[debug_directory.SizeOfData]);
if (!module_subrange_reader_.ReadMemory(
Address() + debug_directory.AddressOfRawData,
debug_directory.SizeOfData,
data.get())) {
LOG(WARNING) << "could not read debug directory from "
<< module_subrange_reader_.name();
return false;
}
if (*reinterpret_cast<DWORD*>(data.get()) !=
CodeViewRecordPDB70::kSignature) {
LOG(WARNING) << "encountered non-7.0 CodeView debug record in "
<< module_subrange_reader_.name();
continue;
}
CodeViewRecordPDB70* codeview =
reinterpret_cast<CodeViewRecordPDB70*>(data.get());
*uuid = codeview->uuid;
*age = codeview->age;
// This is a NUL-terminated string encoded in the codepage of the system
// where the binary was linked. We have no idea what that was, so we just
// assume ASCII.
*pdbname = std::string(reinterpret_cast<char*>(&codeview->pdb_name[0]));
return true;
} else if (debug_directory.PointerToRawData) {
// This occurs for non-PDB based debug information. We simply ignore these
// as we don't expect to encounter modules that will be in this format
// for which we'll actually have symbols. See
// https://crashpad.chromium.org/bug/47.
}
}
return false;
}
bool PEImageReader::VSFixedFileInfo(
VS_FIXEDFILEINFO* vs_fixed_file_info) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
IMAGE_DATA_DIRECTORY data_directory;
if (!ImageDataDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE,
&data_directory)) {
return false;
}
PEImageResourceReader resource_reader;
if (!resource_reader.Initialize(module_subrange_reader_, data_directory)) {
return false;
}
WinVMAddress address;
WinVMSize size;
if (!resource_reader.FindResourceByID(
FromPointerCast<uint16_t>(VS_FILE_INFO), // RT_VERSION
VS_VERSION_INFO,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
&address,
&size,
nullptr)) {
return false;
}
// This structure is not declared anywhere in the SDK, but is documented at
// https://msdn.microsoft.com/library/ms647001.aspx.
struct VS_VERSIONINFO {
WORD wLength;
WORD wValueLength;
WORD wType;
// The structure documentation on MSDN doesnt show the [16], but it does
// say that its supposed to be L"VS_VERSION_INFO", which is is in fact a
// 16-character string (including its NUL terminator).
WCHAR szKey[16];
WORD Padding1;
VS_FIXEDFILEINFO Value;
// Dont include Children or the Padding2 that precedes it, because they may
// not be present.
// WORD Padding2;
// WORD Children;
};
VS_VERSIONINFO version_info;
if (size < sizeof(version_info)) {
LOG(WARNING) << "version info size " << size
<< " too small for structure of size " << sizeof(version_info)
<< " in " << module_subrange_reader_.name();
return false;
}
if (!module_subrange_reader_.ReadMemory(
address, sizeof(version_info), &version_info)) {
LOG(WARNING) << "could not read version info from "
<< module_subrange_reader_.name();
return false;
}
if (version_info.wLength < sizeof(version_info) ||
version_info.wValueLength != sizeof(version_info.Value) ||
version_info.wType != 0 ||
wcsncmp(version_info.szKey,
L"VS_VERSION_INFO",
std::size(version_info.szKey)) != 0) {
LOG(WARNING) << "unexpected VS_VERSIONINFO in "
<< module_subrange_reader_.name();
return false;
}
if (version_info.Value.dwSignature != VS_FFI_SIGNATURE ||
version_info.Value.dwStrucVersion != VS_FFI_STRUCVERSION) {
LOG(WARNING) << "unexpected VS_FIXEDFILEINFO in "
<< module_subrange_reader_.name();
return false;
}
*vs_fixed_file_info = version_info.Value;
vs_fixed_file_info->dwFileFlags &= vs_fixed_file_info->dwFileFlagsMask;
return true;
}
template <class Traits>
bool PEImageReader::GetCrashpadInfoSectionInternal(WinVMAddress* address,
WinVMSize* size) const {
IMAGE_SECTION_HEADER section;
if (!GetSectionByName<typename NtHeadersForTraits<Traits>::type>("CPADinfo",
&section)) {
return false;
}
process_types::CrashpadInfo<Traits> crashpad_info;
if (section.Misc.VirtualSize <
offsetof(process_types::CrashpadInfo<Traits>, size) +
sizeof(crashpad_info.size)) {
LOG(WARNING) << "small crashpad info section size "
<< section.Misc.VirtualSize << ", "
<< module_subrange_reader_.name();
return false;
}
*address = Address() + section.VirtualAddress;
*size = std::min<WinVMSize>(sizeof(crashpad_info), section.Misc.VirtualSize);
return true;
}
template <class NtHeadersType>
bool PEImageReader::ReadNtHeaders(NtHeadersType* nt_headers,
WinVMAddress* nt_headers_address) const {
IMAGE_DOS_HEADER dos_header;
if (!module_subrange_reader_.ReadMemory(
Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) {
LOG(WARNING) << "could not read dos header from "
<< module_subrange_reader_.name();
return false;
}
if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) {
LOG(WARNING) << "invalid e_magic in dos header of "
<< module_subrange_reader_.name();
return false;
}
WinVMAddress local_nt_headers_address = Address() + dos_header.e_lfanew;
if (!module_subrange_reader_.ReadMemory(
local_nt_headers_address, sizeof(NtHeadersType), nt_headers)) {
LOG(WARNING) << "could not read nt headers from "
<< module_subrange_reader_.name();
return false;
}
if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
LOG(WARNING) << "invalid signature in nt headers of "
<< module_subrange_reader_.name();
return false;
}
if (nt_headers_address)
*nt_headers_address = local_nt_headers_address;
return true;
}
template <class NtHeadersType>
bool PEImageReader::GetSectionByName(const std::string& name,
IMAGE_SECTION_HEADER* section) const {
if (name.size() > sizeof(section->Name)) {
LOG(WARNING) << "supplied section name too long " << name;
return false;
}
NtHeadersType nt_headers;
WinVMAddress nt_headers_address;
if (!ReadNtHeaders(&nt_headers, &nt_headers_address))
return false;
WinVMAddress first_section_address =
nt_headers_address + offsetof(NtHeadersType, OptionalHeader) +
nt_headers.FileHeader.SizeOfOptionalHeader;
for (DWORD i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) {
WinVMAddress section_address =
first_section_address + sizeof(IMAGE_SECTION_HEADER) * i;
if (!module_subrange_reader_.ReadMemory(
section_address, sizeof(IMAGE_SECTION_HEADER), section)) {
LOG(WARNING) << "could not read section " << i << " from "
<< module_subrange_reader_.name();
return false;
}
if (strncmp(reinterpret_cast<const char*>(section->Name),
name.c_str(),
sizeof(section->Name)) == 0) {
return true;
}
}
return false;
}
bool PEImageReader::ImageDataDirectoryEntry(size_t index,
IMAGE_DATA_DIRECTORY* entry) const {
bool rv;
if (module_subrange_reader_.Is64Bit()) {
rv = ImageDataDirectoryEntryT<IMAGE_NT_HEADERS64>(index, entry);
} else {
rv = ImageDataDirectoryEntryT<IMAGE_NT_HEADERS32>(index, entry);
}
return rv && entry->VirtualAddress != 0 && entry->Size != 0;
}
template <class NtHeadersType>
bool PEImageReader::ImageDataDirectoryEntryT(
size_t index,
IMAGE_DATA_DIRECTORY* entry) const {
NtHeadersType nt_headers;
if (!ReadNtHeaders(&nt_headers, nullptr)) {
return false;
}
if (nt_headers.FileHeader.SizeOfOptionalHeader <
offsetof(decltype(nt_headers.OptionalHeader), DataDirectory[index]) +
sizeof(nt_headers.OptionalHeader.DataDirectory[index]) ||
nt_headers.OptionalHeader.NumberOfRvaAndSizes <= index) {
return false;
}
*entry = nt_headers.OptionalHeader.DataDirectory[index];
return true;
}
// Explicit instantiations with the only 2 valid template arguments to avoid
// putting the body of the function in the header.
template bool PEImageReader::GetCrashpadInfo<process_types::internal::Traits32>(
process_types::CrashpadInfo<process_types::internal::Traits32>*
crashpad_info) const;
template bool PEImageReader::GetCrashpadInfo<process_types::internal::Traits64>(
process_types::CrashpadInfo<process_types::internal::Traits64>*
crashpad_info) const;
} // namespace crashpad