blob: dd27c77422f78a55622782d632997621a955aac2 [file] [log] [blame]
// Copyright 2019 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 <pthread.h>
#include <sys/prctl.h>
#include <limits>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/posix/global_descriptors.h"
#include "base/strings/string_number_conversions.h"
#include "build/branding_buildflags.h"
#include "components/crash/content/app/crash_reporter_client.h"
#include "components/crash/content/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"
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);
}
} // namespace
void SetFirstChanceExceptionHandler(bool (*handler)(int, siginfo_t*, void*)) {
DCHECK(!g_first_chance_handler);
g_first_chance_handler = handler;
crashpad::CrashpadClient::SetFirstChanceExceptionHandler(
FirstChanceHandlerHelper);
}
// TODO(jperaza): Remove kEnableCrashpad and IsCrashpadEnabled() when Crashpad
// is fully enabled on Linux.
const char kEnableCrashpad[] = "enable-crashpad";
bool IsCrashpadEnabled() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(kEnableCrashpad);
}
bool GetHandlerSocket(int* fd, pid_t* pid) {
return crashpad::CrashpadClient::GetHandlerSocket(fd, pid);
}
void SetPtracerAtFork() {
pid_t pid;
if (!GetHandlerSocket(nullptr, &pid)) {
return;
}
if (pid > 0 && prctl(PR_SET_PTRACER, pid, 0, 0, 0) != 0) {
PLOG(ERROR) << "prctl";
}
}
namespace internal {
base::FilePath 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) {
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 defined(OS_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
if (initial_client) {
CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
base::FilePath database_path, 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 database_path;
}
handler_path = handler_path.Append("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(GOOGLE_CHROME_BRANDING) && defined(OFFICIAL_BUILD) && \
!defined(OS_CHROMEOS)
url = "https://clients2.google.com/cr/report";
#else
url = std::string();
#endif
std::string product_name, product_version, channel;
crash_reporter_client->GetProductNameAndVersion(&product_name,
&product_version, &channel);
std::map<std::string, std::string> annotations;
annotations["prod"] = product_name;
annotations["ver"] = product_version;
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
// Empty means stable.
const bool allow_empty_channel = true;
#else
const bool allow_empty_channel = false;
#endif
if (allow_empty_channel || !channel.empty()) {
annotations["channel"] = channel;
}
annotations["plat"] = std::string("Linux");
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 defined(OS_CHROMEOS)
arguments.push_back("--use-cros-crash-reporter");
#endif
bool result =
client.StartHandler(handler_path, database_path, metrics_path, url,
annotations, arguments, false, false);
DCHECK(result);
pthread_atfork(nullptr, nullptr, SetPtracerAtFork);
return database_path;
}
int fd = base::GlobalDescriptors::GetInstance()->Get(
service_manager::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);
pthread_atfork(nullptr, nullptr, SetPtracerAtFork);
return base::FilePath();
}
} // namespace internal
} // namespace crash_reporter