crashpad/handler/win/crashy_test_program.cc
Mark Mentovai 281be63d00 Standardize on static constexpr for arrays when possible
This uses “static” at function scope to avoid making local copies, even
in cases where the compiler can’t see that the local copy is
unnecessary. “constexpr” adds additional safety in that it prevents
global state from being initialized from any runtime dependencies, which
would be undesirable.

At namespace scope, “constexpr” is also used where appropriate.

For the most part, this was a mechanical transformation for things
matching '(^| )const [^=]*\['.

Similar transformations could be applied to non-arrays in some cases,
but there’s limited practical impact in most non-array cases relative to
arrays, there are far more use sites, and much more manual intervention
would be required.

Change-Id: I3513b739ee8b0be026f8285475cddc5f9cc81152
Reviewed-on: https://chromium-review.googlesource.com/583997
Commit-Queue: Mark Mentovai <mark@chromium.org>
Reviewed-by: Leonard Mosescu <mosescu@chromium.org>
2017-07-25 17:40:51 +00:00

259 lines
8.3 KiB
C++

// Copyright 2015 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 <intrin.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <windows.h>
#include <winternl.h>
#include <map>
#include <string>
#include <vector>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "client/crashpad_client.h"
#include "client/crashpad_info.h"
#include "util/win/critical_section_with_debug_info.h"
#include "util/win/get_function.h"
// ntstatus.h conflicts with windows.h so define this locally.
#ifndef STATUS_NO_SUCH_FILE
#define STATUS_NO_SUCH_FILE static_cast<NTSTATUS>(0xC000000F)
#endif
namespace crashpad {
int* g_extra_memory_pointer;
int* g_extra_memory_not_saved;
namespace {
CRITICAL_SECTION g_test_critical_section;
unsigned char g_test_memory[] = {
99, 98, 97, 96, 95, 94, 93, 92, 91, 90,
89, 88, 87, 86, 85, 84, 83, 82, 81, 80,
};
ULONG RtlNtStatusToDosError(NTSTATUS status) {
static const auto rtl_nt_status_to_dos_error =
GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlNtStatusToDosError);
return rtl_nt_status_to_dos_error(status);
}
void AllocateMemoryOfVariousProtections() {
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
const size_t kPageSize = system_info.dwPageSize;
static constexpr uint32_t kPageTypes[] = {
PAGE_NOACCESS,
PAGE_READONLY,
PAGE_READWRITE,
PAGE_EXECUTE,
PAGE_EXECUTE_READ,
PAGE_EXECUTE_READWRITE,
// PAGE_NOACCESS is invalid with PAGE_GUARD.
PAGE_READONLY | PAGE_GUARD,
PAGE_READWRITE | PAGE_GUARD,
PAGE_EXECUTE | PAGE_GUARD,
PAGE_EXECUTE_READ | PAGE_GUARD,
PAGE_EXECUTE_READWRITE | PAGE_GUARD,
};
// All of these allocations are leaked, we want to view them in windbg via
// !vprot.
void* reserve = VirtualAlloc(
nullptr, arraysize(kPageTypes) * kPageSize, MEM_RESERVE, PAGE_READWRITE);
PCHECK(reserve) << "VirtualAlloc MEM_RESERVE";
uintptr_t reserve_as_int = reinterpret_cast<uintptr_t>(reserve);
for (size_t i = 0; i < arraysize(kPageTypes); ++i) {
void* result =
VirtualAlloc(reinterpret_cast<void*>(reserve_as_int + (kPageSize * i)),
kPageSize,
MEM_COMMIT,
kPageTypes[i]);
PCHECK(result) << "VirtualAlloc MEM_COMMIT " << i;
}
}
DWORD WINAPI NullThreadProc(void* param) {
return 0;
}
// Creates a suspended background thread, and sets EDI/RDI to point at
// g_test_memory so we can confirm it's available in the minidump.
bool CreateThreadWithRegisterPointingToTestMemory() {
HANDLE thread = CreateThread(
nullptr, 0, &NullThreadProc, nullptr, CREATE_SUSPENDED, nullptr);
if (!thread) {
PLOG(ERROR) << "CreateThread";
return false;
}
CONTEXT context = {0};
context.ContextFlags = CONTEXT_INTEGER;
if (!GetThreadContext(thread, &context)) {
PLOG(ERROR) << "GetThreadContext";
return false;
}
#if defined(ARCH_CPU_X86_64)
context.Rdi = reinterpret_cast<DWORD64>(g_test_memory);
#elif defined(ARCH_CPU_X86)
context.Edi = reinterpret_cast<DWORD>(g_test_memory);
#endif
if (!SetThreadContext(thread, &context)) {
PLOG(ERROR) << "SetThreadContext";
return false;
}
return true;
}
void SomeCrashyFunction() {
// SetLastError and NTSTATUS so that we have something to view in !gle in
// windbg. RtlNtStatusToDosError() stores STATUS_NO_SUCH_FILE into the
// LastStatusError of the TEB as a side-effect, and we'll be setting
// ERROR_FILE_NOT_FOUND for GetLastError().
SetLastError(RtlNtStatusToDosError(STATUS_NO_SUCH_FILE));
volatile int* foo = reinterpret_cast<volatile int*>(7);
*foo = 42;
}
void AllocateExtraMemoryToBeSaved(
crashpad::SimpleAddressRangeBag* extra_ranges) {
const size_t kNumInts = 2000;
int* extra_memory = new int[kNumInts];
g_extra_memory_pointer = extra_memory;
for (int i = 0; i < kNumInts; ++i)
extra_memory[i] = i * 13 + 2;
extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumInts);
extra_ranges->Insert(&g_extra_memory_pointer, sizeof(g_extra_memory_pointer));
}
void AllocateExtraUnsavedMemory(crashpad::SimpleAddressRangeBag* extra_ranges) {
// Allocate some extra memory, and then Insert() but also Remove() it so we
// can confirm it doesn't get saved.
const size_t kNumInts = 2000;
int* extra_memory = new int[kNumInts];
g_extra_memory_not_saved = extra_memory;
for (int i = 0; i < kNumInts; ++i)
extra_memory[i] = i * 17 + 7;
extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumInts);
extra_ranges->Insert(&g_extra_memory_not_saved,
sizeof(g_extra_memory_not_saved));
// We keep the pointer's memory, but remove the pointed-to memory.
extra_ranges->Remove(extra_memory, sizeof(extra_memory[0]) * kNumInts);
}
int CrashyMain(int argc, wchar_t* argv[]) {
CrashpadClient client;
if (argc == 2) {
if (!client.SetHandlerIPCPipe(argv[1])) {
LOG(ERROR) << "SetHandler";
return EXIT_FAILURE;
}
} else if (argc == 3) {
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;
}
} else {
fprintf(stderr, "Usage: %ls <server_pipe_name>\n", argv[0]);
fprintf(stderr, " %ls <handler_path> <database_path>\n", argv[0]);
return EXIT_FAILURE;
}
crashpad::SimpleAddressRangeBag* extra_ranges =
new crashpad::SimpleAddressRangeBag();
crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(
extra_ranges);
AllocateExtraMemoryToBeSaved(extra_ranges);
AllocateExtraUnsavedMemory(extra_ranges);
// 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 element size
// advancement of RTL_UNLOAD_EVENT_TRACE correct.
CHECK(GetModuleHandle(L"lz32.dll") == nullptr);
CHECK(GetModuleHandle(L"wmerror.dll") == nullptr);
HMODULE lz32 = LoadLibrary(L"lz32.dll");
HMODULE wmerror = LoadLibrary(L"wmerror.dll");
FreeLibrary(lz32);
FreeLibrary(wmerror);
// Make sure data pointed to by the stack is captured.
const int kDataSize = 512;
int* pointed_to_data = new int[kDataSize];
for (int i = 0; i < kDataSize; ++i)
pointed_to_data[i] = i | ((i % 2 == 0) ? 0x80000000 : 0);
int* offset_pointer = &pointed_to_data[128];
// Encourage the compiler to keep this variable around.
printf("%p, %p\n", offset_pointer, &offset_pointer);
crashpad::CrashpadInfo::GetCrashpadInfo()
->set_gather_indirectly_referenced_memory(
TriState::kEnabled, std::numeric_limits<uint32_t>::max());
std::vector<uint8_t> data_stream1(128, 'x');
crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream(
222222,
reinterpret_cast<const void*>(data_stream1.data()),
data_stream1.size());
std::vector<uint8_t> data_stream2(4096, 'z');
crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream(
333333,
reinterpret_cast<const void*>(data_stream2.data()),
data_stream2.size());
AllocateMemoryOfVariousProtections();
if (InitializeCriticalSectionWithDebugInfoIfPossible(
&g_test_critical_section)) {
EnterCriticalSection(&g_test_critical_section);
}
if (!CreateThreadWithRegisterPointingToTestMemory())
return EXIT_FAILURE;
SomeCrashyFunction();
return EXIT_SUCCESS;
}
} // namespace
} // namespace crashpad
int wmain(int argc, wchar_t* argv[]) {
return crashpad::CrashyMain(argc, argv);
}