[libcxx-commits] [libcxx] DRAFT [libc++][hardening] In production hardening modes, trap rather than abort (PR #78561)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jan 18 13:05:53 PST 2024


================
@@ -199,118 +360,86 @@ struct DeathTest {
 
     if (WIFEXITED(status_value)) {
       exit_code_ = WEXITSTATUS(status_value);
-      if (!IsValidResultKind(exit_code_))
-        return RK_Unknown;
-      return static_cast<ResultKind>(exit_code_);
+      return ConvertToDeathCause(exit_code_);
     }
-    return RK_Unknown;
-  }
 
-  DeathTest(DeathTest const&) = delete;
-  DeathTest& operator=(DeathTest const&) = delete;
+    if (WIFSIGNALED(status_value)) {
+      exit_code_ = WTERMSIG(status_value);
+      if (exit_code_ == SIGILL || exit_code_ == SIGTRAP) {
+        return DeathCause::Trap;
+      }
+    }
 
-  int GetStdOutReadFD() const {
-    return stdout_pipe_fd_[0];
+    return DeathCause::Unknown;
   }
 
-  int GetStdOutWriteFD() const {
-    return stdout_pipe_fd_[1];
-  }
+  int GetStdOutReadFD() const { return stdout_pipe_fd_[0]; }
 
-  int GetStdErrReadFD() const {
-    return stderr_pipe_fd_[0];
-  }
+  int GetStdOutWriteFD() const { return stdout_pipe_fd_[1]; }
+
+  int GetStdErrReadFD() const { return stderr_pipe_fd_[0]; }
+
+  int GetStdErrWriteFD() const { return stderr_pipe_fd_[1]; }
 
-  int GetStdErrWriteFD() const {
-    return stderr_pipe_fd_[1];
-  }
-private:
-  AssertionInfoMatcher matcher_;
   pid_t child_pid_ = -1;
-  int exit_code_ = -1;
+  int exit_code_   = -1;
   int stdout_pipe_fd_[2];
   int stderr_pipe_fd_[2];
   std::string stdout_from_child_;
   std::string stderr_from_child_;
 };
 
 #ifdef _LIBCPP_VERSION
-void std::__libcpp_verbose_abort(char const* printf_format, ...) {
-  // Extract information from the error message. This has to stay synchronized with how we format assertions in the
-  // library.
+void std::__libcpp_verbose_abort(char const* format, ...) {
   va_list args;
-  va_start(args, printf_format);
-  char const* message = va_arg(args, char const*);
+  va_start(args, format);
 
-  std::regex message_format("(.*):(\\d+): assertion (.*) failed: (.*)\\n");
+  std::fprintf(stderr, "%s\n", AssertionInfoMatcher::Marker);
+  std::vfprintf(stderr, format, args);
+  std::fprintf(stderr, "%s", AssertionInfoMatcher::Marker);
 
-  std::cmatch match_result;
-  bool has_match = std::regex_match(message, match_result, message_format);
-  assert(has_match);
-  assert(match_result.size() == 5);
+  va_end(args);
 
-  std::string file = match_result[1];
-  int line         = std::stoi(match_result[2]);
-  // Omitting `expression` in `match_result[3]`
-  std::string failure_reason = match_result[4];
-
-  if (GlobalMatcher().Matches(file.c_str(), line, failure_reason.c_str())) {
-    std::exit(DeathTest::RK_MatchFound);
-  }
-  std::exit(DeathTest::RK_MatchFailure);
+  StopChildProcess(DeathCause::VerboseAbort);
 }
 #endif // _LIBCPP_VERSION
 
-[[noreturn]] inline void terminate_handler() {
-  std::exit(DeathTest::RK_Terminate);
-}
-
 template <class Func>
-inline bool ExpectDeath(const char* stmt, Func&& func, AssertionInfoMatcher Matcher) {
-  std::set_terminate(terminate_handler);
-  DeathTest DT(Matcher);
-  DeathTest::ResultKind RK = DT.Run(func);
-  auto OnFailure = [&](const char* msg) {
-    std::fprintf(stderr, "EXPECT_DEATH( %s ) failed! (%s)\n\n", stmt, msg);
-    if (RK != DeathTest::RK_Unknown) {
-      std::fprintf(stderr, "child exit code: %d\n", DT.getChildExitCode());
-    }
-    if (!DT.getChildStdErr().empty()) {
-      std::fprintf(stderr, "---------- standard err ----------\n%s\n", DT.getChildStdErr().c_str());
-    }
-    if (!DT.getChildStdOut().empty()) {
-      std::fprintf(stderr, "---------- standard out ----------\n%s\n", DT.getChildStdOut().c_str());
-    }
-    return false;
-  };
-  switch (RK) {
-  case DeathTest::RK_MatchFound:
-  case DeathTest::RK_Terminate:
-    return true;
-  case DeathTest::RK_SetupFailure:
-    return OnFailure("child failed to setup test environment");
-  case DeathTest::RK_Unknown:
-      return OnFailure("reason unknown");
-  case DeathTest::RK_DidNotDie:
-      return OnFailure("child did not die");
-  case DeathTest::RK_MatchFailure:
-      return OnFailure("matcher failed");
+inline bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func, AssertionInfoMatcher matcher) {
+  assert(IsValidCause(expected_cause));
+
+  DeathTest test_case;
+  DeathTestResult test_result = test_case.Run(expected_cause, func, matcher);
+  if (!test_result.success()) {
+    test_case.PrintFailureDetails(test_result.failure_description(), stmt, test_result.cause());
   }
-  assert(false && "unreachable");
+
+  return test_result.success();
 }
 
 template <class Func>
-inline bool ExpectDeath(const char* stmt, Func&& func) {
-  return ExpectDeath(stmt, func, AnyMatcher);
+inline bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
----------------
ldionne wrote:

```suggestion
bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
```

While we're at it?

https://github.com/llvm/llvm-project/pull/78561


More information about the libcxx-commits mailing list