File: crashpad_linux.cc

package info (click to toggle)
chromium 139.0.7258.138-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 6,120,676 kB
  • sloc: cpp: 35,100,869; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (285 lines) | stat: -rw-r--r-- 10,775 bytes parent folder | download | duplicates (6)
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
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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

#include <pthread.h>
#include <sys/prctl.h>

#include <limits>

#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/linux_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/posix/global_descriptors.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/system/sys_info.h"
#include "build/branding_buildflags.h"
#include "build/chromeos_buildflags.h"
#include "components/crash/core/app/crash_reporter_client.h"
#include "components/crash/core/app/crash_switches.h"
#include "content/public/common/content_descriptors.h"
#include "sandbox/linux/services/namespace_sandbox.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
#include "third_party/crashpad/crashpad/client/crashpad_info.h"

#if BUILDFLAG(IS_CHROMEOS_DEVICE)
#include <sys/types.h>
#include <unistd.h>

#include <utility>

#include "base/at_exit.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "third_party/cros_system_api/constants/crash_reporter.h"
#endif

namespace crash_reporter {

namespace {

// TODO(jperaza): This is the first chance handler type used by Breakpad and v8.
// The Crashpad FirstChanceHandler type explicitly declares the third parameter
// to be a ucontext_t* instead of a void*. Using a reinterpret cast to convert
// one function type to another causes calling the handler to fail with SIGILL
// when CFI is enabled. Use a helper with Crashpad's FirstChanceHandler type
// to delegate to the real first chance handler instead of re-interpreting the
// handler itself. This can be removed if the two function types converge.
bool (*g_first_chance_handler)(int, siginfo_t*, void*);

bool FirstChanceHandlerHelper(int signo,
                              siginfo_t* siginfo,
                              ucontext_t* context) {
  return g_first_chance_handler(signo, siginfo, context);
}

#if BUILDFLAG(IS_CHROMEOS_DEVICE)
// Returns /run/crash_reporter/crashpad_ready/<pid>, the file we touch to
// tell crash_reporter that crashpad is ready and it doesn't need to use
// early-crash mode.
base::FilePath GetCrashpadReadyFilename() {
  pid_t pid = getpid();
  base::FilePath path(crash_reporter::kCrashpadReadyDirectory);
  return path.Append(base::NumberToString(pid));
}

// Inform crash_reporter than crashpad is ready to handle crashes by
// touching /run/crash_reporter/crashpad_ready/<pid>.
//
// Before this point, crash_reporter will attempt to handle Chrome crashes
// sent to it by the kernel core_pattern. This gives us a chance to catch
// crashes that happen before crashpad initializes. See code around
// UserCollector::handling_early_chrome_crash_ for details. Once the
// /run/crash_reporter/crashpad_ready/<pid> file exists, however,
// crash_reporter's UserCollector will assume crashpad will deal with the
// crash and can early-return.
//
// We only need this for the browser process; the cores are too large for
// other processes so early crash doesn't attempt to handle them.
void InformCrashReporterThatCrashpadIsReady() {
  base::FilePath path = GetCrashpadReadyFilename();
  // Note: Using a base::File with FLAG_CREATE instead of WriteFile() to
  // avoid symbolic link shenanigans.
  base::File file(path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
  if (!file.IsValid()) {
    LOG(ERROR) << "Could not create " << path << ": "
               << base::File::ErrorToString(file.error_details());
  }

  // Remove file when the program exits. This isn't perfect; if the program
  // crashes, and if the next browser process comes up with the same PID
  // (without an intervening reboot), and that browser process crashes before
  // crashpad initializes, then the crash service won't get the crash report.
  // This is unlikely enough that I think we can risk it, especially since
  // losing a single crash report is not a huge deal.
  base::AtExitManager::RegisterTask(base::BindOnce(&DeleteCrashpadIsReadyFile));
}
#endif  // BUILDFLAG(IS_CHROMEOS_DEVICE)

}  // namespace

void SetFirstChanceExceptionHandler(bool (*handler)(int, siginfo_t*, void*)) {
  DCHECK(!g_first_chance_handler);
  g_first_chance_handler = handler;
  crashpad::CrashpadClient::SetFirstChanceExceptionHandler(
      FirstChanceHandlerHelper);
}

bool GetHandlerSocket(int* fd, pid_t* pid) {
  return crashpad::CrashpadClient::GetHandlerSocket(fd, pid);
}

#if BUILDFLAG(IS_CHROMEOS_DEVICE)
void DeleteCrashpadIsReadyFile() {
  // Attempt delete but do not log errors if the delete fails. The file might
  // not exist if this function is called twice, or if Chrome did a fork-
  // without-exec and this function is not in the same process that called
  // InformCrashReporterThatCrashpadIsReady().
  base::DeleteFile(GetCrashpadReadyFilename());
}
#endif  // BUILDFLAG(IS_CHROMEOS_DEVICE)

namespace internal {

bool PlatformCrashpadInitialization(
    bool initial_client,
    bool browser_process,
    bool embedded_handler,
    const std::string& user_data_dir,
    const base::FilePath& exe_path,
    const std::vector<std::string>& initial_arguments,
    base::FilePath* database_path) {
  DCHECK_EQ(initial_client, browser_process);
  DCHECK(initial_arguments.empty());

  // Not used on Linux.
  DCHECK(!embedded_handler);
  DCHECK(exe_path.empty());

  crashpad::CrashpadClient client;
#if BUILDFLAG(IS_CHROMEOS)
  std::string crash_loop_before =
      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
          switches::kCrashLoopBefore);
  if (!crash_loop_before.empty()) {
    uint64_t crash_loop_before_time;
    if (!base::StringToUint64(crash_loop_before, &crash_loop_before_time)) {
      LOG(ERROR) << "Couldn't parse --crash-loop-before=" << crash_loop_before;
      DCHECK(false);
      crash_loop_before_time = std::numeric_limits<uint64_t>::max();
    }
    client.SetCrashLoopBefore(crash_loop_before_time);
  }
#endif

  CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
  if (initial_client) {
    base::FilePath metrics_path;
    crash_reporter_client->GetCrashDumpLocation(database_path);
    crash_reporter_client->GetCrashMetricsLocation(&metrics_path);

    base::FilePath handler_path;
    if (!base::PathService::Get(base::DIR_EXE, &handler_path)) {
      return false;
    }
    handler_path = handler_path.Append("chrome_crashpad_handler");

    // When --use-cros-crash-reporter is set (below), the handler passes dumps
    // to ChromeOS's /sbin/crash_reporter which in turn passes the dump to
    // crash_sender which handles the upload.
    std::string url;
#if !BUILDFLAG(IS_CHROMEOS_DEVICE)
    url = crash_reporter_client->GetUploadUrl();
#else
    url = std::string();
#endif

    ProductInfo product_info;
    crash_reporter_client->GetProductInfo(&product_info);

    std::map<std::string, std::string> annotations;
    annotations["prod"] = product_info.product_name;
    annotations["ver"] = product_info.version;

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
    // Empty means stable.
    const bool allow_empty_channel = true;
    if (product_info.channel == "extended") {
      // Extended stable reports as stable (empty string) with an extra bool.
      product_info.channel.clear();
      annotations["extended_stable_channel"] = "true";
    }
#else
    const bool allow_empty_channel = false;
#endif
    if (allow_empty_channel || !product_info.channel.empty()) {
      annotations["channel"] = product_info.channel;
    }

    annotations["plat"] = std::string("Linux");

#if BUILDFLAG(IS_CHROMEOS_DEVICE)
    // Chromium OS: save board and builder path for 'tast symbolize'.
    annotations["chromeos-board"] = base::SysInfo::GetLsbReleaseBoard();
    std::string builder_path;
    if (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILDER_PATH",
                                          &builder_path)) {
      annotations["chromeos-builder-path"] = builder_path;
    }

#else
    // Other Linux: save lsb-release. This isn't needed on Chromium OS,
    // where crash_reporter provides it's own values for lsb-release.
    annotations["lsb-release"] = base::GetLinuxDistro();
#endif

    std::vector<std::string> arguments;
    if (crash_reporter_client->ShouldMonitorCrashHandlerExpensively()) {
      arguments.push_back("--monitor-self");
    }

    // Set up --monitor-self-annotation even in the absence of --monitor-self
    // so that minidumps produced by Crashpad's generate_dump tool will
    // contain these annotations.
    arguments.push_back("--monitor-self-annotation=ptype=crashpad-handler");

#if BUILDFLAG(IS_CHROMEOS_DEVICE)
    arguments.push_back("--use-cros-crash-reporter");

    if (crash_reporter_client->IsRunningUnattended()) {
      arguments.push_back(base::StringPrintf("--minidump-dir-for-tests=%s",
                                             database_path->value().c_str()));
      arguments.push_back("--always-allow-feedback");
    }
#endif

    CHECK(client.StartHandler(handler_path, *database_path, metrics_path, url,
                              annotations, arguments, false, false));
  } else {
    int fd = base::GlobalDescriptors::GetInstance()->Get(kCrashDumpSignal);

    pid_t pid = 0;
    if (!sandbox::NamespaceSandbox::InNewUserNamespace()) {
      std::string pid_string =
          base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
              switches::kCrashpadHandlerPid);
      bool parsed = base::StringToInt(pid_string, &pid);
      DCHECK(parsed);
    }

    // SIGSYS handling is reserved for the sandbox.
    client.SetUnhandledSignals({SIGSYS});

    client.SetHandlerSocket(crashpad::ScopedFileHandle(fd), pid);

    *database_path = base::FilePath();
  }

  // In the not-large-dumps case record enough extra memory to be able to save
  // dereferenced memory from all registers on the crashing thread. crashpad may
  // save 512-bytes per register, and the largest register set (not including
  // stack pointers) is ARM64 with 32 registers. Hence, 16 KiB.
  const uint32_t kIndirectMemoryLimit =
      crash_reporter_client->GetShouldDumpLargerDumps() ? 4 * 1024 * 1024
                                                        : 16 * 1024;
  crashpad::CrashpadInfo::GetCrashpadInfo()
      ->set_gather_indirectly_referenced_memory(crashpad::TriState::kEnabled,
                                                kIndirectMemoryLimit);

#if BUILDFLAG(IS_CHROMEOS_DEVICE)
  if (initial_client) {
    InformCrashReporterThatCrashpadIsReady();
  }
#endif

  return true;
}

}  // namespace internal
}  // namespace crash_reporter