File: crashpad_win.cc

package info (click to toggle)
chromium-browser 57.0.2987.98-1~deb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 2,637,852 kB
  • ctags: 2,544,394
  • sloc: cpp: 12,815,961; ansic: 3,676,222; python: 1,147,112; asm: 526,608; java: 523,212; xml: 286,794; perl: 92,654; sh: 86,408; objc: 73,271; makefile: 27,698; cs: 18,487; yacc: 13,031; tcl: 12,957; pascal: 4,875; ml: 4,716; lex: 3,904; sql: 3,862; ruby: 1,982; lisp: 1,508; php: 1,368; exp: 404; awk: 325; csh: 117; jsp: 39; sed: 37
file content (368 lines) | stat: -rw-r--r-- 13,947 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/crash/content/app/crashpad.h"

#include <memory>

#include "base/debug/crash_logging.h"
#include "base/environment.h"
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/crash/content/app/crash_reporter_client.h"
#include "components/crash/content/app/crash_switches.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
#include "third_party/crashpad/crashpad/client/crashpad_info.h"
#include "third_party/crashpad/crashpad/client/simulate_crash_win.h"

namespace crash_reporter {
namespace internal {

namespace {

base::LazyInstance<crashpad::CrashpadClient>::Leaky g_crashpad_client =
    LAZY_INSTANCE_INITIALIZER;

}  // namespace

void GetPlatformCrashpadAnnotations(
    std::map<std::string, std::string>* annotations) {
  CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
  wchar_t exe_file[MAX_PATH] = {};
  CHECK(::GetModuleFileName(nullptr, exe_file, arraysize(exe_file)));
  base::string16 product_name, version, special_build, channel_name;
  crash_reporter_client->GetProductNameAndVersion(
      exe_file, &product_name, &version, &special_build, &channel_name);
  (*annotations)["prod"] = base::UTF16ToUTF8(product_name);
  (*annotations)["ver"] = base::UTF16ToUTF8(version);
  (*annotations)["channel"] = base::UTF16ToUTF8(channel_name);
  if (!special_build.empty())
    (*annotations)["special"] = base::UTF16ToUTF8(special_build);
#if defined(ARCH_CPU_X86)
  (*annotations)["plat"] = std::string("Win32");
#elif defined(ARCH_CPU_X86_64)
  (*annotations)["plat"] = std::string("Win64");
#endif
}

base::FilePath PlatformCrashpadInitialization(bool initial_client,
                                              bool browser_process,
                                              bool embedded_handler) {
  base::FilePath database_path;  // Only valid in the browser process.
  base::FilePath metrics_path;  // Only valid in the browser process.

  const char kPipeNameVar[] = "CHROME_CRASHPAD_PIPE_NAME";
  const char kServerUrlVar[] = "CHROME_CRASHPAD_SERVER_URL";
  std::unique_ptr<base::Environment> env(base::Environment::Create());
  if (initial_client) {
    CrashReporterClient* crash_reporter_client = GetCrashReporterClient();

    base::string16 database_path_str;
    if (crash_reporter_client->GetCrashDumpLocation(&database_path_str))
      database_path = base::FilePath(database_path_str);

    base::string16 metrics_path_str;
    if (crash_reporter_client->GetCrashMetricsLocation(&metrics_path_str)) {
      metrics_path = base::FilePath(metrics_path_str);
      CHECK(base::CreateDirectoryAndGetError(metrics_path, nullptr));
    }

    std::map<std::string, std::string> process_annotations;
    GetPlatformCrashpadAnnotations(&process_annotations);

#if defined(GOOGLE_CHROME_BUILD)
    std::string url = "https://clients2.google.com/cr/report";
#else
    std::string url;
#endif

    // Allow the crash server to be overridden for testing. If the variable
    // isn't present in the environment then the default URL will remain.
    env->GetVar(kServerUrlVar, &url);

    wchar_t exe_file_path[MAX_PATH] = {};
    CHECK(
        ::GetModuleFileName(nullptr, exe_file_path, arraysize(exe_file_path)));

    base::FilePath exe_file(exe_file_path);

    bool is_per_user_install =
        crash_reporter_client->GetIsPerUserInstall(exe_file.value());
    if (crash_reporter_client->GetShouldDumpLargerDumps(is_per_user_install)) {
      const uint32_t kIndirectMemoryLimit = 4 * 1024 * 1024;
      crashpad::CrashpadInfo::GetCrashpadInfo()
          ->set_gather_indirectly_referenced_memory(
              crashpad::TriState::kEnabled, kIndirectMemoryLimit);
    }

    // If the handler is embedded in the binary (e.g. chrome, setup), we
    // reinvoke it with --type=crashpad-handler. Otherwise, we use the
    // standalone crashpad_handler.exe (for tests, etc.).
    std::vector<std::string> arguments;
    if (embedded_handler) {
      arguments.push_back(std::string("--type=") + switches::kCrashpadHandler);
      // The prefetch argument added here has to be documented in
      // chrome_switches.cc, below the kPrefetchArgument* constants. A constant
      // can't be used here because crashpad can't depend on Chrome.
      arguments.push_back("/prefetch:7");
    } else {
      base::FilePath exe_dir = exe_file.DirName();
      exe_file = exe_dir.Append(FILE_PATH_LITERAL("crashpad_handler.exe"));
    }

    if (!g_crashpad_client.Get().StartHandler(
            exe_file, database_path, metrics_path, url, process_annotations,
            arguments, false, true)) {
      // This means that CreateThread() failed, so this process is very messed
      // up. This should be effectively unreachable. It is unlikely that there
      // is any utility to ever making this non-fatal, however, if this is done,
      // calls to BlockUntilHandlerStarted() will have to be amended.
      LOG(FATAL) << "synchronous part of handler startup failed";
    }

    // If we're the browser, push the pipe name into the environment so child
    // processes can connect to it. If we inherited another crashpad_handler's
    // pipe name, we'll overwrite it here.
    env->SetVar(kPipeNameVar,
                base::UTF16ToUTF8(g_crashpad_client.Get().GetHandlerIPCPipe()));
  } else {
    std::string pipe_name_utf8;
    if (env->GetVar(kPipeNameVar, &pipe_name_utf8)) {
      g_crashpad_client.Get().SetHandlerIPCPipe(
          base::UTF8ToUTF16(pipe_name_utf8));
    }
  }

  return database_path;
}

// TODO(scottmg): http://crbug.com/546288 These exported functions are for
// compatibility with how Breakpad worked, but it seems like there's no need to
// do the CreateRemoteThread() dance with a minor extension of the Crashpad API
// (to just pass the pid we want a dump for). We should add that and then modify
// hang_crash_dump_win.cc to work in a more direct manner.

// Used for dumping a process state when there is no crash.
extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() {
  CRASHPAD_SIMULATE_CRASH();
}

namespace {

// We need to prevent ICF from folding DumpForHangDebuggingThread(),
// DumpProcessForHungInputThread(), DumpProcessForHungInputNoCrashKeysThread()
// and DumpProcessWithoutCrashThread() together, since that makes them
// indistinguishable in crash dumps. We do this by making the function
// bodies unique, and prevent optimization from shuffling things around.
MSVC_DISABLE_OPTIMIZE()
MSVC_PUSH_DISABLE_WARNING(4748)

// Note that this function must be in a namespace for the [Renderer hang]
// annotations to work on the crash server.
DWORD WINAPI DumpProcessWithoutCrashThread(void*) {
  DumpProcessWithoutCrash();
  return 0;
}

// TODO(dtapuska): Remove when enough information is gathered where the crash
// reports without crash keys come from.
DWORD WINAPI DumpProcessForHungInputThread(void* crash_keys_str) {
  base::StringPairs crash_keys;
  if (crash_keys_str && base::SplitStringIntoKeyValuePairs(
                            reinterpret_cast<const char*>(crash_keys_str), ':',
                            ',', &crash_keys)) {
    for (const auto& crash_key : crash_keys) {
      base::debug::SetCrashKeyValue(crash_key.first, crash_key.second);
    }
  }
  DumpProcessWithoutCrash();
  return 0;
}

// TODO(dtapuska): Remove when enough information is gathered where the crash
// reports without crash keys come from.
DWORD WINAPI DumpProcessForHungInputNoCrashKeysThread(void* reason) {
#pragma warning(push)
#pragma warning(disable : 4311 4302)
  base::debug::SetCrashKeyValue(
      "hung-reason", base::IntToString(reinterpret_cast<int>(reason)));
#pragma warning(pop)

  DumpProcessWithoutCrash();
  return 0;
}

// TODO(yzshen): Remove when enough information is collected and the hang rate
// of pepper/renderer processes is reduced.
DWORD WINAPI DumpForHangDebuggingThread(void*) {
  DumpProcessWithoutCrash();
  VLOG(1) << "dumped for hang debugging";
  return 0;
}

MSVC_POP_WARNING()
MSVC_ENABLE_OPTIMIZE()

}  // namespace

}  // namespace internal

void BlockUntilHandlerStarted() {
  // We know that the StartHandler() at least started asynchronous startup if
  // we're here, as if it doesn't, we abort.
  const unsigned int kTimeoutMS = 5000;
  if (!internal::g_crashpad_client.Get().WaitForHandlerStart(kTimeoutMS)) {
    LOG(ERROR) << "Crashpad handler failed to start, crash reporting disabled";
  }
}

}  // namespace crash_reporter

extern "C" {

// Crashes the process after generating a dump for the provided exception. Note
// that the crash reporter should be initialized before calling this function
// for it to do anything.
// NOTE: This function is used by SyzyASAN to invoke a crash. If you change the
// the name or signature of this function you will break SyzyASAN instrumented
// releases of Chrome. Please contact syzygy-team@chromium.org before doing so!
int __declspec(dllexport) CrashForException(
    EXCEPTION_POINTERS* info) {
  crash_reporter::internal::g_crashpad_client.Get().DumpAndCrash(info);
  return EXCEPTION_CONTINUE_SEARCH;
}

// Injects a thread into a remote process to dump state when there is no crash.
HANDLE __declspec(dllexport) __cdecl InjectDumpProcessWithoutCrash(
    HANDLE process) {
  return CreateRemoteThread(
      process, nullptr, 0,
      crash_reporter::internal::DumpProcessWithoutCrashThread, nullptr, 0,
      nullptr);
}

// Injects a thread into a remote process to dump state when there is no crash.
// |serialized_crash_keys| is a nul terminated string that represents serialized
// crash keys sent from the browser. Keys and values are separated by ':', and
// key/value pairs are separated by ','. All keys should be previously
// registered as crash keys. This method is used solely to classify hung input.
HANDLE __declspec(dllexport) __cdecl InjectDumpForHungInput(
    HANDLE process,
    void* serialized_crash_keys) {
  return CreateRemoteThread(
      process, nullptr, 0,
      crash_reporter::internal::DumpProcessForHungInputThread,
      serialized_crash_keys, 0, nullptr);
}

// Injects a thread into a remote process to dump state when there is no crash.
// This method provides |reason| which will interpreted as an integer and logged
// as a crash key.
HANDLE __declspec(dllexport) __cdecl InjectDumpForHungInputNoCrashKeys(
    HANDLE process,
    int reason) {
  return CreateRemoteThread(
      process, nullptr, 0,
      crash_reporter::internal::DumpProcessForHungInputNoCrashKeysThread,
      reinterpret_cast<void*>(reason), 0, nullptr);
}

HANDLE __declspec(dllexport) __cdecl InjectDumpForHangDebugging(
    HANDLE process) {
  return CreateRemoteThread(
      process, nullptr, 0, crash_reporter::internal::DumpForHangDebuggingThread,
      0, 0, nullptr);
}

#if defined(ARCH_CPU_X86_64)

static int CrashForExceptionInNonABICompliantCodeRange(
    PEXCEPTION_RECORD ExceptionRecord,
    ULONG64 EstablisherFrame,
    PCONTEXT ContextRecord,
    PDISPATCHER_CONTEXT DispatcherContext) {
  EXCEPTION_POINTERS info = { ExceptionRecord, ContextRecord };
  return CrashForException(&info);
}

// See https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
typedef struct _UNWIND_INFO {
  unsigned char Version : 3;
  unsigned char Flags : 5;
  unsigned char SizeOfProlog;
  unsigned char CountOfCodes;
  unsigned char FrameRegister : 4;
  unsigned char FrameOffset : 4;
  ULONG ExceptionHandler;
} UNWIND_INFO, *PUNWIND_INFO;

struct ExceptionHandlerRecord {
  RUNTIME_FUNCTION runtime_function;
  UNWIND_INFO unwind_info;
  unsigned char thunk[12];
};

// These are GetProcAddress()d from V8 binding code.
void __declspec(dllexport) __cdecl RegisterNonABICompliantCodeRange(
    void* start,
    size_t size_in_bytes) {
  ExceptionHandlerRecord* record =
      reinterpret_cast<ExceptionHandlerRecord*>(start);

  // We assume that the first page of the code range is executable and
  // committed and reserved for breakpad. What could possibly go wrong?

  // All addresses are 32bit relative offsets to start.
  record->runtime_function.BeginAddress = 0;
  record->runtime_function.EndAddress =
      base::checked_cast<DWORD>(size_in_bytes);
  record->runtime_function.UnwindData =
      offsetof(ExceptionHandlerRecord, unwind_info);

  // Create unwind info that only specifies an exception handler.
  record->unwind_info.Version = 1;
  record->unwind_info.Flags = UNW_FLAG_EHANDLER;
  record->unwind_info.SizeOfProlog = 0;
  record->unwind_info.CountOfCodes = 0;
  record->unwind_info.FrameRegister = 0;
  record->unwind_info.FrameOffset = 0;
  record->unwind_info.ExceptionHandler =
      offsetof(ExceptionHandlerRecord, thunk);

  // Hardcoded thunk.
  // mov imm64, rax
  record->thunk[0] = 0x48;
  record->thunk[1] = 0xb8;
  void* handler = &CrashForExceptionInNonABICompliantCodeRange;
  memcpy(&record->thunk[2], &handler, 8);

  // jmp rax
  record->thunk[10] = 0xff;
  record->thunk[11] = 0xe0;

  // Protect reserved page against modifications.
  DWORD old_protect;
  CHECK(VirtualProtect(
      start, sizeof(ExceptionHandlerRecord), PAGE_EXECUTE_READ, &old_protect));
  CHECK(RtlAddFunctionTable(
      &record->runtime_function, 1, reinterpret_cast<DWORD64>(start)));
}

void __declspec(dllexport) __cdecl UnregisterNonABICompliantCodeRange(
    void* start) {
  ExceptionHandlerRecord* record =
      reinterpret_cast<ExceptionHandlerRecord*>(start);

  CHECK(RtlDeleteFunctionTable(&record->runtime_function));
}
#endif  // ARCH_CPU_X86_64

}  // extern "C"