--- a/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc
+++ b/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc
@@ -1,40 +1,71 @@
/* -*- 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;
+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;
+};
+}
+
bool GetAppMemoryFromRegister(HANDLE aProcess,
+ const NT_TIB* aTib,
RegisterValueType aRegister,
- AppMemoryList::iterator aResult)
+ AppMemory* aResult)
{
static_assert(sizeof(RegisterValueType) == sizeof(void*),
"Size mismatch between DWORD/DWORD64 and void*");
if (!sPageSize) {
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
sPageSize = systemInfo.dwPageSize;
}
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.
@@ -74,24 +105,72 @@ FindNextPreallocated(AppMemoryList& aLis
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;
+ }
+
+ using FuncPtr = decltype(::NtQueryInformationThread);
+ FuncPtr* pNtQueryInformationThread =
+ (FuncPtr*)(::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"),
+ "NtQueryInformationThread"));
+ if (!pNtQueryInformationThread) {
+ return false;
+ }
+
+ THREAD_BASIC_INFORMATION threadInfo;
+ auto status = (*pNtQueryInformationThread)(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;
@@ -116,26 +195,57 @@ void IncludeAppMemoryFromExceptionContex
heapAddrCandidates[numElements++] = aExceptionContext->R12;
heapAddrCandidates[numElements++] = aExceptionContext->R13;
heapAddrCandidates[numElements++] = aExceptionContext->R14;
heapAddrCandidates[numElements++] = aExceptionContext->R15;
}
heapAddrCandidates[numElements++] = aExceptionContext->Rip;
#endif
- auto appMemory = aList.begin();
+ // 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++) {
- appMemory = FindNextPreallocated(aList, appMemory);
if (appMemory == aList.end()) {
break;
}
- if (GetAppMemoryFromRegister(aProcess, heapAddrCandidates[i], appMemory)) {
- appMemory++;
+ AppMemory tmp{};
+ if (!GetAppMemoryFromRegister(aProcess,
+ aInstructionPointerOnly ? nullptr : &tib,
+ heapAddrCandidates[i],
+ &tmp)) {
+ continue;
+ }
+
+ assert(tmp.ptr && tmp.length);
+
+ 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) {