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>
diff --git a/client/crashpad_client.h b/client/crashpad_client.h
index 11fa66e..3070e2e 100644
--- a/client/crashpad_client.h
+++ b/client/crashpad_client.h
@@ -456,6 +456,24 @@
   //! \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.
   //!
diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc
index 630c24f..f805ff1 100644
--- a/client/crashpad_client_linux.cc
+++ b/client/crashpad_client_linux.cc
@@ -131,6 +131,8 @@
 
 #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 @@
     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 @@
     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 @@
   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 @@
   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;
diff --git a/client/crashpad_client_linux_test.cc b/client/crashpad_client_linux_test.cc
index 9b207db..8f4151e 100644
--- a/client/crashpad_client_linux_test.cc
+++ b/client/crashpad_client_linux_test.cc
@@ -71,11 +71,14 @@
   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 @@
 
 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 @@
     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 @@
 #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 @@
       *x;
       break;
     }
+
+    case CrashType::kFakeSegv: {
+      raise(SIGSEGV);
+      break;
+    }
   }
 }
 
@@ -403,6 +416,10 @@
     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 @@
         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 @@
     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 @@
                      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 {