Merge master e7630628e9c9 into doc

This commit is contained in:
Mark Mentovai 2017-01-24 10:59:59 -05:00
commit 9833c53e29
35 changed files with 993 additions and 157 deletions

1
.gitignore vendored
View File

@ -12,6 +12,7 @@
/out /out
/third_party/gtest/gtest /third_party/gtest/gtest
/third_party/gyp/gyp /third_party/gyp/gyp
/third_party/llvm
/third_party/mini_chromium/mini_chromium /third_party/mini_chromium/mini_chromium
/xcodebuild /xcodebuild
tags tags

46
DEPS
View File

@ -17,18 +17,28 @@ vars = {
} }
deps = { deps = {
'buildtools':
Var('chromium_git') + '/chromium/buildtools.git@' +
'f8fc76ea5ce4a60cda2fa5d7df3d4a62935b3113',
'crashpad/third_party/gtest/gtest': 'crashpad/third_party/gtest/gtest':
Var('chromium_git') + '/external/github.com/google/googletest@' + Var('chromium_git') + '/external/github.com/google/googletest@' +
'ec44c6c1675c25b9827aacd08c02433cccde7780', 'ec44c6c1675c25b9827aacd08c02433cccde7780',
'crashpad/third_party/gyp/gyp': 'crashpad/third_party/gyp/gyp':
Var('chromium_git') + '/external/gyp@' + Var('chromium_git') + '/external/gyp@' +
'93cc6e2c23e4d5ebd179f388e67aa907d0dfd43d', '93cc6e2c23e4d5ebd179f388e67aa907d0dfd43d',
# TODO(scottmg): Consider pinning these. For now, we don't have any particular
# reason to do so.
'crashpad/third_party/llvm':
Var('chromium_git') + '/external/llvm.org/llvm.git@HEAD',
'crashpad/third_party/llvm/tools/clang':
Var('chromium_git') + '/external/llvm.org/clang.git@HEAD',
'crashpad/third_party/llvm/tools/lldb':
Var('chromium_git') + '/external/llvm.org/lldb.git@HEAD',
'crashpad/third_party/mini_chromium/mini_chromium': 'crashpad/third_party/mini_chromium/mini_chromium':
Var('chromium_git') + '/chromium/mini_chromium@' + Var('chromium_git') + '/chromium/mini_chromium@' +
'414d59602ac38e24f1e93929fda3d79d72cea139', 'de1afb04f4afc074ec6d23bd9ee7b1e6b365427f',
'buildtools':
Var('chromium_git') + '/chromium/buildtools.git@' +
'f8fc76ea5ce4a60cda2fa5d7df3d4a62935b3113',
} }
hooks = [ hooks = [
@ -41,7 +51,6 @@ hooks = [
'--no_resume', '--no_resume',
'--no_auth', '--no_auth',
'--bucket=chromium-clang-format', '--bucket=chromium-clang-format',
'--output=buildtools/mac/clang-format',
'--sha1_file', '--sha1_file',
'buildtools/mac/clang-format.sha1', 'buildtools/mac/clang-format.sha1',
], ],
@ -55,11 +64,36 @@ hooks = [
'--no_resume', '--no_resume',
'--no_auth', '--no_auth',
'--bucket=chromium-clang-format', '--bucket=chromium-clang-format',
'--output=buildtools/win/clang-format.exe',
'--sha1_file', '--sha1_file',
'buildtools/win/clang-format.exe.sha1', 'buildtools/win/clang-format.exe.sha1',
], ],
}, },
{
'name': 'gn_mac',
'pattern': '.',
'action': [
'download_from_google_storage',
'--platform=^darwin$',
'--no_resume',
'--no_auth',
'--bucket=chromium-gn',
'--sha1_file',
'buildtools/mac/gn.sha1',
],
},
{
'name': 'gn_win',
'pattern': '.',
'action': [
'download_from_google_storage',
'--platform=^win32$',
'--no_resume',
'--no_auth',
'--bucket=chromium-gn',
'--sha1_file',
'buildtools/win/gn.exe.sha1',
],
},
{ {
'name': 'gyp', 'name': 'gyp',
'pattern': '\.gypi?$', 'pattern': '\.gypi?$',

View File

@ -21,6 +21,7 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "util/misc/address_sanitizer.h"
#include "util/misc/implicit_cast.h" #include "util/misc/implicit_cast.h"
namespace crashpad { namespace crashpad {
@ -103,13 +104,14 @@ void TestCaptureContext() {
// captured program counter should be slightly greater than or equal to the // captured program counter should be slightly greater than or equal to the
// reference program counter. // reference program counter.
uintptr_t pc = ProgramCounterFromContext(context_1); uintptr_t pc = ProgramCounterFromContext(context_1);
#if !__has_feature(address_sanitizer)
#if !defined(ADDRESS_SANITIZER)
// AddressSanitizer can cause enough code bloat that the “nearby” check would // AddressSanitizer can cause enough code bloat that the “nearby” check would
// likely fail. // likely fail.
const uintptr_t kReferencePC = const uintptr_t kReferencePC =
reinterpret_cast<uintptr_t>(TestCaptureContext); reinterpret_cast<uintptr_t>(TestCaptureContext);
EXPECT_LT(pc - kReferencePC, 64u); EXPECT_LT(pc - kReferencePC, 64u);
#endif #endif // !defined(ADDRESS_SANITIZER)
// Declare sp and context_2 here because all local variables need to be // Declare sp and context_2 here because all local variables need to be
// declared before computing the stack pointer reference value, so that the // declared before computing the stack pointer reference value, so that the

View File

@ -173,9 +173,12 @@ class CrashpadClient {
//! //!
//! This method should not be used unless `asynchronous_start` was `true`. //! This method should not be used unless `asynchronous_start` was `true`.
//! //!
//! \param[in] timeout_ms The number of milliseconds to wait for a result from
//! the background launch, or `0xffffffff` to block indefinitely.
//!
//! \return `true` if the hander startup succeeded, `false` otherwise, and an //! \return `true` if the hander startup succeeded, `false` otherwise, and an
//! error message will have been logged. //! error message will have been logged.
bool WaitForHandlerStart(); bool WaitForHandlerStart(unsigned int timeout_ms);
//! \brief Requests that the handler capture a dump even though there hasn't //! \brief Requests that the handler capture a dump even though there hasn't
//! been a crash. //! been a crash.

View File

@ -15,6 +15,7 @@
#include "client/crashpad_client.h" #include "client/crashpad_client.h"
#include <windows.h> #include <windows.h>
#include <signal.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
@ -31,6 +32,7 @@
#include "util/file/file_io.h" #include "util/file/file_io.h"
#include "util/misc/random_string.h" #include "util/misc/random_string.h"
#include "util/win/address_types.h" #include "util/win/address_types.h"
#include "util/win/capture_context.h"
#include "util/win/command_line.h" #include "util/win/command_line.h"
#include "util/win/critical_section_with_debug_info.h" #include "util/win/critical_section_with_debug_info.h"
#include "util/win/get_function.h" #include "util/win/get_function.h"
@ -107,7 +109,18 @@ StartupState BlockUntilHandlerStartedOrFailed() {
return static_cast<StartupState>(startup_state); return static_cast<StartupState>(startup_state);
} }
#if defined(ADDRESS_SANITIZER)
extern "C" LONG __asan_unhandled_exception_filter(EXCEPTION_POINTERS* info);
#endif
LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
#if defined(ADDRESS_SANITIZER)
// In ASan builds, delegate to the ASan exception filter.
LONG status = __asan_unhandled_exception_filter(exception_pointers);
if (status != EXCEPTION_CONTINUE_SEARCH)
return status;
#endif
if (BlockUntilHandlerStartedOrFailed() == StartupState::kFailed) { if (BlockUntilHandlerStartedOrFailed() == StartupState::kFailed) {
// If we know for certain that the handler has failed to start, then abort // If we know for certain that the handler has failed to start, then abort
// here, rather than trying to signal to a handler that will never arrive, // here, rather than trying to signal to a handler that will never arrive,
@ -163,6 +176,28 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
void HandleAbortSignal(int signum) {
DCHECK_EQ(signum, SIGABRT);
CONTEXT context;
CaptureContext(&context);
EXCEPTION_RECORD record = {};
record.ExceptionCode = STATUS_FATAL_APP_EXIT;
record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
#if defined(ARCH_CPU_64_BITS)
record.ExceptionAddress = reinterpret_cast<void*>(context.Rip);
#else
record.ExceptionAddress = reinterpret_cast<void*>(context.Eip);
#endif // ARCH_CPU_64_BITS
EXCEPTION_POINTERS exception_pointers;
exception_pointers.ContextRecord = &context;
exception_pointers.ExceptionRecord = &record;
UnhandledExceptionHandler(&exception_pointers);
}
std::wstring FormatArgumentString(const std::string& name, std::wstring FormatArgumentString(const std::string& name,
const std::wstring& value) { const std::wstring& value) {
return std::wstring(L"--") + base::UTF8ToUTF16(name) + L"=" + value; return std::wstring(L"--") + base::UTF8ToUTF16(name) + L"=" + value;
@ -500,6 +535,32 @@ void CommonInProcessInitialization() {
g_non_crash_dump_lock = new base::Lock(); g_non_crash_dump_lock = new base::Lock();
} }
void RegisterHandlers() {
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
// The Windows CRT's signal.h lists:
// - SIGINT
// - SIGILL
// - SIGFPE
// - SIGSEGV
// - SIGTERM
// - SIGBREAK
// - SIGABRT
// SIGILL and SIGTERM are documented as not being generated. SIGBREAK and
// SIGINT are for Ctrl-Break and Ctrl-C, and aren't something for which
// capturing a dump is warranted. SIGFPE and SIGSEGV are captured as regular
// exceptions through the unhandled exception filter. This leaves SIGABRT. In
// the standard CRT, abort() is implemented as a synchronous call to the
// SIGABRT signal handler if installed, but after doing so, the unhandled
// exception filter is not triggered (it instead __fastfail()s). So, register
// to handle SIGABRT to catch abort() calls, as client code might use this and
// expect it to cause a crash dump. This will only work when the abort()
// that's called in client code is the same (or has the same behavior) as the
// one in use here.
void (*rv)(int) = signal(SIGABRT, HandleAbortSignal);
DCHECK_NE(rv, SIG_ERR);
}
} // namespace } // namespace
CrashpadClient::CrashpadClient() : ipc_pipe_(), handler_start_thread_() {} CrashpadClient::CrashpadClient() : ipc_pipe_(), handler_start_thread_() {}
@ -536,7 +597,7 @@ bool CrashpadClient::StartHandler(
CommonInProcessInitialization(); CommonInProcessInitialization();
SetUnhandledExceptionFilter(&UnhandledExceptionHandler); RegisterHandlers();
auto data = new BackgroundHandlerStartThreadData(handler, auto data = new BackgroundHandlerStartThreadData(handler,
database, database,
@ -609,7 +670,8 @@ bool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) {
} }
SetHandlerStartupState(StartupState::kSucceeded); SetHandlerStartupState(StartupState::kSucceeded);
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
RegisterHandlers();
// The server returns these already duplicated to be valid in this process. // The server returns these already duplicated to be valid in this process.
g_signal_exception = g_signal_exception =
@ -627,10 +689,16 @@ std::wstring CrashpadClient::GetHandlerIPCPipe() const {
return ipc_pipe_; return ipc_pipe_;
} }
bool CrashpadClient::WaitForHandlerStart() { bool CrashpadClient::WaitForHandlerStart(unsigned int timeout_ms) {
DCHECK(handler_start_thread_.is_valid()); DCHECK(handler_start_thread_.is_valid());
if (WaitForSingleObject(handler_start_thread_.get(), INFINITE) != DWORD result = WaitForSingleObject(handler_start_thread_.get(), timeout_ms);
WAIT_OBJECT_0) { if (result == WAIT_TIMEOUT) {
LOG(ERROR) << "WaitForSingleObject timed out";
return false;
} else if (result == WAIT_ABANDONED) {
LOG(ERROR) << "WaitForSingleObject abandoned";
return false;
} else if (result != WAIT_OBJECT_0) {
PLOG(ERROR) << "WaitForSingleObject"; PLOG(ERROR) << "WaitForSingleObject";
return false; return false;
} }

View File

@ -39,7 +39,7 @@ void StartAndUseHandler() {
std::vector<std::string>(), std::vector<std::string>(),
true, true,
true)); true));
ASSERT_TRUE(client.WaitForHandlerStart()); ASSERT_TRUE(client.WaitForHandlerStart(INFINITE));
} }
class StartWithInvalidHandles final : public WinMultiprocess { class StartWithInvalidHandles final : public WinMultiprocess {

View File

@ -14,6 +14,7 @@
#include "client/crashpad_info.h" #include "client/crashpad_info.h"
#include "util/misc/address_sanitizer.h"
#include "util/stdlib/cxx.h" #include "util/stdlib/cxx.h"
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
@ -72,14 +73,14 @@ __attribute__((
#error Port #error Port
#endif // !defined(OS_MACOSX) && !defined(OS_LINUX) && !defined(OS_ANDROID) #endif // !defined(OS_MACOSX) && !defined(OS_LINUX) && !defined(OS_ANDROID)
#if __has_feature(address_sanitizer) #if defined(ADDRESS_SANITIZER)
// AddressSanitizer would add a trailing red zone of at least 32 bytes, // AddressSanitizer would add a trailing red zone of at least 32 bytes,
// which would be reflected in the size of the custom section. This confuses // which would be reflected in the size of the custom section. This confuses
// MachOImageReader::GetCrashpadInfo(), which finds that the sections size // MachOImageReader::GetCrashpadInfo(), which finds that the sections size
// disagrees with the structures size_ field. By specifying an alignment // disagrees with the structures size_ field. By specifying an alignment
// greater than the red zone size, the red zone will be suppressed. // greater than the red zone size, the red zone will be suppressed.
aligned(64), aligned(64),
#endif // __has_feature(address_sanitizer) #endif // defined(ADDRESS_SANITIZER)
// The “used” attribute prevents the structure from being dead-stripped. // The “used” attribute prevents the structure from being dead-stripped.
used, used,

View File

@ -55,7 +55,7 @@ struct Settings::Data {
uint32_t version; uint32_t version;
uint32_t options; uint32_t options;
uint32_t padding_0; uint32_t padding_0;
uint64_t last_upload_attempt_time; // time_t int64_t last_upload_attempt_time; // time_t
UUID client_id; UUID client_id;
}; };
@ -136,7 +136,7 @@ bool Settings::SetLastUploadAttemptTime(time_t time) {
if (!handle.is_valid()) if (!handle.is_valid())
return false; return false;
settings.last_upload_attempt_time = InRangeCast<uint64_t>(time, 0); settings.last_upload_attempt_time = InRangeCast<int64_t>(time, 0);
return WriteSettings(handle.get(), settings); return WriteSettings(handle.get(), settings);
} }

View File

@ -147,6 +147,20 @@
'win/crashy_test_program.cc', 'win/crashy_test_program.cc',
], ],
}, },
{
'target_name': 'crashy_signal',
'type': 'executable',
'dependencies': [
'../client/client.gyp:crashpad_client',
'../third_party/mini_chromium/mini_chromium.gyp:base',
],
'include_dirs': [
'..',
],
'sources': [
'win/crashy_signal.cc',
],
},
{ {
'target_name': 'crash_other_program', 'target_name': 'crash_other_program',
'type': 'executable', 'type': 'executable',

View File

@ -129,9 +129,15 @@ void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) {
#endif // OS_MACOSX #endif // OS_MACOSX
#if defined(OS_WIN) #if defined(OS_WIN)
LONG(WINAPI* g_original_exception_filter)(EXCEPTION_POINTERS*) = nullptr;
LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
Metrics::HandlerCrashed(exception_pointers->ExceptionRecord->ExceptionCode); Metrics::HandlerCrashed(exception_pointers->ExceptionRecord->ExceptionCode);
return EXCEPTION_CONTINUE_SEARCH;
if (g_original_exception_filter)
return g_original_exception_filter(exception_pointers);
else
return EXCEPTION_CONTINUE_SEARCH;
} }
#endif // OS_WIN #endif // OS_WIN
@ -139,7 +145,8 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
int HandlerMain(int argc, char* argv[]) { int HandlerMain(int argc, char* argv[]) {
#if defined(OS_WIN) #if defined(OS_WIN)
SetUnhandledExceptionFilter(&UnhandledExceptionHandler); g_original_exception_filter =
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
#endif #endif
const base::FilePath argv0( const base::FilePath argv0(

View File

@ -66,7 +66,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
const mach_msg_trailer_t* trailer, const mach_msg_trailer_t* trailer,
bool* destroy_complex_request) { bool* destroy_complex_request) {
Metrics::ExceptionEncountered(); Metrics::ExceptionEncountered();
Metrics::ExceptionCode(exception); Metrics::ExceptionCode(ExceptionCodeForMetrics(exception, code[0]));
*destroy_complex_request = true; *destroy_complex_request = true;
// The expected behavior is EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, // The expected behavior is EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,

View File

@ -0,0 +1,90 @@
// Copyright 2016 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "base/logging.h"
#include "client/crashpad_client.h"
namespace crashpad {
namespace {
enum WhereToSignalFrom {
kUnknown = -1,
kMain = 0,
kBackground = 1,
};
WhereToSignalFrom MainOrBackground(wchar_t* name) {
if (wcscmp(name, L"main") == 0)
return kMain;
if (wcscmp(name, L"background") == 0)
return kBackground;
return kUnknown;
}
DWORD WINAPI BackgroundThread(void* arg) {
abort();
return 0;
}
int CrashySignalMain(int argc, wchar_t* argv[]) {
CrashpadClient client;
WhereToSignalFrom from;
if (argc == 3 && (from = MainOrBackground(argv[2])) != kUnknown) {
if (!client.SetHandlerIPCPipe(argv[1])) {
LOG(ERROR) << "SetHandler";
return EXIT_FAILURE;
}
} else {
fprintf(stderr, "Usage: %ls <server_pipe_name> main|background\n", argv[0]);
return EXIT_FAILURE;
}
// In debug builds part of abort() is to open a dialog. We don't want tests to
// block at that dialog, so disable it.
_set_abort_behavior(0, _WRITE_ABORT_MSG);
if (from == kBackground) {
HANDLE thread = CreateThread(nullptr,
0,
&BackgroundThread,
nullptr,
0,
nullptr);
if (!thread) {
PLOG(ERROR) << "CreateThread";
return EXIT_FAILURE;
}
if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) {
PLOG(ERROR) << "WaitForSingleObject";
return EXIT_FAILURE;
}
} else {
abort();
}
return EXIT_SUCCESS;
}
} // namespace
} // namespace crashpad
int wmain(int argc, wchar_t* argv[]) {
return crashpad::CrashySignalMain(argc, argv);
}

View File

@ -201,8 +201,8 @@ int CrashyMain(int argc, wchar_t* argv[]) {
AllocateExtraUnsavedMemory(extra_ranges); AllocateExtraUnsavedMemory(extra_ranges);
// Load and unload some uncommonly used modules so we can see them in the list // Load and unload some uncommonly used modules so we can see them in the list
// reported by `lm`. At least two so that we confirm we got the size of // reported by `lm`. At least two so that we confirm we got the element size
// RTL_UNLOAD_EVENT_TRACE right. // advancement of RTL_UNLOAD_EVENT_TRACE correct.
CHECK(GetModuleHandle(L"lz32.dll") == nullptr); CHECK(GetModuleHandle(L"lz32.dll") == nullptr);
CHECK(GetModuleHandle(L"wmerror.dll") == nullptr); CHECK(GetModuleHandle(L"wmerror.dll") == nullptr);
HMODULE lz32 = LoadLibrary(L"lz32.dll"); HMODULE lz32 = LoadLibrary(L"lz32.dll");

View File

@ -66,20 +66,7 @@ bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader,
exception_ = ExcCrashRecoverOriginalException( exception_ = ExcCrashRecoverOriginalException(
exception_code_0, &exception_code_0, nullptr); exception_code_0, &exception_code_0, nullptr);
if (exception_ == EXC_CRASH || if (!ExcCrashCouldContainException(exception_)) {
exception_ == EXC_RESOURCE ||
exception_ == EXC_GUARD) {
// EXC_CRASH should never be wrapped in another EXC_CRASH.
//
// EXC_RESOURCE and EXC_GUARD are software exceptions that are never
// wrapped in EXC_CRASH. The only time EXC_CRASH is generated is for
// processes exiting due to an unhandled core-generating signal or being
// killed by SIGKILL for code-signing reasons. Neither of these applies to
// EXC_RESOURCE or EXC_GUARD. See 10.10 xnu-2782.1.97/bsd/kern/kern_exit.c
// proc_prepareexit(). Receiving these exception types wrapped in
// EXC_CRASH would lose information because their code[0] uses all 64 bits
// (see below) and the code[0] recovered from EXC_CRASH only contains 20
// significant bits.
LOG(WARNING) << base::StringPrintf( LOG(WARNING) << base::StringPrintf(
"exception %s invalid in EXC_CRASH", "exception %s invalid in EXC_CRASH",
ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric) ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)

View File

@ -143,6 +143,10 @@ def GetDumpFromOtherProgram(out_dir, pipe_name, *args):
*args) *args)
def GetDumpFromSignal(out_dir, pipe_name, *args):
return GetDumpFromProgram(out_dir, pipe_name, 'crashy_signal.exe', *args)
def GetDumpFromSelfDestroyingProgram(out_dir, pipe_name): def GetDumpFromSelfDestroyingProgram(out_dir, pipe_name):
return GetDumpFromProgram(out_dir, pipe_name, 'self_destroying_program.exe') return GetDumpFromProgram(out_dir, pipe_name, 'self_destroying_program.exe')
@ -201,6 +205,8 @@ def RunTests(cdb_path,
z7_dump_path, z7_dump_path,
other_program_path, other_program_path,
other_program_no_exception_path, other_program_no_exception_path,
sigabrt_main_path,
sigabrt_background_path,
pipe_name): pipe_name):
"""Runs various tests in sequence. Runs a new cdb instance on the dump for """Runs various tests in sequence. Runs a new cdb instance on the dump for
each block of tests to reduce the chances that output from one command is each block of tests to reduce the chances that output from one command is
@ -361,6 +367,13 @@ def RunTests(cdb_path,
'other program with no exception given') 'other program with no exception given')
out.Check('!RaiseException', 'other program in RaiseException()') out.Check('!RaiseException', 'other program in RaiseException()')
out = CdbRun(cdb_path, sigabrt_main_path, '.ecxr')
out.Check('code 40000015', 'got sigabrt signal')
out.Check('::HandleAbortSignal', ' stack in expected location')
out = CdbRun(cdb_path, sigabrt_background_path, '.ecxr')
out.Check('code 40000015', 'got sigabrt signal from background thread')
def main(args): def main(args):
try: try:
@ -411,6 +424,15 @@ def main(args):
if not other_program_no_exception_path: if not other_program_no_exception_path:
return 1 return 1
sigabrt_main_path = GetDumpFromSignal(args[0], pipe_name, 'main')
if not sigabrt_main_path:
return 1
sigabrt_background_path = GetDumpFromSignal(
args[0], pipe_name, 'background')
if not sigabrt_background_path:
return 1
RunTests(cdb_path, RunTests(cdb_path,
crashy_dump_path, crashy_dump_path,
start_handler_dump_path, start_handler_dump_path,
@ -418,6 +440,8 @@ def main(args):
z7_dump_path, z7_dump_path,
other_program_path, other_program_path,
other_program_no_exception_path, other_program_no_exception_path,
sigabrt_main_path,
sigabrt_background_path,
pipe_name) pipe_name)
return 1 if g_had_failures else 0 return 1 if g_had_failures else 0

View File

@ -281,21 +281,43 @@ void ProcessSnapshotWin::InitializeUnloadedModules() {
#error port #error port
#endif #endif
RTL_UNLOAD_EVENT_TRACE<Traits>* unload_event_trace_address = ULONG* element_size;
RtlGetUnloadEventTrace<Traits>(); ULONG* element_count;
WinVMAddress address_in_target_process = void* event_trace_address;
reinterpret_cast<WinVMAddress>(unload_event_trace_address); RtlGetUnloadEventTraceEx(&element_size, &element_count, &event_trace_address);
std::vector<RTL_UNLOAD_EVENT_TRACE<Traits>> events( if (*element_size < sizeof(RTL_UNLOAD_EVENT_TRACE<Traits>)) {
RTL_UNLOAD_EVENT_TRACE_NUMBER); LOG(ERROR) << "unexpected unloaded module list element size";
if (!process_reader_.ReadMemory(address_in_target_process,
events.size() * sizeof(events[0]),
&events[0])) {
return; return;
} }
for (const RTL_UNLOAD_EVENT_TRACE<Traits>& uet : events) { const WinVMAddress address_in_target_process =
if (uet.ImageName[0]) { reinterpret_cast<WinVMAddress>(event_trace_address);
Traits::Pointer pointer_to_array;
if (!process_reader_.ReadMemory(address_in_target_process,
sizeof(pointer_to_array),
&pointer_to_array)) {
LOG(ERROR) << "failed to read target address";
return;
}
// No unloaded modules.
if (pointer_to_array == 0)
return;
const size_t data_size = *element_size * *element_count;
std::vector<uint8_t> data(data_size);
if (!process_reader_.ReadMemory(pointer_to_array, data_size, &data[0])) {
LOG(ERROR) << "failed to read unloaded module data";
return;
}
for (ULONG i = 0; i < *element_count; ++i) {
const uint8_t* base_address = &data[i * *element_size];
const auto& uet =
*reinterpret_cast<const RTL_UNLOAD_EVENT_TRACE<Traits>*>(base_address);
if (uet.ImageName[0] != 0) {
unloaded_modules_.push_back(UnloadedModuleSnapshot( unloaded_modules_.push_back(UnloadedModuleSnapshot(
uet.BaseAddress, uet.BaseAddress,
uet.SizeOfImage, uet.SizeOfImage,

View File

@ -38,6 +38,7 @@
#include "util/file/file_io.h" #include "util/file/file_io.h"
#include "util/file/file_reader.h" #include "util/file/file_reader.h"
#include "util/misc/uuid.h" #include "util/misc/uuid.h"
#include "util/stdlib/string_number_conversion.h"
namespace crashpad { namespace crashpad {
namespace { namespace {
@ -130,16 +131,23 @@ std::string BoolToString(bool boolean) {
return std::string(boolean ? "true" : "false"); return std::string(boolean ? "true" : "false");
} }
// Converts |string| to |time|, returning true if a conversion could be // Converts |string| to |out_time|, returning true if a conversion could be
// performed, and false without setting |boolean| if no conversion could be // performed, and false without setting |boolean| if no conversion could be
// performed. Various time formats are recognized, including several string // performed. Various time formats are recognized, including several string
// representations and a numeric time_t representation. The special string // representations and a numeric time_t representation. The special |string|
// "never" is recognized as |string| and converts to a |time| value of 0. |utc|, // "never" is recognized as converted to a |out_time| value of 0; "now" is
// when true, causes |string| to be interpreted as a UTC time rather than a // converted to the current time. |utc|, when true, causes |string| to be
// local time when the time zone is ambiguous. // interpreted as a UTC time rather than a local time when the time zone is
bool StringToTime(const char* string, time_t* time, bool utc) { // ambiguous.
bool StringToTime(const char* string, time_t* out_time, bool utc) {
if (strcasecmp(string, "never") == 0) { if (strcasecmp(string, "never") == 0) {
*time = 0; *out_time = 0;
return true;
}
if (strcasecmp(string, "now") == 0) {
errno = 0;
PCHECK(time(out_time) != -1 || errno == 0);
return true; return true;
} }
@ -155,41 +163,53 @@ bool StringToTime(const char* string, time_t* time, bool utc) {
tm time_tm; tm time_tm;
const char* strptime_result = strptime(string, kFormats[index], &time_tm); const char* strptime_result = strptime(string, kFormats[index], &time_tm);
if (strptime_result == end) { if (strptime_result == end) {
time_t test_out_time;
if (utc) { if (utc) {
*time = timegm(&time_tm); test_out_time = timegm(&time_tm);
} else { } else {
*time = mktime(&time_tm); test_out_time = mktime(&time_tm);
} }
return true; // mktime() is supposed to set errno in the event of an error, but support
// for this is spotty, so theres no way to distinguish between a true
// time_t of -1 (1969-12-31 23:59:59 UTC) and an error. Assume error.
//
// See 10.11.5 Libc-1082.50.1/stdtime/FreeBSD/localtime.c and
// glibc-2.24/time/mktime.c, which dont set errno or save and restore
// errno. Post-Android 7.1.0 Bionic is even more hopeless, setting errno
// whenever the time conversion returns -1, even for valid input. See
// libc/tzcode/localtime.c mktime(). Windows seems to get it right: see
// 10.0.14393 SDK Source/ucrt/time/mktime.cpp.
if (test_out_time != -1) {
*out_time = test_out_time;
return true;
}
} }
} }
char* end_result; int64_t int64_result;
errno = 0; if (StringToNumber(string, &int64_result) &&
long long strtoll_result = strtoll(string, &end_result, 0); base::IsValueInRangeForNumericType<time_t>(int64_result)) {
if (end_result == end && errno == 0 && *out_time = int64_result;
base::IsValueInRangeForNumericType<time_t>(strtoll_result)) {
*time = strtoll_result;
return true; return true;
} }
return false; return false;
} }
// Converts |time_tt| to a string, and returns it. |utc| determines whether the // Converts |out_time| to a string, and returns it. |utc| determines whether the
// converted time will reference local time or UTC. If |time_tt| is 0, the // converted time will reference local time or UTC. If |out_time| is 0, the
// string "never" will be returned as a special case. // string "never" will be returned as a special case.
std::string TimeToString(time_t time_tt, bool utc) { std::string TimeToString(time_t out_time, bool utc) {
if (time_tt == 0) { if (out_time == 0) {
return std::string("never"); return std::string("never");
} }
tm time_tm; tm time_tm;
if (utc) { if (utc) {
gmtime_r(&time_tt, &time_tm); PCHECK(gmtime_r(&out_time, &time_tm));
} else { } else {
localtime_r(&time_tt, &time_tm); PCHECK(localtime_r(&out_time, &time_tm));
} }
char string[64]; char string[64];

View File

@ -118,7 +118,8 @@ database.
Set the last-upload-attempt time in the databases settings. _TIME_ is a Set the last-upload-attempt time in the databases settings. _TIME_ is a
string representation of a time, which may be in _yyyy-mm-dd hh:mm:ss_ string representation of a time, which may be in _yyyy-mm-dd hh:mm:ss_
format, a numeric `time_t` value, or the special string `"never"`. format, a numeric `time_t` value, or the special strings `"never"` or
`"now"`.
See also **--show-last-upload-attempt-time**. See also **--show-last-upload-attempt-time**.

View File

@ -157,17 +157,16 @@ FileOffset StringFile::Seek(FileOffset offset, int whence) {
LOG(ERROR) << "Seek(): new_offset invalid"; LOG(ERROR) << "Seek(): new_offset invalid";
return -1; return -1;
} }
FileOffset new_offset_fileoffset = new_offset.ValueOrDie();
size_t new_offset_sizet; size_t new_offset_sizet;
if (!AssignIfInRange(&new_offset_sizet, new_offset_fileoffset)) { if (!new_offset.AssignIfValid(&new_offset_sizet)) {
LOG(ERROR) << "Seek(): new_offset " << new_offset_fileoffset LOG(ERROR) << "Seek(): new_offset " << new_offset.ValueOrDie()
<< " invalid for size_t"; << " invalid for size_t";
return -1; return -1;
} }
offset_ = new_offset_sizet; offset_ = new_offset_sizet;
return offset_.ValueOrDie(); return base::ValueOrDieForType<FileOffset>(offset_);
} }
} // namespace crashpad } // namespace crashpad

View File

@ -24,6 +24,8 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/mach_logging.h" #include "base/mac/mach_logging.h"
#include "util/mac/mac_util.h" #include "util/mac/mac_util.h"
#include "util/mach/mach_extensions.h"
#include "util/numeric/in_range_cast.h"
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
@ -128,6 +130,114 @@ exception_type_t ExcCrashRecoverOriginalException(
return (code_0 >> 20) & 0xf; return (code_0 >> 20) & 0xf;
} }
bool ExcCrashCouldContainException(exception_type_t exception) {
// EXC_CRASH should never be wrapped in another EXC_CRASH.
//
// EXC_RESOURCE and EXC_GUARD are software exceptions that are never wrapped
// in EXC_CRASH. The only time EXC_CRASH is generated is for processes exiting
// due to an unhandled core-generating signal or being killed by SIGKILL for
// code-signing reasons. Neither of these apply to EXC_RESOURCE or EXC_GUARD.
// See 10.10 xnu-2782.1.97/bsd/kern/kern_exit.c proc_prepareexit(). Receiving
// these exception types wrapped in EXC_CRASH would lose information because
// their code[0] uses all 64 bits (see ExceptionSnapshotMac::Initialize()) and
// the code[0] recovered from EXC_CRASH only contains 20 significant bits.
//
// EXC_CORPSE_NOTIFY may be generated from EXC_CRASH, but the opposite should
// never occur.
//
// kMachExceptionSimulated is a non-fatal Crashpad-specific pseudo-exception
// that never exists as an exception within the kernel and should thus never
// be wrapped in EXC_CRASH.
return exception != EXC_CRASH &&
exception != EXC_RESOURCE &&
exception != EXC_GUARD &&
exception != EXC_CORPSE_NOTIFY &&
exception != kMachExceptionSimulated;
}
int32_t ExceptionCodeForMetrics(exception_type_t exception,
mach_exception_code_t code_0) {
if (exception == kMachExceptionSimulated) {
return exception;
}
int signal = 0;
if (exception == EXC_CRASH) {
const exception_type_t original_exception =
ExcCrashRecoverOriginalException(code_0, &code_0, &signal);
if (!ExcCrashCouldContainException(original_exception)) {
LOG(WARNING) << "EXC_CRASH should not contain exception "
<< original_exception;
return InRangeCast<uint16_t>(original_exception, 0xffff) << 16;
}
exception = original_exception;
}
uint16_t metrics_exception = InRangeCast<uint16_t>(exception, 0xffff);
uint16_t metrics_code_0;
switch (exception) {
case EXC_RESOURCE:
metrics_code_0 = (EXC_RESOURCE_DECODE_RESOURCE_TYPE(code_0) << 8) |
EXC_RESOURCE_DECODE_FLAVOR(code_0);
break;
case EXC_GUARD: {
// This will be GUARD_TYPE_MACH_PORT (1) from <mach/port.h> or
// GUARD_TYPE_FD (2) from 10.12.2 xnu-3789.31.2/bsd/sys/guarded.h
const uint8_t guard_type = (code_0) >> 61;
// These exceptions come through 10.12.2
// xnu-3789.31.2/osfmk/ipc/mach_port.c mach_port_guard_exception() or
// xnu-3789.31.2/bsd/kern/kern_guarded.c fp_guard_exception(). In each
// case, bits 32-60 of code_0 encode the guard type-specific “flavor”. For
// Mach port guards, these flavor codes come from the
// mach_port_guard_exception_codes enum in <mach/port.h>. For file
// descriptor guards, they come from the guard_exception_codes enum in
// xnu-3789.31.2/bsd/sys/guarded.h. Both of these enums define shifted-bit
// values (1 << 0, 1 << 1, 1 << 2, etc.) In actual usage as determined by
// callers to these functions, these “flavor” codes are never ORed with
// one another. For the purposes of encoding these codes for metrics,
// convert the flavor codes to their corresponding bit shift values.
const uint32_t guard_flavor = (code_0 >> 32) & 0x1fffffff;
uint8_t metrics_guard_flavor = 0xff;
for (int bit = 0; bit < 29; ++bit) {
const uint32_t test_mask = 1 << bit;
if (guard_flavor & test_mask) {
// Make sure that no other bits are set.
DCHECK_EQ(guard_flavor, test_mask);
metrics_guard_flavor = bit;
break;
}
}
metrics_code_0 = (guard_type << 8) | metrics_guard_flavor;
break;
}
case EXC_CORPSE_NOTIFY:
// code_0 may be a pointer. See 10.12.2 xnu-3789.31.2/osfmk/kern/task.c
// task_deliver_crash_notification(). Just encode 0 for metrics purposes.
metrics_code_0 = 0;
break;
default:
metrics_code_0 = InRangeCast<uint16_t>(code_0, 0xffff);
if (exception == 0 && metrics_code_0 == 0 && signal != 0) {
// This exception came from a signal that did not originate as another
// Mach exception. Encode the signal number, using EXC_CRASH as the
// top-level exception type. This is safe because EXC_CRASH will not
// otherwise appear as metrics_exception.
metrics_exception = EXC_CRASH;
metrics_code_0 = signal;
}
break;
}
return (metrics_exception << 16) | metrics_code_0;
}
bool IsExceptionNonfatalResource(exception_type_t exception, bool IsExceptionNonfatalResource(exception_type_t exception,
mach_exception_code_t code_0, mach_exception_code_t code_0,
pid_t pid) { pid_t pid) {

View File

@ -15,6 +15,7 @@
#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_ #ifndef CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_
#define CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_ #define CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_
#include <inttypes.h>
#include <mach/mach.h> #include <mach/mach.h>
#include <sys/types.h> #include <sys/types.h>
@ -54,6 +55,55 @@ exception_type_t ExcCrashRecoverOriginalException(
mach_exception_code_t* original_code_0, mach_exception_code_t* original_code_0,
int* signal); int* signal);
//! \brief Determines whether a given exception type could plausibly be carried
//! within an `EXC_CRASH` exception.
//!
//! \param[in] exception The exception type to test.
//!
//! \return `true` if an `EXC_CRASH` exception could plausibly carry \a
//! exception.
//!
//! An `EXC_CRASH` exception can wrap exceptions that originate as hardware
//! faults, as well as exceptions that originate from certain software sources
//! such as POSIX signals. It cannot wrap another `EXC_CRASH` exception, nor can
//! it wrap `EXC_RESOURCE`, `EXC_GUARD`, or `EXC_CORPSE_NOTIFY` exceptions. It
//! also cannot wrap Crashpad-specific #kMachExceptionSimulated exceptions.
bool ExcCrashCouldContainException(exception_type_t exception);
//! \brief Returns the exception code to report via a configured metrics system.
//!
//! \param[in] exception The exception type as received by a Mach exception
//! handler.
//! \param[in] code_0 The first exception code (`code[0]`) as received by a
//! Mach exception handler.
//!
//! \return An exception code that maps useful information from \a exception and
//! \a code_0 to the more limited data type available for metrics reporting.
//!
//! For classic Mach exceptions (including hardware faults reported as Mach
//! exceptions), the mapping is `(exception << 16) | code_0`.
//!
//! For `EXC_CRASH` exceptions that originate as Mach exceptions described
//! above, the mapping above is used, with the original exceptions values. For
//! `EXC_CRASH` exceptions that originate as POSIX signals without an underlying
//! Mach exception, the mapping is `(EXC_CRASH << 16) | code_0`.
//!
//! `EXC_RESOURCE` and `EXC_GUARD` exceptions both contain exception-specific
//! “type” values and type-specific “flavor” values. In these cases, the mapping
//! is `(exception << 16) | (type << 8) | flavor`. For `EXC_GUARD`, the “flavor”
//! value is rewritten to be more space-efficient by replacing the
//! kernel-supplied bitmask having exactly one bit set with the index of the set
//! bit.
//!
//! `EXC_CORPSE_NOTIFY` exceptions are reported as classic Mach exceptions with
//! the \a code_0 field set to `0`.
//!
//! If \a exception is #kMachExceptionSimulated, that value is returned as-is.
//!
//! Overflow conditions in any field are handled via saturation.
int32_t ExceptionCodeForMetrics(exception_type_t exception,
mach_exception_code_t code_0);
//! \brief Determines whether an exception is a non-fatal `EXC_RESOURCE`. //! \brief Determines whether an exception is a non-fatal `EXC_RESOURCE`.
//! //!
//! \param[in] exception The exception type as received by a Mach exception //! \param[in] exception The exception type as received by a Mach exception

View File

@ -41,9 +41,21 @@ TEST(ExceptionTypes, ExcCrashRecoverOriginalException) {
{0xb100001, EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, SIGSEGV}, {0xb100001, EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, SIGSEGV},
{0xb100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGSEGV}, {0xb100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGSEGV},
{0xa100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGBUS}, {0xa100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGBUS},
{0x4200001, EXC_BAD_INSTRUCTION, 1, SIGILL}, {0xa100005, EXC_BAD_ACCESS, VM_PROT_READ | VM_PROT_EXECUTE, SIGBUS},
{0x8300001, EXC_ARITHMETIC, 1, SIGFPE}, {0xa10000d, EXC_BAD_ACCESS, EXC_I386_GPFLT, SIGBUS},
{0x5600002, EXC_BREAKPOINT, 2, SIGTRAP}, {0x9100032, EXC_BAD_ACCESS, KERN_CODESIGN_ERROR, SIGKILL},
{0x4200001, EXC_BAD_INSTRUCTION, EXC_I386_INVOP, SIGILL},
{0x420000b, EXC_BAD_INSTRUCTION, EXC_I386_SEGNPFLT, SIGILL},
{0x420000c, EXC_BAD_INSTRUCTION, EXC_I386_STKFLT, SIGILL},
{0x8300001, EXC_ARITHMETIC, EXC_I386_DIV, SIGFPE},
{0x8300002, EXC_ARITHMETIC, EXC_I386_INTO, SIGFPE},
{0x8300005, EXC_ARITHMETIC, EXC_I386_EXTERR, SIGFPE},
{0x8300008, EXC_ARITHMETIC, EXC_I386_SSEEXTERR, SIGFPE},
{0x5500007, EXC_SOFTWARE, EXC_I386_BOUND, SIGTRAP},
{0x5600001, EXC_BREAKPOINT, EXC_I386_SGL, SIGTRAP},
{0x5600002, EXC_BREAKPOINT, EXC_I386_BPT, SIGTRAP},
{0x0700080, EXC_SYSCALL, 128, 0},
{0x0706000, EXC_SYSCALL, 0x6000, 0},
{0x3000000, 0, 0, SIGQUIT}, {0x3000000, 0, 0, SIGQUIT},
{0x6000000, 0, 0, SIGABRT}, {0x6000000, 0, 0, SIGABRT},
{0xc000000, 0, 0, SIGSYS}, {0xc000000, 0, 0, SIGSYS},
@ -86,40 +98,173 @@ TEST(ExceptionTypes, ExcCrashRecoverOriginalException) {
EXPECT_EQ(test_data.signal, signal); EXPECT_EQ(test_data.signal, signal);
} }
// These macros come from the #ifdef KERNEL section of <kern/exc_resource.h>: TEST(ExceptionTypes, ExcCrashCouldContainException) {
// 10.10 xnu-2782.1.97/osfmk/kern/exc_resource.h. // This seems wrong, but it happens when EXC_CRASH carries an exception not
#define EXC_RESOURCE_ENCODE_TYPE(code, type) \ // originally caused by a hardware fault, such as SIGABRT.
((code) |= ((static_cast<uint64_t>(type) & 0x7ull) << 61)) EXPECT_TRUE(ExcCrashCouldContainException(0));
#define EXC_RESOURCE_ENCODE_FLAVOR(code, flavor) \
((code) |= ((static_cast<uint64_t>(flavor) & 0x7ull) << 58)) EXPECT_TRUE(ExcCrashCouldContainException(EXC_BAD_ACCESS));
EXPECT_TRUE(ExcCrashCouldContainException(EXC_BAD_INSTRUCTION));
EXPECT_TRUE(ExcCrashCouldContainException(EXC_ARITHMETIC));
EXPECT_TRUE(ExcCrashCouldContainException(EXC_EMULATION));
EXPECT_TRUE(ExcCrashCouldContainException(EXC_SOFTWARE));
EXPECT_TRUE(ExcCrashCouldContainException(EXC_BREAKPOINT));
EXPECT_TRUE(ExcCrashCouldContainException(EXC_SYSCALL));
EXPECT_TRUE(ExcCrashCouldContainException(EXC_MACH_SYSCALL));
EXPECT_TRUE(ExcCrashCouldContainException(EXC_RPC_ALERT));
EXPECT_FALSE(ExcCrashCouldContainException(EXC_CRASH));
EXPECT_FALSE(ExcCrashCouldContainException(EXC_RESOURCE));
EXPECT_FALSE(ExcCrashCouldContainException(EXC_GUARD));
EXPECT_FALSE(ExcCrashCouldContainException(EXC_CORPSE_NOTIFY));
EXPECT_FALSE(ExcCrashCouldContainException(kMachExceptionSimulated));
}
// This macro is adapted from those in the #ifdef KERNEL section of
// <kern/exc_resource.h>: 10.12.2 xnu-3789.31.2/osfmk/kern/exc_resource.h.
#define EXC_RESOURCE_ENCODE_TYPE_FLAVOR(type, flavor) \
(static_cast<mach_exception_code_t>( \
(((static_cast<uint64_t>(type) & 0x7ull) << 61) | \
(static_cast<uint64_t>(flavor) & 0x7ull) << 58)))
TEST(ExceptionTypes, ExceptionCodeForMetrics) {
struct TestData {
exception_type_t exception;
mach_exception_code_t code_0;
int32_t metrics_code;
};
const TestData kTestData[] = {
#define ENCODE_EXC(type, code_0) \
{ (type), (code_0), ((type) << 16) | (code_0) }
ENCODE_EXC(EXC_BAD_ACCESS, KERN_INVALID_ADDRESS),
ENCODE_EXC(EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE),
ENCODE_EXC(EXC_BAD_ACCESS, VM_PROT_READ | VM_PROT_EXECUTE),
ENCODE_EXC(EXC_BAD_ACCESS, EXC_I386_GPFLT),
ENCODE_EXC(EXC_BAD_ACCESS, KERN_CODESIGN_ERROR),
ENCODE_EXC(EXC_BAD_INSTRUCTION, EXC_I386_INVOP),
ENCODE_EXC(EXC_BAD_INSTRUCTION, EXC_I386_SEGNPFLT),
ENCODE_EXC(EXC_BAD_INSTRUCTION, EXC_I386_STKFLT),
ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_DIV),
ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_INTO),
ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_EXTERR),
ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_SSEEXTERR),
ENCODE_EXC(EXC_SOFTWARE, EXC_I386_BOUND),
ENCODE_EXC(EXC_BREAKPOINT, EXC_I386_SGL),
ENCODE_EXC(EXC_BREAKPOINT, EXC_I386_BPT),
ENCODE_EXC(EXC_SYSCALL, 128),
ENCODE_EXC(EXC_SYSCALL, 0x6000),
#undef ENCODE_EXC
#define ENCODE_EXC_CRASH(type, code_0) \
{ \
EXC_CRASH, (((type) & 0xf) << 20) | ((code_0) & 0xfffff), \
((type) << 16) | (code_0) \
}
ENCODE_EXC_CRASH(EXC_BAD_ACCESS, KERN_INVALID_ADDRESS),
ENCODE_EXC_CRASH(EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE),
ENCODE_EXC_CRASH(EXC_BAD_ACCESS, VM_PROT_READ | VM_PROT_EXECUTE),
ENCODE_EXC_CRASH(EXC_BAD_ACCESS, EXC_I386_GPFLT),
ENCODE_EXC_CRASH(EXC_BAD_ACCESS, KERN_CODESIGN_ERROR),
ENCODE_EXC_CRASH(EXC_BAD_INSTRUCTION, EXC_I386_INVOP),
ENCODE_EXC_CRASH(EXC_BAD_INSTRUCTION, EXC_I386_SEGNPFLT),
ENCODE_EXC_CRASH(EXC_BAD_INSTRUCTION, EXC_I386_STKFLT),
ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_DIV),
ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_INTO),
ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_EXTERR),
ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_SSEEXTERR),
ENCODE_EXC_CRASH(EXC_SOFTWARE, EXC_I386_BOUND),
ENCODE_EXC_CRASH(EXC_BREAKPOINT, EXC_I386_SGL),
ENCODE_EXC_CRASH(EXC_BREAKPOINT, EXC_I386_BPT),
ENCODE_EXC_CRASH(EXC_SYSCALL, 128),
ENCODE_EXC_CRASH(EXC_SYSCALL, 0x6000),
#undef ENCODE_EXC_CRASH
#define ENCODE_EXC_CRASH_SIGNAL(signal) \
{ EXC_CRASH, (((signal) & 0xff) << 24), (EXC_CRASH << 16) | (signal) }
ENCODE_EXC_CRASH_SIGNAL(SIGQUIT),
ENCODE_EXC_CRASH_SIGNAL(SIGABRT),
ENCODE_EXC_CRASH_SIGNAL(SIGSYS),
#undef ENCODE_EXC_CRASH_SIGNAL
#define ENCODE_EXC_RESOURCE(type, flavor) \
{ \
EXC_RESOURCE, EXC_RESOURCE_ENCODE_TYPE_FLAVOR((type), (flavor)), \
(EXC_RESOURCE << 16) | ((type) << 8) | (flavor) \
}
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR),
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR_FATAL),
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_WAKEUPS, FLAVOR_WAKEUPS_MONITOR),
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_MEMORY, FLAVOR_HIGH_WATERMARK),
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_IO, FLAVOR_IO_PHYSICAL_WRITES),
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_IO, FLAVOR_IO_LOGICAL_WRITES),
#undef ENCODE_EXC_RESOURCE
#define ENCODE_EXC_GUARD(type, flavor) \
{ \
EXC_GUARD, \
static_cast<mach_exception_code_t>(static_cast<uint64_t>((type) & 0x7) \
<< 61) | \
(static_cast<uint64_t>((1 << (flavor)) & 0x1ffffffff) << 32), \
(EXC_GUARD << 16) | ((type) << 8) | (flavor) \
}
ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 0), // kGUARD_EXC_DESTROY
ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 1), // kGUARD_EXC_MOD_REFS
ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 2), // kGUARD_EXC_SET_CONTEXT
ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 3), // kGUARD_EXC_UNGUARDED
ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 4), // kGUARD_EXC_INCORRECT_GUARD
// 2 is GUARD_TYPE_FD from 10.12.2 xnu-3789.31.2/bsd/sys/guarded.h.
ENCODE_EXC_GUARD(2, 0), // kGUARD_EXC_CLOSE
ENCODE_EXC_GUARD(2, 1), // kGUARD_EXC_DUP
ENCODE_EXC_GUARD(2, 2), // kGUARD_EXC_NOCLOEXEC
ENCODE_EXC_GUARD(2, 3), // kGUARD_EXC_SOCKET_IPC
ENCODE_EXC_GUARD(2, 4), // kGUARD_EXC_FILEPORT
ENCODE_EXC_GUARD(2, 5), // kGUARD_EXC_MISMATCH
ENCODE_EXC_GUARD(2, 6), // kGUARD_EXC_WRITE
#undef ENCODE_EXC_GUARD
// Test that overflow saturates.
{0x00010000, 0x00001000, static_cast<int32_t>(0xffff1000)},
{0x00001000, 0x00010000, 0x1000ffff},
{0x00010000, 0x00010000, static_cast<int32_t>(0xffffffff)},
};
for (size_t index = 0; index < arraysize(kTestData); ++index) {
const TestData& test_data = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %zu, exception 0x%x, code_0 0x%llx",
index,
test_data.exception,
test_data.code_0));
int32_t metrics_code =
ExceptionCodeForMetrics(test_data.exception, test_data.code_0);
EXPECT_EQ(test_data.metrics_code, metrics_code);
}
}
TEST(ExceptionTypes, IsExceptionNonfatalResource) { TEST(ExceptionTypes, IsExceptionNonfatalResource) {
const pid_t pid = getpid(); const pid_t pid = getpid();
mach_exception_code_t code = 0; mach_exception_code_t code =
EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_CPU); EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR);
EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_CPU_MONITOR);
EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid)); EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
if (MacOSXMinorVersion() >= 10) { if (MacOSXMinorVersion() >= 10) {
// FLAVOR_CPU_MONITOR_FATAL was introduced in OS X 10.10. // FLAVOR_CPU_MONITOR_FATAL was introduced in OS X 10.10.
code = 0; code = EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_CPU,
EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_CPU); FLAVOR_CPU_MONITOR_FATAL);
EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_CPU_MONITOR_FATAL);
EXPECT_FALSE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid)); EXPECT_FALSE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
} }
// This assumes that WAKEMON_MAKE_FATAL is not set for this process. The // This assumes that WAKEMON_MAKE_FATAL is not set for this process. The
// default is for WAKEMON_MAKE_FATAL to not be set, theres no public API to // default is for WAKEMON_MAKE_FATAL to not be set, theres no public API to
// enable it, and nothing in this process should have enabled it. // enable it, and nothing in this process should have enabled it.
code = 0; code = EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_WAKEUPS,
EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_WAKEUPS); FLAVOR_WAKEUPS_MONITOR);
EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_WAKEUPS_MONITOR);
EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid)); EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
code = 0; code = EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_MEMORY,
EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_MEMORY); FLAVOR_HIGH_WATERMARK);
EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_HIGH_WATERMARK);
EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid)); EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
// Non-EXC_RESOURCE exceptions should never be considered nonfatal resource // Non-EXC_RESOURCE exceptions should never be considered nonfatal resource

View File

@ -0,0 +1,28 @@
// Copyright 2016 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.
#ifndef CRASHPAD_UTIL_MISC_ADDRESS_SANITIZER_H_
#define CRASHPAD_UTIL_MISC_ADDRESS_SANITIZER_H_
#include "base/compiler_specific.h"
#include "build/build_config.h"
#if !defined(ADDRESS_SANITIZER)
#if HAS_FEATURE(address_sanitizer) || \
(defined(COMPILER_GCC) && defined(__SANITIZE_ADDRESS__))
#define ADDRESS_SANITIZER 1
#endif
#endif // !defined(ADDRESS_SANITIZER)
#endif // CRASHPAD_UTIL_MISC_ADDRESS_SANITIZER_H_

View File

@ -14,6 +14,7 @@
#include "util/misc/arraysize_unsafe.h" #include "util/misc/arraysize_unsafe.h"
#include "base/compiler_specific.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
namespace crashpad { namespace crashpad {
@ -21,35 +22,37 @@ namespace test {
namespace { namespace {
TEST(ArraySizeUnsafe, ArraySizeUnsafe) { TEST(ArraySizeUnsafe, ArraySizeUnsafe) {
char c0[0];
static_assert(ARRAYSIZE_UNSAFE(c0) == 0, "c0");
char c1[1]; char c1[1];
static_assert(ARRAYSIZE_UNSAFE(c1) == 1, "c1"); static_assert(ARRAYSIZE_UNSAFE(c1) == 1, "c1");
ALLOW_UNUSED_LOCAL(c1);
char c2[2]; char c2[2];
static_assert(ARRAYSIZE_UNSAFE(c2) == 2, "c2"); static_assert(ARRAYSIZE_UNSAFE(c2) == 2, "c2");
ALLOW_UNUSED_LOCAL(c2);
char c4[4]; char c4[4];
static_assert(ARRAYSIZE_UNSAFE(c4) == 4, "c4"); static_assert(ARRAYSIZE_UNSAFE(c4) == 4, "c4");
ALLOW_UNUSED_LOCAL(c4);
int i0[0];
static_assert(ARRAYSIZE_UNSAFE(i0) == 0, "i0");
int i1[1]; int i1[1];
static_assert(ARRAYSIZE_UNSAFE(i1) == 1, "i1"); static_assert(ARRAYSIZE_UNSAFE(i1) == 1, "i1");
ALLOW_UNUSED_LOCAL(i1);
int i2[2]; int i2[2];
static_assert(ARRAYSIZE_UNSAFE(i2) == 2, "i2"); static_assert(ARRAYSIZE_UNSAFE(i2) == 2, "i2");
ALLOW_UNUSED_LOCAL(i2);
int i4[4]; int i4[4];
static_assert(ARRAYSIZE_UNSAFE(i4) == 4, "i4"); static_assert(ARRAYSIZE_UNSAFE(i4) == 4, "i4");
ALLOW_UNUSED_LOCAL(i4);
long l8[8]; long l8[8];
static_assert(ARRAYSIZE_UNSAFE(l8) == 8, "l8"); static_assert(ARRAYSIZE_UNSAFE(l8) == 8, "l8");
ALLOW_UNUSED_LOCAL(l8);
int l9[9]; int l9[9];
static_assert(ARRAYSIZE_UNSAFE(l9) == 9, "l9"); static_assert(ARRAYSIZE_UNSAFE(l9) == 9, "l9");
ALLOW_UNUSED_LOCAL(l9);
struct S { struct S {
char c; char c;
@ -58,14 +61,13 @@ TEST(ArraySizeUnsafe, ArraySizeUnsafe) {
bool b; bool b;
}; };
S s0[0];
static_assert(ARRAYSIZE_UNSAFE(s0) == 0, "s0");
S s1[1]; S s1[1];
static_assert(ARRAYSIZE_UNSAFE(s1) == 1, "s1"); static_assert(ARRAYSIZE_UNSAFE(s1) == 1, "s1");
ALLOW_UNUSED_LOCAL(s1);
S s10[10]; S s10[10];
static_assert(ARRAYSIZE_UNSAFE(s10) == 10, "s10"); static_assert(ARRAYSIZE_UNSAFE(s10) == 10, "s10");
ALLOW_UNUSED_LOCAL(s10);
} }
} // namespace } // namespace

View File

@ -95,17 +95,11 @@ bool UUID::InitializeWithNew() {
uuid_generate(uuid); uuid_generate(uuid);
InitializeFromBytes(uuid); InitializeFromBytes(uuid);
return true; return true;
#elif defined(OS_WIN) #elif defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID)
::UUID system_uuid;
if (UuidCreate(&system_uuid) != RPC_S_OK) {
LOG(ERROR) << "UuidCreate";
return false;
}
InitializeFromSystemUUID(&system_uuid);
return true;
#elif defined(OS_LINUX) || defined(OS_ANDROID)
// Linux does not provide a UUID generator in a widely-available system // Linux does not provide a UUID generator in a widely-available system
// library. uuid_generate() from libuuid is not available everywhere. // library. uuid_generate() from libuuid is not available everywhere.
// On Windows, do not use UuidCreate() to avoid a dependency on rpcrt4, so
// that this function is usable early in DllMain().
base::RandBytes(this, sizeof(*this)); base::RandBytes(this, sizeof(*this));
// Set six bits per RFC 4122 §4.4 to identify this as a pseudo-random UUID. // Set six bits per RFC 4122 §4.4 to identify this as a pseudo-random UUID.

View File

@ -110,6 +110,13 @@ struct StringToUnsignedIntTraits
} }
}; };
struct StringToInt64Traits
: public StringToSignedIntegerTraits<int64_t, int64_t> {
static LongType Convert(const char* str, char** end, int base) {
return strtoll(str, end, base);
}
};
struct StringToUnsignedInt64Traits struct StringToUnsignedInt64Traits
: public StringToUnsignedIntegerTraits<uint64_t, uint64_t> { : public StringToUnsignedIntegerTraits<uint64_t, uint64_t> {
static LongType Convert(const char* str, char** end, int base) { static LongType Convert(const char* str, char** end, int base) {
@ -166,6 +173,10 @@ bool StringToNumber(const base::StringPiece& string, unsigned int* number) {
return StringToIntegerInternal<StringToUnsignedIntTraits>(string, number); return StringToIntegerInternal<StringToUnsignedIntTraits>(string, number);
} }
bool StringToNumber(const base::StringPiece& string, int64_t* number) {
return StringToIntegerInternal<StringToInt64Traits>(string, number);
}
bool StringToNumber(const base::StringPiece& string, uint64_t* number) { bool StringToNumber(const base::StringPiece& string, uint64_t* number) {
return StringToIntegerInternal<StringToUnsignedInt64Traits>(string, number); return StringToIntegerInternal<StringToUnsignedInt64Traits>(string, number);
} }

View File

@ -56,6 +56,7 @@ namespace crashpad {
//! where such prefix recognition is desirable. //! where such prefix recognition is desirable.
bool StringToNumber(const base::StringPiece& string, int* number); bool StringToNumber(const base::StringPiece& string, int* number);
bool StringToNumber(const base::StringPiece& string, unsigned int* number); bool StringToNumber(const base::StringPiece& string, unsigned int* number);
bool StringToNumber(const base::StringPiece& string, int64_t* number);
bool StringToNumber(const base::StringPiece& string, uint64_t* number); bool StringToNumber(const base::StringPiece& string, uint64_t* number);
//! \} //! \}

View File

@ -221,6 +221,105 @@ TEST(StringNumberConversion, StringToUnsignedInt) {
EXPECT_EQ(6u, output); EXPECT_EQ(6u, output);
} }
TEST(StringNumberConversion, StringToInt64) {
const struct {
const char* string;
bool valid;
int64_t value;
} kTestData[] = {
{"", false, 0},
{"0", true, 0},
{"1", true, 1},
{"2147483647", true, 2147483647},
{"2147483648", true, 2147483648},
{"4294967295", true, 4294967295},
{"4294967296", true, 4294967296},
{"9223372036854775807", true, std::numeric_limits<int64_t>::max()},
{"9223372036854775808", false, 0},
{"18446744073709551615", false, 0},
{"18446744073709551616", false, 0},
{"-1", true, -1},
{"-2147483648", true, INT64_C(-2147483648)},
{"-2147483649", true, INT64_C(-2147483649)},
{"-9223372036854775808", true, std::numeric_limits<int64_t>::min()},
{"-9223372036854775809", false, 0},
{"0x7fffffffffffffff", true, std::numeric_limits<int64_t>::max()},
{"0x8000000000000000", false, 0},
{"0xffffffffffffffff", false, 0},
{"0x10000000000000000", false, 0},
{"-0x7fffffffffffffff", true, -9223372036854775807},
{"-0x8000000000000000", true, std::numeric_limits<int64_t>::min()},
{"-0x8000000000000001", false, 0},
{"0x7Fffffffffffffff", true, std::numeric_limits<int64_t>::max()},
};
for (size_t index = 0; index < arraysize(kTestData); ++index) {
int64_t value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
EXPECT_TRUE(valid) << "index " << index << ", string "
<< kTestData[index].string;
if (valid) {
EXPECT_EQ(kTestData[index].value, value)
<< "index " << index << ", string " << kTestData[index].string;
}
} else {
EXPECT_FALSE(valid) << "index " << index << ", string "
<< kTestData[index].string << ", value " << value;
}
}
}
TEST(StringNumberConversion, StringToUnsignedInt64) {
const struct {
const char* string;
bool valid;
uint64_t value;
} kTestData[] = {
{"", false, 0},
{"0", true, 0},
{"1", true, 1},
{"2147483647", true, 2147483647},
{"2147483648", true, 2147483648},
{"4294967295", true, 4294967295},
{"4294967296", true, 4294967296},
{"9223372036854775807", true, 9223372036854775807},
{"9223372036854775808", true, 9223372036854775808u},
{"18446744073709551615", true, std::numeric_limits<uint64_t>::max()},
{"18446744073709551616", false, 0},
{"-1", false, 0},
{"-2147483648", false, 0},
{"-2147483649", false, 0},
{"-2147483648", false, 0},
{"-9223372036854775808", false, 0},
{"-9223372036854775809", false, 0},
{"0x7fffffffffffffff", true, 9223372036854775807},
{"0x8000000000000000", true, 9223372036854775808u},
{"0xffffffffffffffff", true, std::numeric_limits<uint64_t>::max()},
{"0x10000000000000000", false, 0},
{"-0x7fffffffffffffff", false, 0},
{"-0x8000000000000000", false, 0},
{"-0x8000000000000001", false, 0},
{"0xFfffffffffffffff", true, std::numeric_limits<uint64_t>::max()},
};
for (size_t index = 0; index < arraysize(kTestData); ++index) {
uint64_t value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
EXPECT_TRUE(valid) << "index " << index << ", string "
<< kTestData[index].string;
if (valid) {
EXPECT_EQ(kTestData[index].value, value)
<< "index " << index << ", string " << kTestData[index].string;
}
} else {
EXPECT_FALSE(valid) << "index " << index << ", string "
<< kTestData[index].string << ", value " << value;
}
}
}
} // namespace } // namespace
} // namespace test } // namespace test
} // namespace crashpad } // namespace crashpad

View File

@ -84,6 +84,7 @@
'mach/task_for_pid.h', 'mach/task_for_pid.h',
'mach/task_memory.cc', 'mach/task_memory.cc',
'mach/task_memory.h', 'mach/task_memory.h',
'misc/address_sanitizer.h',
'misc/arraysize_unsafe.h', 'misc/arraysize_unsafe.h',
'misc/clock.h', 'misc/clock.h',
'misc/clock_mac.cc', 'misc/clock_mac.cc',
@ -262,8 +263,6 @@
['OS=="win"', { ['OS=="win"', {
'link_settings': { 'link_settings': {
'libraries': [ 'libraries': [
'-ladvapi32.lib',
'-lrpcrt4.lib',
'-lwinhttp.lib', '-lwinhttp.lib',
], ],
}, },

View File

@ -91,6 +91,7 @@
'win/handle_test.cc', 'win/handle_test.cc',
'win/initial_client_data_test.cc', 'win/initial_client_data_test.cc',
'win/process_info_test.cc', 'win/process_info_test.cc',
'win/registration_protocol_win_test.cc',
'win/scoped_process_suspend_test.cc', 'win/scoped_process_suspend_test.cc',
'win/time_test.cc', 'win/time_test.cc',
], ],
@ -108,6 +109,7 @@
], ],
'link_settings': { 'link_settings': {
'libraries': [ 'libraries': [
'-ladvapi32.lib',
'-limagehlp.lib', '-limagehlp.lib',
'-lrpcrt4.lib', '-lrpcrt4.lib',
], ],

View File

@ -42,7 +42,9 @@ NTSTATUS NTAPI NtSuspendProcess(HANDLE);
NTSTATUS NTAPI NtResumeProcess(HANDLE); NTSTATUS NTAPI NtResumeProcess(HANDLE);
void* NTAPI RtlGetUnloadEventTrace(); VOID NTAPI RtlGetUnloadEventTraceEx(PULONG* ElementSize,
PULONG* ElementCount,
PVOID* EventTrace);
namespace crashpad { namespace crashpad {
@ -145,12 +147,12 @@ NTSTATUS NtResumeProcess(HANDLE handle) {
return nt_resume_process(handle); return nt_resume_process(handle);
} }
template <class Traits> void RtlGetUnloadEventTraceEx(ULONG** element_size,
RTL_UNLOAD_EVENT_TRACE<Traits>* RtlGetUnloadEventTrace() { ULONG** element_count,
static const auto rtl_get_unload_event_trace = void** event_trace) {
GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlGetUnloadEventTrace); static const auto rtl_get_unload_event_trace_ex =
return reinterpret_cast<RTL_UNLOAD_EVENT_TRACE<Traits>*>( GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlGetUnloadEventTraceEx);
rtl_get_unload_event_trace()); rtl_get_unload_event_trace_ex(element_size, element_count, event_trace);
} }
// Explicit instantiations with the only 2 valid template arguments to avoid // Explicit instantiations with the only 2 valid template arguments to avoid
@ -169,10 +171,4 @@ template NTSTATUS NtOpenThread<process_types::internal::Traits64>(
const process_types::CLIENT_ID<process_types::internal::Traits64>* const process_types::CLIENT_ID<process_types::internal::Traits64>*
client_id); client_id);
template RTL_UNLOAD_EVENT_TRACE<process_types::internal::Traits32>*
RtlGetUnloadEventTrace<process_types::internal::Traits32>();
template RTL_UNLOAD_EVENT_TRACE<process_types::internal::Traits64>*
RtlGetUnloadEventTrace<process_types::internal::Traits64>();
} // namespace crashpad } // namespace crashpad

View File

@ -75,10 +75,7 @@ NTSTATUS NtSuspendProcess(HANDLE handle);
NTSTATUS NtResumeProcess(HANDLE handle); NTSTATUS NtResumeProcess(HANDLE handle);
// From https://msdn.microsoft.com/en-us/library/bb432428(VS.85).aspx and // From https://msdn.microsoft.com/en-us/library/cc678403(v=vs.85).aspx.
// http://processhacker.sourceforge.net/doc/struct___r_t_l___u_n_l_o_a_d___e_v_e_n_t___t_r_a_c_e.html
#define RTL_UNLOAD_EVENT_TRACE_NUMBER 64
template <class Traits> template <class Traits>
struct RTL_UNLOAD_EVENT_TRACE { struct RTL_UNLOAD_EVENT_TRACE {
typename Traits::Pointer BaseAddress; typename Traits::Pointer BaseAddress;
@ -87,14 +84,10 @@ struct RTL_UNLOAD_EVENT_TRACE {
ULONG TimeDateStamp; ULONG TimeDateStamp;
ULONG CheckSum; ULONG CheckSum;
WCHAR ImageName[32]; WCHAR ImageName[32];
ULONG Version0;
union {
ULONG Version1;
typename Traits::Pad alignment_for_x64;
};
}; };
template <class Traits> void RtlGetUnloadEventTraceEx(ULONG** element_size,
RTL_UNLOAD_EVENT_TRACE<Traits>* RtlGetUnloadEventTrace(); ULONG** element_count,
void** event_trace);
} // namespace crashpad } // namespace crashpad

View File

@ -15,12 +15,11 @@
#include "util/win/registration_protocol_win.h" #include "util/win/registration_protocol_win.h"
#include <windows.h> #include <windows.h>
#include <sddl.h>
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h"
#include "util/win/exception_handler_server.h" #include "util/win/exception_handler_server.h"
#include "util/win/scoped_handle.h" #include "util/win/scoped_handle.h"
#include "util/win/scoped_local_alloc.h"
namespace crashpad { namespace crashpad {
@ -97,7 +96,6 @@ HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name,
bool first_instance) { bool first_instance) {
SECURITY_ATTRIBUTES security_attributes; SECURITY_ATTRIBUTES security_attributes;
SECURITY_ATTRIBUTES* security_attributes_pointer = nullptr; SECURITY_ATTRIBUTES* security_attributes_pointer = nullptr;
ScopedLocalAlloc scoped_sec_desc;
if (first_instance) { if (first_instance) {
// Pre-Vista does not have integrity levels. // Pre-Vista does not have integrity levels.
@ -105,21 +103,10 @@ HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name,
const DWORD major_version = LOBYTE(LOWORD(version)); const DWORD major_version = LOBYTE(LOWORD(version));
const bool is_vista_or_later = major_version >= 6; const bool is_vista_or_later = major_version >= 6;
if (is_vista_or_later) { if (is_vista_or_later) {
// Mandatory Label, no ACE flags, no ObjectType, integrity level
// untrusted.
const wchar_t kSddl[] = L"S:(ML;;;;;S-1-16-0)";
PSECURITY_DESCRIPTOR sec_desc;
PCHECK(ConvertStringSecurityDescriptorToSecurityDescriptor(
kSddl, SDDL_REVISION_1, &sec_desc, nullptr))
<< "ConvertStringSecurityDescriptorToSecurityDescriptor";
// Take ownership of the allocated SECURITY_DESCRIPTOR.
scoped_sec_desc.reset(sec_desc);
memset(&security_attributes, 0, sizeof(security_attributes)); memset(&security_attributes, 0, sizeof(security_attributes));
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
security_attributes.lpSecurityDescriptor = sec_desc; security_attributes.lpSecurityDescriptor =
const_cast<void*>(GetSecurityDescriptorForNamedPipeInstance(nullptr));
security_attributes.bInheritHandle = TRUE; security_attributes.bInheritHandle = TRUE;
security_attributes_pointer = &security_attributes; security_attributes_pointer = &security_attributes;
} }
@ -136,4 +123,85 @@ HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name,
security_attributes_pointer); security_attributes_pointer);
} }
const void* GetSecurityDescriptorForNamedPipeInstance(size_t* size) {
// Mandatory Label, no ACE flags, no ObjectType, integrity level untrusted is
// "S:(ML;;;;;S-1-16-0)". Typically
// ConvertStringSecurityDescriptorToSecurityDescriptor() would be used to
// convert from a string representation. However, that function cannot be used
// because it is in advapi32.dll and CreateNamedPipeInstance() is called from
// within DllMain() where the loader lock is held. advapi32.dll is delay
// loaded in chrome_elf.dll because it must avoid loading user32.dll. If an
// advapi32.dll function were used, it would cause a load of the DLL, which
// would in turn cause deadlock.
#pragma pack(push, 1)
static const struct SecurityDescriptorBlob {
// See https://msdn.microsoft.com/en-us/library/cc230366.aspx.
SECURITY_DESCRIPTOR_RELATIVE sd_rel;
struct {
ACL acl;
struct {
// This is equivalent to SYSTEM_MANDATORY_LABEL_ACE, but there's no
// DWORD offset to the SID, instead it's inline.
ACE_HEADER header;
ACCESS_MASK mask;
SID sid;
} ace[1];
} sacl;
} kSecDescBlob = {
// sd_rel.
{
SECURITY_DESCRIPTOR_REVISION1, // Revision.
0x00, // Sbz1.
SE_SELF_RELATIVE | SE_SACL_PRESENT, // Control.
0, // OffsetOwner.
0, // OffsetGroup.
offsetof(SecurityDescriptorBlob, sacl), // OffsetSacl.
0, // OffsetDacl.
},
// sacl.
{
// acl.
{
ACL_REVISION, // AclRevision.
0, // Sbz1.
sizeof(kSecDescBlob.sacl), // AclSize.
arraysize(kSecDescBlob.sacl.ace), // AceCount.
0, // Sbz2.
},
// ace[0].
{
{
// header.
{
SYSTEM_MANDATORY_LABEL_ACE_TYPE, // AceType.
0, // AceFlags.
sizeof(kSecDescBlob.sacl.ace[0]), // AceSize.
},
// mask.
0,
// sid.
{
SID_REVISION, // Revision.
// SubAuthorityCount.
arraysize(kSecDescBlob.sacl.ace[0].sid.SubAuthority),
// IdentifierAuthority.
{SECURITY_MANDATORY_LABEL_AUTHORITY},
{SECURITY_MANDATORY_UNTRUSTED_RID}, // SubAuthority.
},
},
},
},
};
#pragma pack(pop)
if (size)
*size = sizeof(kSecDescBlob);
return reinterpret_cast<const void*>(&kSecDescBlob);
}
} // namespace crashpad } // namespace crashpad

View File

@ -145,6 +145,18 @@ bool SendToCrashHandlerServer(const base::string16& pipe_name,
HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name, HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name,
bool first_instance); bool first_instance);
//! \brief Returns the SECURITY_DESCRIPTOR blob that will be used for creating
//! the connection pipe in CreateNamedPipeInstance().
//!
//! This function is exposed for only for testing.
//!
//! \param[out] size The size of the returned blob. May be `nullptr` if not
//! required.
//!
//! \return A pointer to a self-relative `SECURITY_DESCRIPTOR`. Ownership is not
//! transferred to the caller.
const void* GetSecurityDescriptorForNamedPipeInstance(size_t* size);
} // namespace crashpad } // namespace crashpad
#endif // CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_ #endif // CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_

View File

@ -0,0 +1,53 @@
// Copyright 2016 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 "util/win/registration_protocol_win.h"
#include <windows.h>
#include <sddl.h>
#include <string.h>
#include "gtest/gtest.h"
#include "test/errors.h"
#include "util/win/scoped_local_alloc.h"
namespace crashpad {
namespace test {
namespace {
TEST(SecurityDescriptor, MatchesAdvapi32) {
// This security descriptor is built manually in the connection code to avoid
// calling the advapi32 functions. Verify that it returns the same thing as
// ConvertStringSecurityDescriptorToSecurityDescriptor() would.
// Mandatory Label, no ACE flags, no ObjectType, integrity level
// untrusted.
const wchar_t kSddl[] = L"S:(ML;;;;;S-1-16-0)";
PSECURITY_DESCRIPTOR sec_desc;
ULONG sec_desc_len;
ASSERT_TRUE(ConvertStringSecurityDescriptorToSecurityDescriptor(
kSddl, SDDL_REVISION_1, &sec_desc, &sec_desc_len))
<< ErrorMessage("ConvertStringSecurityDescriptorToSecurityDescriptor");
ScopedLocalAlloc sec_desc_owner(sec_desc);
size_t created_len;
const void* const created =
GetSecurityDescriptorForNamedPipeInstance(&created_len);
ASSERT_EQ(sec_desc_len, created_len);
EXPECT_EQ(0, memcmp(sec_desc, created, sec_desc_len));
}
} // namespace
} // namespace test
} // namespace crashpad