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:
Keishi Hattori 2023-08-02 19:56:12 +00:00 committed by Crashpad LUCI CQ
parent ca6d64d0ae
commit b1e66e322d
3 changed files with 72 additions and 3 deletions

View File

@ -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.
//!

View File

@ -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;

View File

@ -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 {