mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-12 16:22:46 +00:00
Add SetLastChanceExceptionHandler to implement permissive MTE mode
SetLastChanceExceptionHandler sets a callback to be called after a crash has been reported. Returning true from this callback will not reraise the signal so the execution can continue. This will be used to implement permissive MTE mode, which will continue execution after a MTE crash. Bug: chromium:1467915 Change-Id: I93a28ceea921fe977805482cf47c07643ca6133c Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4707688 Reviewed-by: Robert Sesek <rsesek@chromium.org> Commit-Queue: Keishi Hattori <keishi@chromium.org>
This commit is contained in:
parent
ca6d64d0ae
commit
b1e66e322d
@ -456,6 +456,24 @@ class CrashpadClient {
|
||||
//! \param[in] handler The custom crash signal handler to install.
|
||||
static void SetFirstChanceExceptionHandler(FirstChanceHandler handler);
|
||||
|
||||
//! \brief Installs a custom crash signal handler which runs after the
|
||||
//! currently installed Crashpad handler.
|
||||
//!
|
||||
//! Handling signals appropriately can be tricky and use of this method
|
||||
//! should be avoided, if possible.
|
||||
//!
|
||||
//! A handler must have already been installed before calling this method.
|
||||
//!
|
||||
//! The custom handler runs in a signal handler context and must be safe for
|
||||
//! that purpose.
|
||||
//!
|
||||
//! If the custom handler returns `true`, the signal is not reraised.
|
||||
//!
|
||||
//! \param[in] handler The custom crash signal handler to install.
|
||||
static void SetLastChanceExceptionHandler(bool (*handler)(int,
|
||||
siginfo_t*,
|
||||
ucontext_t*));
|
||||
|
||||
//! \brief Configures a set of signals that shouldn't have Crashpad signal
|
||||
//! handlers installed.
|
||||
//!
|
||||
|
@ -131,6 +131,8 @@ std::vector<std::string> BuildArgsToLaunchWithLinker(
|
||||
|
||||
#endif // BUILDFLAG(IS_ANDROID)
|
||||
|
||||
using LastChanceHandler = bool (*)(int, siginfo_t*, ucontext_t*);
|
||||
|
||||
// A base class for Crashpad signal handler implementations.
|
||||
class SignalHandler {
|
||||
public:
|
||||
@ -154,6 +156,10 @@ class SignalHandler {
|
||||
first_chance_handler_ = handler;
|
||||
}
|
||||
|
||||
void SetLastChanceExceptionHandler(LastChanceHandler handler) {
|
||||
last_chance_handler_ = handler;
|
||||
}
|
||||
|
||||
// The base implementation for all signal handlers, suitable for calling
|
||||
// directly to simulate signal delivery.
|
||||
void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
|
||||
@ -212,6 +218,11 @@ class SignalHandler {
|
||||
if (!handler_->disabled_.test_and_set()) {
|
||||
handler_->HandleCrash(signo, siginfo, context);
|
||||
handler_->WakeThreads();
|
||||
if (handler_->last_chance_handler_ &&
|
||||
handler_->last_chance_handler_(
|
||||
signo, siginfo, static_cast<ucontext_t*>(context))) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Processes on Android normally have several chained signal handlers that
|
||||
// co-operate to report crashes. e.g. WebView will have this signal
|
||||
@ -254,6 +265,7 @@ class SignalHandler {
|
||||
Signals::OldActions old_actions_ = {};
|
||||
ExceptionInformation exception_information_ = {};
|
||||
CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
|
||||
LastChanceHandler last_chance_handler_ = nullptr;
|
||||
int32_t dump_done_futex_ = kDumpNotDone;
|
||||
#if !defined(__cpp_lib_atomic_value_initialization) || \
|
||||
__cpp_lib_atomic_value_initialization < 201911L
|
||||
@ -739,6 +751,12 @@ void CrashpadClient::SetFirstChanceExceptionHandler(
|
||||
SignalHandler::Get()->SetFirstChanceHandler(handler);
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashpadClient::SetLastChanceExceptionHandler(LastChanceHandler handler) {
|
||||
DCHECK(SignalHandler::Get());
|
||||
SignalHandler::Get()->SetLastChanceExceptionHandler(handler);
|
||||
}
|
||||
|
||||
void CrashpadClient::SetUnhandledSignals(const std::set<int>& signals) {
|
||||
DCHECK(!SignalHandler::Get());
|
||||
unhandled_signals_ = signals;
|
||||
|
@ -71,11 +71,14 @@ enum class CrashType : uint32_t {
|
||||
kBuiltinTrap,
|
||||
kInfiniteRecursion,
|
||||
kSegvWithTagBits,
|
||||
// kFakeSegv is meant to simulate a MTE segv error.
|
||||
kFakeSegv,
|
||||
};
|
||||
|
||||
struct StartHandlerForSelfTestOptions {
|
||||
bool start_handler_at_crash;
|
||||
bool set_first_chance_handler;
|
||||
bool set_last_chance_handler;
|
||||
bool crash_non_main_thread;
|
||||
bool client_uses_signals;
|
||||
bool gather_indirectly_referenced_memory;
|
||||
@ -84,7 +87,7 @@ struct StartHandlerForSelfTestOptions {
|
||||
|
||||
class StartHandlerForSelfTest
|
||||
: public testing::TestWithParam<
|
||||
std::tuple<bool, bool, bool, bool, bool, CrashType>> {
|
||||
std::tuple<bool, bool, bool, bool, bool, bool, CrashType>> {
|
||||
public:
|
||||
StartHandlerForSelfTest() = default;
|
||||
|
||||
@ -99,6 +102,7 @@ class StartHandlerForSelfTest
|
||||
memset(&options_, 0, sizeof(options_));
|
||||
std::tie(options_.start_handler_at_crash,
|
||||
options_.set_first_chance_handler,
|
||||
options_.set_last_chance_handler,
|
||||
options_.crash_non_main_thread,
|
||||
options_.client_uses_signals,
|
||||
options_.gather_indirectly_referenced_memory,
|
||||
@ -244,6 +248,10 @@ bool HandleCrashSuccessfully(int, siginfo_t*, ucontext_t*) {
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
bool HandleCrashSuccessfullyAfterReporting(int, siginfo_t*, ucontext_t*) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DoCrash(const StartHandlerForSelfTestOptions& options,
|
||||
CrashpadClient* client) {
|
||||
if (sigsetjmp(do_crash_sigjmp_env, 1) != 0) {
|
||||
@ -273,6 +281,11 @@ void DoCrash(const StartHandlerForSelfTestOptions& options,
|
||||
*x;
|
||||
break;
|
||||
}
|
||||
|
||||
case CrashType::kFakeSegv: {
|
||||
raise(SIGSEGV);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -403,6 +416,10 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) {
|
||||
client.SetFirstChanceExceptionHandler(HandleCrashSuccessfully);
|
||||
}
|
||||
|
||||
if (options.set_last_chance_handler) {
|
||||
client.SetLastChanceExceptionHandler(HandleCrashSuccessfullyAfterReporting);
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
if (android_set_abort_message) {
|
||||
android_set_abort_message(kTestAbortMessage);
|
||||
@ -440,6 +457,16 @@ class StartHandlerForSelfInChildTest : public MultiprocessExec {
|
||||
case CrashType::kSegvWithTagBits:
|
||||
SetExpectedChildTermination(TerminationReason::kTerminationSignal,
|
||||
SIGSEGV);
|
||||
break;
|
||||
case CrashType::kFakeSegv:
|
||||
if (!options.set_last_chance_handler) {
|
||||
SetExpectedChildTermination(TerminationReason::kTerminationSignal,
|
||||
SIGSEGV);
|
||||
} else {
|
||||
SetExpectedChildTermination(TerminationReason::kTerminationNormal,
|
||||
EXIT_SUCCESS);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -471,7 +498,11 @@ class StartHandlerForSelfInChildTest : public MultiprocessExec {
|
||||
writer.Close();
|
||||
|
||||
if (options_.client_uses_signals && !options_.set_first_chance_handler &&
|
||||
options_.crash_type != CrashType::kSimulated) {
|
||||
options_.crash_type != CrashType::kSimulated &&
|
||||
// The last chance handler will prevent the client handler from being
|
||||
// called if crash type is kFakeSegv.
|
||||
(!options_.set_last_chance_handler ||
|
||||
options_.crash_type != CrashType::kFakeSegv)) {
|
||||
// Wait for child's client signal handler.
|
||||
char c;
|
||||
EXPECT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c)));
|
||||
@ -549,10 +580,12 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
testing::Bool(),
|
||||
testing::Bool(),
|
||||
testing::Bool(),
|
||||
testing::Bool(),
|
||||
testing::Values(CrashType::kSimulated,
|
||||
CrashType::kBuiltinTrap,
|
||||
CrashType::kInfiniteRecursion,
|
||||
CrashType::kSegvWithTagBits)));
|
||||
CrashType::kSegvWithTagBits,
|
||||
CrashType::kFakeSegv)));
|
||||
|
||||
// Test state for starting the handler for another process.
|
||||
class StartHandlerForClientTest {
|
||||
|
Loading…
x
Reference in New Issue
Block a user