diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index 3de50779..37469b20 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -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 diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 02bf11a6..7cab5191 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -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") { diff --git a/handler/win/heap_corrupting_program.cc b/handler/win/heap_corrupting_program.cc new file mode 100644 index 00000000..6c7c2cd5 --- /dev/null +++ b/handler/win/heap_corrupting_program.cc @@ -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 + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "client/crashpad_client.h" +#include "util/misc/paths.h" + +#include + +// 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(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::vector(), + 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 \n", argv[0]); + fprintf(stderr, " %ls \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); +} diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py index 86da4651..25f661c1 100755 --- a/snapshot/win/end_to_end_test.py +++ b/snapshot/win/end_to_end_test.py @@ -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()):