diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc')
-rw-r--r-- | toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc b/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc new file mode 100644 index 0000000000..54d8e25274 --- /dev/null +++ b/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc @@ -0,0 +1,319 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "minidump_callback.h" + +#include <winternl.h> + +#include <algorithm> +#include <cassert> + +namespace google_breakpad { + +static const DWORD sHeapRegionSize= 1024; +static DWORD sPageSize = 0; + +using NtQueryInformationThreadFunc = decltype(::NtQueryInformationThread); +static NtQueryInformationThreadFunc* sNtQueryInformationThread = nullptr; + + +namespace { +enum { + ThreadBasicInformation, +}; + +struct CLIENT_ID { + PVOID UniqueProcess; + PVOID UniqueThread; +}; + +struct THREAD_BASIC_INFORMATION { + NTSTATUS ExitStatus; + PVOID TebBaseAddress; + CLIENT_ID ClientId; + KAFFINITY AffMask; + DWORD Priority; + DWORD BasePriority; +}; +} + +void InitAppMemoryInternal() +{ + if (!sPageSize) { + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + sPageSize = systemInfo.dwPageSize; + } + + if (!sNtQueryInformationThread) { + sNtQueryInformationThread = (NtQueryInformationThreadFunc*) + (::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), + "NtQueryInformationThread")); + } +} + +bool GetAppMemoryFromRegister(HANDLE aProcess, + const NT_TIB* aTib, + RegisterValueType aRegister, + AppMemory* aResult) +{ + static_assert(sizeof(RegisterValueType) == sizeof(void*), + "Size mismatch between DWORD/DWORD64 and void*"); + + if (!sPageSize) { + // GetSystemInfo() should not fail, but bail out just in case it fails. + return false; + } + + RegisterValueType addr = aRegister; + addr &= ~(static_cast<RegisterValueType>(sPageSize) - 1); + + if (aTib) { + if (aRegister >= (RegisterValueType)aTib->StackLimit && + aRegister <= (RegisterValueType)aTib->StackBase) { + // aRegister points to the stack. + return false; + } + } + + MEMORY_BASIC_INFORMATION memInfo; + memset(&memInfo, 0, sizeof(memInfo)); + SIZE_T rv = ::VirtualQueryEx(aProcess, + reinterpret_cast<void*>(addr), + &memInfo, + sizeof(memInfo)); + if (!rv) { + // VirtualQuery fails: aAddr is not on heap. + return false; + } + + // Check protection and type of the memory region. Include the region if it's + // 1. read-write: heap, or + // 2. read-executable and private: likely to be JIT code. + if (memInfo.Protect != PAGE_READWRITE && + memInfo.Protect != PAGE_EXECUTE_READ) { + return false; + } + + // Try to include a region of size sHeapRegionSize around aRegister, bounded + // by the [BaseAddress, BaseAddress + RegionSize]. + RegisterValueType lower = + std::max(aRegister - sHeapRegionSize / 2, + reinterpret_cast<RegisterValueType>(memInfo.BaseAddress)); + + RegisterValueType upper = + std::min(lower + sHeapRegionSize, + reinterpret_cast<RegisterValueType>(memInfo.BaseAddress) + + memInfo.RegionSize); + + aResult->ptr = lower; + aResult->length = upper - lower; + + return true; +} + +static AppMemoryList::iterator +FindNextPreallocated(AppMemoryList& aList, AppMemoryList::iterator aBegin) { + auto it = aBegin; + for (auto it = aBegin; it != aList.end(); it++) { + if (it->preallocated) { + return it; + } + } + + assert(it == aList.end()); + return it; +} + +static bool +GetThreadTib(HANDLE aProcess, DWORD aThreadId, NT_TIB* aTib) { + HANDLE threadHandle = ::OpenThread(THREAD_QUERY_INFORMATION, + FALSE, + aThreadId); + if (!threadHandle) { + return false; + } + + if (!sNtQueryInformationThread) { + return false; + } + + THREAD_BASIC_INFORMATION threadInfo; + auto status = (*sNtQueryInformationThread)(threadHandle, + (THREADINFOCLASS)ThreadBasicInformation, + &threadInfo, + sizeof(threadInfo), + NULL); + if (!NT_SUCCESS(status)) { + return false; + } + + auto readSuccess = ::ReadProcessMemory(aProcess, + threadInfo.TebBaseAddress, + aTib, + sizeof(*aTib), + NULL); + if (!readSuccess) { + return false; + } + + ::CloseHandle(threadHandle); + return true; +} + +void IncludeAppMemoryFromExceptionContext(HANDLE aProcess, + DWORD aThreadId, + AppMemoryList& aList, + PCONTEXT aExceptionContext, + bool aInstructionPointerOnly) { + RegisterValueType heapAddrCandidates[kExceptionAppMemoryRegions]; + size_t numElements = 0; + + NT_TIB tib; + memset(&tib, 0, sizeof(tib)); + if (!GetThreadTib(aProcess, aThreadId, &tib)) { + // Fail to query thread stack range: only safe to include the region around + // the instruction pointer. + aInstructionPointerOnly = true; + } + + // Add registers that might have a heap address to heapAddrCandidates. + // Note that older versions of DbgHelp.dll don't correctly put the memory + // around the faulting instruction pointer into the minidump. Include Rip/Eip + // unconditionally ensures it gets included. +#if defined(_M_IX86) + if (!aInstructionPointerOnly) { + heapAddrCandidates[numElements++] = aExceptionContext->Eax; + heapAddrCandidates[numElements++] = aExceptionContext->Ebx; + heapAddrCandidates[numElements++] = aExceptionContext->Ecx; + heapAddrCandidates[numElements++] = aExceptionContext->Edx; + heapAddrCandidates[numElements++] = aExceptionContext->Esi; + heapAddrCandidates[numElements++] = aExceptionContext->Edi; + } + heapAddrCandidates[numElements++] = aExceptionContext->Eip; +#elif defined(_M_AMD64) + if (!aInstructionPointerOnly) { + heapAddrCandidates[numElements++] = aExceptionContext->Rax; + heapAddrCandidates[numElements++] = aExceptionContext->Rbx; + heapAddrCandidates[numElements++] = aExceptionContext->Rcx; + heapAddrCandidates[numElements++] = aExceptionContext->Rdx; + heapAddrCandidates[numElements++] = aExceptionContext->Rsi; + heapAddrCandidates[numElements++] = aExceptionContext->Rdi; + heapAddrCandidates[numElements++] = aExceptionContext->R8; + heapAddrCandidates[numElements++] = aExceptionContext->R9; + heapAddrCandidates[numElements++] = aExceptionContext->R10; + heapAddrCandidates[numElements++] = aExceptionContext->R11; + heapAddrCandidates[numElements++] = aExceptionContext->R12; + heapAddrCandidates[numElements++] = aExceptionContext->R13; + heapAddrCandidates[numElements++] = aExceptionContext->R14; + heapAddrCandidates[numElements++] = aExceptionContext->R15; + } + heapAddrCandidates[numElements++] = aExceptionContext->Rip; +#elif defined(_M_ARM64) + if (!aInstructionPointerOnly) { + for (auto reg : aExceptionContext->X) { + heapAddrCandidates[numElements++] = reg; + } + heapAddrCandidates[numElements++] = aExceptionContext->Sp; + } + heapAddrCandidates[numElements++] = aExceptionContext->Pc; +#endif + + // Inplace sort the candidates for excluding or merging memory regions. + auto begin = &heapAddrCandidates[0], end = &heapAddrCandidates[numElements]; + std::make_heap(begin, end); + std::sort_heap(begin, end); + + auto appMemory = FindNextPreallocated(aList, aList.begin()); + for (size_t i = 0; i < numElements; i++) { + if (appMemory == aList.end()) { + break; + } + + AppMemory tmp{}; + if (!GetAppMemoryFromRegister(aProcess, + aInstructionPointerOnly ? nullptr : &tib, + heapAddrCandidates[i], + &tmp)) { + continue; + } + + if (!(tmp.ptr && tmp.length)) { + // Something unexpected happens. Skip this candidate. + continue; + } + + if (!appMemory->ptr) { + *appMemory = tmp; + continue; + } + + if (appMemory->ptr + appMemory->length > tmp.ptr) { + // The beginning of the next region fall within the range of the previous + // region: merge into one. Note that we don't merge adjacent regions like + // [0, 99] and [100, 199] in case we cross the border of memory allocation + // regions. + appMemory->length = tmp.ptr + tmp.length - appMemory->ptr; + continue; + } + + appMemory = FindNextPreallocated(aList, ++appMemory); + if (appMemory == aList.end()) { + break; + } + + *appMemory = tmp; + } +} + +BOOL CALLBACK MinidumpWriteDumpCallback( + PVOID context, + const PMINIDUMP_CALLBACK_INPUT callback_input, + PMINIDUMP_CALLBACK_OUTPUT callback_output) { + switch (callback_input->CallbackType) { + case MemoryCallback: { + MinidumpCallbackContext* callback_context = + reinterpret_cast<MinidumpCallbackContext*>(context); + + // Skip unused preallocated AppMemory elements. + while (callback_context->iter != callback_context->end && + callback_context->iter->preallocated && + !callback_context->iter->ptr) { + callback_context->iter++; + } + + if (callback_context->iter == callback_context->end) + return FALSE; + + // Include the specified memory region. + callback_output->MemoryBase = callback_context->iter->ptr; + callback_output->MemorySize = callback_context->iter->length; + callback_context->iter++; + return TRUE; + } + + // Include all modules. + case IncludeModuleCallback: + case ModuleCallback: + return TRUE; + + // Include all threads. + case IncludeThreadCallback: + case ThreadCallback: + return TRUE; + + // Stop receiving cancel callbacks. + case CancelCallback: + callback_output->CheckCancel = FALSE; + callback_output->Cancel = FALSE; + return TRUE; + } + // Ignore other callback types. + return FALSE; +} + +} // namespace google_breakpad + |