1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/chrome_elf/crash/crash_helper.h"
#include <windows.h>
#include <assert.h>
#include <algorithm>
#include <string>
#include <vector>
#include "chrome/app/chrome_crash_reporter_client_win.h"
#include "components/crash/core/app/crashpad.h"
#include "components/crash/core/common/crash_keys.h"
#include "sandbox/policy/win/hook_util/hook_util.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
namespace {
// Crash handling from elf is only enabled for the chrome.exe process.
// Use this global to safely handle the rare case where elf may not be in that
// process (e.g. tests).
bool g_crash_helper_enabled = false;
// Global pointer to a vector of crash reports.
// This structure will be initialized in InitializeCrashReportingForProcess()
// and cleaned up in DllDetachCrashReportingCleanup().
std::vector<crash_reporter::Report>* g_crash_reports = nullptr;
// chrome_elf loads early in the process and initializes Crashpad. That in turn
// uses the SetUnhandledExceptionFilter API to set a top level exception
// handler for the process. When the process eventually initializes, CRT sets
// an exception handler which calls TerminateProcess which effectively bypasses
// us. Ideally we want to be at the top of the unhandled exception filter
// chain. However we don't have a good way of intercepting the
// SetUnhandledExceptionFilter API in the sandbox. EAT patching kernel32 or
// kernelbase should ideally work. However the kernel32 kernelbase dlls are
// prebound which causes EAT patching to not work. Sidestep works. However it
// is only supported for 32 bit. For now we use IAT patching for the
// executable.
// TODO(ananta).
// Check if it is possible to fix EAT patching or use sidestep patching for
// 32 bit and 64 bit for this purpose.
sandbox::policy::IATHook* g_set_unhandled_exception_filter = nullptr;
// Hook function, which ignores the request to set an unhandled-exception
// filter.
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
SetUnhandledExceptionFilterPatch(LPTOP_LEVEL_EXCEPTION_FILTER filter) {
// Don't set the exception filter. Please see above for comments.
return nullptr;
}
} // namespace
//------------------------------------------------------------------------------
// Public chrome_elf crash APIs
//------------------------------------------------------------------------------
namespace elf_crash {
// NOTE: This function will be called from DllMain during DLL_PROCESS_ATTACH
// (while we have the loader lock), so do not misbehave.
bool InitializeCrashReporting() {
if (g_crash_helper_enabled)
return true;
#if defined(_DEBUG) || defined(DCHECK_ALWAYS_ON)
assert(g_crash_reports == nullptr);
assert(g_set_unhandled_exception_filter == nullptr);
#endif // defined(_DEBUG) || defined(DCHECK_ALWAYS_ON)
// No global objects with destructors, so using global pointers.
// DllMain on detach will clean these up.
g_crash_reports = new std::vector<crash_reporter::Report>;
g_set_unhandled_exception_filter = new sandbox::policy::IATHook();
ChromeCrashReporterClient::InitializeCrashReportingForProcess();
g_crash_helper_enabled = true;
return true;
}
// NOTE: This function will be called from DllMain during DLL_PROCESS_DETACH
// (while we have the loader lock), so do not misbehave.
void ShutdownCrashReporting() {
if (g_crash_reports) {
g_crash_reports->clear();
delete g_crash_reports;
}
if (g_set_unhandled_exception_filter) {
delete g_set_unhandled_exception_filter;
}
}
// Please refer to the comment on g_set_unhandled_exception_filter for more
// information about why we intercept the SetUnhandledExceptionFilter API.
void DisableSetUnhandledExceptionFilter() {
#if defined(_DEBUG) || defined(DCHECK_ALWAYS_ON)
// Should never patch SetUnhandledExceptionFilter before crashpad has called
// it.
assert(g_crash_helper_enabled);
#endif // defined(_DEBUG) || defined(DCHECK_ALWAYS_ON)
if (g_set_unhandled_exception_filter->Hook(
::GetModuleHandle(nullptr), "kernel32.dll",
"SetUnhandledExceptionFilter",
reinterpret_cast<void*>(SetUnhandledExceptionFilterPatch)) !=
NO_ERROR) {
#if defined(_DEBUG) || defined(DCHECK_ALWAYS_ON)
assert(false);
#endif // defined(_DEBUG) || defined(DCHECK_ALWAYS_ON)
}
}
int GenerateCrashDump(EXCEPTION_POINTERS* exception_pointers) {
if (g_crash_helper_enabled)
crashpad::CrashpadClient::DumpWithoutCrash(
*(exception_pointers->ContextRecord));
return EXCEPTION_CONTINUE_SEARCH;
}
void DumpWithoutCrashing() {
crash_reporter::DumpWithoutCrashing();
}
void SetMetricsClientIdImpl(const char* client_id) {
if (!g_crash_helper_enabled)
return;
if (client_id)
crash_keys::SetMetricsClientIdFromGUID(client_id);
}
} // namespace elf_crash
|