Send SIGABRT from the watchdog thread to the thread executing the inputs.

This improves error reporting, because the produced stack trace is more likely
to be related to the reason for exceeding a resource limit.

PiperOrigin-RevId: 725821779
diff --git a/centipede/runner.cc b/centipede/runner.cc
index 3c6481f..0faf988 100644
--- a/centipede/runner.cc
+++ b/centipede/runner.cc
@@ -23,6 +23,7 @@
 #include "./centipede/runner.h"
 
 #include <pthread.h>  // NOLINT: use pthread to avoid extra dependencies.
+#include <signal.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/time.h>
@@ -39,6 +40,7 @@
 #include <ctime>
 #include <functional>
 #include <memory>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -256,7 +258,17 @@
                 " (%s); exiting\n",
                 resource.what, resource.value, resource.limit, resource.units);
         WriteFailureDescription(resource.failure);
-        abort();
+        pthread_mutex_lock(&state.runner_main_thread_mu);
+        if (state.runner_main_thread.has_value()) {
+          fprintf(stderr, "Sending SIGABRT to the runner main thread.\n");
+          pthread_kill(*state.runner_main_thread, SIGABRT);
+          pthread_mutex_unlock(&state.runner_main_thread_mu);
+          return;
+        } else {
+          pthread_mutex_unlock(&state.runner_main_thread_mu);
+          fprintf(stderr, "Aborting the runner in the watchdog thread.\n");
+          std::abort();
+        }
       }
     }
   }
@@ -293,7 +305,7 @@
             tls.top_frame_sp - sp, stack_limit);
     centipede::WriteFailureDescription(
         centipede::kExecutionFailureStackLimitExceeded.data());
-    abort();
+    std::abort();
   }
 }
 
@@ -1109,6 +1121,17 @@
 //
 //  Note: argc/argv are used for only ReadOneInputExecuteItAndDumpCoverage().
 int RunnerMain(int argc, char **argv, RunnerCallbacks &callbacks) {
+  {
+    LockGuard lock(state.runner_main_thread_mu);
+    state.runner_main_thread = pthread_self();
+  }
+  struct OnReturn {
+    ~OnReturn() {
+      LockGuard lock(state.runner_main_thread_mu);
+      state.runner_main_thread = std::nullopt;
+    }
+  } on_return;
+
   state.centipede_runner_main_executed = true;
 
   fprintf(stderr, "Centipede fuzz target runner; argv[0]: %s flags: %s\n",
diff --git a/centipede/runner.h b/centipede/runner.h
index fb2c87f..3233b19 100644
--- a/centipede/runner.h
+++ b/centipede/runner.h
@@ -24,6 +24,7 @@
 #include <cstddef>
 #include <cstdint>
 #include <cstdlib>
+#include <optional>
 
 #include "absl/base/const_init.h"
 #include "absl/base/nullability.h"
@@ -213,7 +214,7 @@
     return strndup(value_beg, end - value_beg);
   }
 
-  pthread_mutex_t execution_result_override_mu;
+  pthread_mutex_t execution_result_override_mu = PTHREAD_MUTEX_INITIALIZER;
   // If not nullptr, it points to a batch result with either zero or one
   // execution. When an execution result present, it will be passed as the
   // execution result of the current test input. The object is owned and cleaned
@@ -225,7 +226,8 @@
   ThreadLocalRunnerState *tls_list;
   // Doubly linked list of detached TLSs.
   ThreadLocalRunnerState *detached_tls_list;
-  pthread_mutex_t tls_list_mu;  // Guards tls_list and detached_tls_list.
+  // Guards `tls_list` and `detached_tls_list`.
+  pthread_mutex_t tls_list_mu = PTHREAD_MUTEX_INITIALIZER;
   // Iterates all TLS objects under tls_list_mu, except those with `ignore` set.
   // Calls `callback()` on every TLS.
   template <typename Callback>
@@ -324,6 +326,11 @@
   // CentipedeRunnerMain() sets this to true.
   bool centipede_runner_main_executed = false;
 
+  // The thread that runs `RunnerMain()` and executes the inputs.
+  std::optional<pthread_t> runner_main_thread;
+  // Guards `runner_main_thread`.
+  pthread_mutex_t runner_main_thread_mu = PTHREAD_MUTEX_INITIALIZER;
+
   // Timeout-related machinery.
 
   // Starts the watchdog thread that terminates the runner if any of the