mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-26 06:31:50 +08:00
Catch heap corruption failures on Windows
Windows claims that heap corruption crashes are passed to Windows Error Reporting but they are not, they are swallowed and the process is simply terminated. WerFault.exe does not run. We can however intercept these crashes using a vectored exception handler which forwards STATUS_HEAP_CORRUPTION to the normal crash handler. Adds an end-to-end test. Bug: 2515 Change-Id: I2e1361dacef6fd03ea0f00327fee0b05a0c4899e Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4637533 Commit-Queue: Alex Gough <ajgo@chromium.org> Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
parent
bc1e904f09
commit
a5e179663a
@ -187,6 +187,15 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
LONG WINAPI HandleHeapCorruption(EXCEPTION_POINTERS* exception_pointers) {
|
||||
if (exception_pointers->ExceptionRecord->ExceptionCode ==
|
||||
STATUS_HEAP_CORRUPTION) {
|
||||
return UnhandledExceptionHandler(exception_pointers);
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
void HandleAbortSignal(int signum) {
|
||||
DCHECK_EQ(signum, SIGABRT);
|
||||
|
||||
@ -580,6 +589,16 @@ void CommonInProcessInitialization() {
|
||||
void RegisterHandlers() {
|
||||
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
||||
|
||||
// Windows swallows heap corruption failures but we can intercept them with
|
||||
// a vectored exception handler.
|
||||
#if defined(ADDRESS_SANITIZER)
|
||||
// Let ASAN have first go.
|
||||
bool go_first = false;
|
||||
#else
|
||||
bool go_first = true;
|
||||
#endif
|
||||
AddVectoredExceptionHandler(go_first, HandleHeapCorruption);
|
||||
|
||||
// The Windows CRT's signal.h lists:
|
||||
// - SIGINT
|
||||
// - SIGILL
|
||||
|
@ -49,10 +49,9 @@ static_library("handler") {
|
||||
"linux/cros_crash_report_exception_handler.cc",
|
||||
"linux/cros_crash_report_exception_handler.h",
|
||||
]
|
||||
|
||||
# TODO(https://crbug.com/1420445): Remove this config when M115 branches.
|
||||
configs += [
|
||||
"../build:crashpad_is_in_chromium",
|
||||
]
|
||||
configs += [ "../build:crashpad_is_in_chromium" ]
|
||||
}
|
||||
|
||||
if (crashpad_is_win) {
|
||||
@ -346,6 +345,19 @@ if (crashpad_is_win) {
|
||||
]
|
||||
}
|
||||
|
||||
crashpad_executable("heap_corrupting_program") {
|
||||
testonly = true
|
||||
|
||||
sources = [ "win/heap_corrupting_program.cc" ]
|
||||
|
||||
deps = [
|
||||
"../client",
|
||||
"../compat",
|
||||
"../snapshot",
|
||||
"../third_party/mini_chromium:base",
|
||||
]
|
||||
}
|
||||
|
||||
if (current_cpu == "x86") {
|
||||
# Cannot create an x64 DLL with embedded debug info.
|
||||
crashpad_executable("crashy_z7_loader") {
|
||||
|
95
handler/win/heap_corrupting_program.cc
Normal file
95
handler/win/heap_corrupting_program.cc
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright 2023 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 <string.h>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "client/crashpad_client.h"
|
||||
#include "util/misc/paths.h"
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
// We set up a program that crashes with a heap corruption exception.
|
||||
// STATUS_HEAP_CORRUPTION (0xC0000374 3221226356).
|
||||
namespace crashpad {
|
||||
namespace {
|
||||
|
||||
void HeapCorruptionCrash() {
|
||||
__try {
|
||||
HANDLE heap = ::HeapCreate(0, 0, 0);
|
||||
CHECK(heap);
|
||||
CHECK(HeapSetInformation(
|
||||
heap, HeapEnableTerminationOnCorruption, nullptr, 0));
|
||||
void* addr = ::HeapAlloc(heap, 0, 0x1000);
|
||||
CHECK(addr);
|
||||
// Corrupt heap header.
|
||||
char* addr_mutable = reinterpret_cast<char*>(addr);
|
||||
memset(addr_mutable - sizeof(addr), 0xCC, sizeof(addr));
|
||||
|
||||
HeapFree(heap, 0, addr);
|
||||
HeapDestroy(heap);
|
||||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
// Heap corruption exception should never be caught.
|
||||
CHECK(false);
|
||||
}
|
||||
// Should never reach here.
|
||||
abort();
|
||||
}
|
||||
|
||||
int CrashyMain(int argc, wchar_t* argv[]) {
|
||||
static CrashpadClient* client = new crashpad::CrashpadClient();
|
||||
|
||||
if (argc == 2) {
|
||||
// We call this from end_to_end_test.py.
|
||||
if (!client->SetHandlerIPCPipe(argv[1])) {
|
||||
LOG(ERROR) << "SetHandler";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else if (argc == 3) {
|
||||
// This is helpful for debugging.
|
||||
if (!client->StartHandler(base::FilePath(argv[1]),
|
||||
base::FilePath(argv[2]),
|
||||
base::FilePath(),
|
||||
std::string(),
|
||||
std::map<std::string, std::string>(),
|
||||
std::vector<std::string>(),
|
||||
false,
|
||||
true)) {
|
||||
LOG(ERROR) << "StartHandler";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// Got to have a handler & registration.
|
||||
if (!client->WaitForHandlerStart(10000)) {
|
||||
LOG(ERROR) << "Handler failed to start";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Usage: %ls <server_pipe_name>\n", argv[0]);
|
||||
fprintf(stderr, " %ls <handler_path> <database_path>\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
HeapCorruptionCrash();
|
||||
|
||||
LOG(ERROR) << "Invalid type or exception failed.";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace crashpad
|
||||
|
||||
int wmain(int argc, wchar_t* argv[]) {
|
||||
return crashpad::CrashyMain(argc, argv);
|
||||
}
|
@ -212,6 +212,12 @@ def GetDumpFromZ7Program(out_dir, pipe_name):
|
||||
win32con.EXCEPTION_ACCESS_VIOLATION)
|
||||
|
||||
|
||||
def GetDumpFromHeapCorruptingProgram(out_dir, pipe_name):
|
||||
STATUS_HEAP_CORRUPTION = 0xC0000374
|
||||
return GetDumpFromProgram(out_dir, pipe_name, 'heap_corrupting_program.exe',
|
||||
STATUS_HEAP_CORRUPTION)
|
||||
|
||||
|
||||
def GetDumpFromFastFailProgram(out_dir, pipe_name, *args):
|
||||
STATUS_STACK_BUFFER_OVERRUN = 0xc0000409
|
||||
return GetDumpFromProgram(out_dir, pipe_name, 'fastfail_program.exe',
|
||||
@ -444,6 +450,14 @@ def RunSigAbrtTest(cdb_path, sigabrt_main_path, sigabrt_background_path):
|
||||
out.Check('code 40000015', 'got sigabrt signal from background thread')
|
||||
|
||||
|
||||
def RunHeapCorruptionTest(cdb_path, heap_path):
|
||||
"""Runs tests on heap corruption caught using the vectored handler."""
|
||||
out = CdbRun(cdb_path, heap_path, '.ecxr;k')
|
||||
out.Check('code c0000374', 'captured exception from heap corruption crash')
|
||||
out.Check('::HeapCorruptionCrash', 'See expected throwing function')
|
||||
out = CdbRun(cdb_path, heap_path, '.ecxr;k')
|
||||
|
||||
|
||||
def RunFastFailDumpTest(cdb_path, fastfail_path):
|
||||
"""Runs tests on __fastfail() caught using the runtime exception helper."""
|
||||
out = CdbRun(cdb_path, fastfail_path, '.ecxr;k')
|
||||
@ -541,6 +555,11 @@ def main(args):
|
||||
return 1
|
||||
Run7zDumpTest(cdb_path, z7_dump_path)
|
||||
|
||||
heap_path = GetDumpFromHeapCorruptingProgram(args[0], pipe_name)
|
||||
if not heap_path:
|
||||
return 1
|
||||
RunHeapCorruptionTest(cdb_path, heap_path)
|
||||
|
||||
# __fastfail() & CFG crash caught by WerRuntimeExceptionHelperModule.
|
||||
# TODO(crashpad:458) These are not working when launched from python.
|
||||
if (False and Win32_20H1()):
|
||||
|
Loading…
x
Reference in New Issue
Block a user