Skip to content

Commit

Permalink
Add SetLastChanceExceptionHandler to implement permissive MTE mode
Browse files Browse the repository at this point in the history
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 <[email protected]>
Commit-Queue: Keishi Hattori <[email protected]>
  • Loading branch information
Keishi Hattori authored and Crashpad LUCI CQ committed Aug 3, 2023
1 parent ca6d64d commit b1e66e3
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 3 deletions.
18 changes: 18 additions & 0 deletions client/crashpad_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//!
Expand Down
18 changes: 18 additions & 0 deletions client/crashpad_client_linux.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
39 changes: 36 additions & 3 deletions client/crashpad_client_linux_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -273,6 +281,11 @@ void DoCrash(const StartHandlerForSelfTestOptions& options,
*x;
break;
}

case CrashType::kFakeSegv: {
raise(SIGSEGV);
break;
}
}
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}
}
Expand Down Expand Up @@ -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)));
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit b1e66e3

Please sign in to comment.