Fix StackTraceTest.AsyncSignalUnsafeSignalHandlerHang.

Since AsyncSignalUnsafeSignalHandlerHang depends on tcmalloc
debugallocation, the test does not work correctly now.
The test checks whether StackDumpSignalHandler() is malloc-free
or not. So modify the test to use allocator shim to see whether
StackDumpSignalHandler() uses malloc() or not.

Bug: 1454536

Change-Id: I2725160696f94c09a5ee932ac9ea7e93f645006b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4940954
Reviewed-by: Daniel Cheng <[email protected]>
Commit-Queue: Daniel Cheng <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1211048}
diff --git a/base/debug/stack_trace_unittest.cc b/base/debug/stack_trace_unittest.cc
index 0f3e9d76..0662aa85 100644
--- a/base/debug/stack_trace_unittest.cc
+++ b/base/debug/stack_trace_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "base/debug/debugging_buildflags.h"
 #include "base/debug/stack_trace.h"
+#include "base/immediate_crash.h"
 #include "base/logging.h"
 #include "base/process/kill.h"
 #include "base/process/process_handle.h"
@@ -20,6 +21,12 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
 
+#include "base/allocator/buildflags.h"
+#include "base/allocator/partition_allocator/src/partition_alloc/partition_alloc.h"
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
+#include "base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim.h"
+#endif
+
 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 #include "base/test/multiprocess_test.h"
 #endif
@@ -32,6 +39,7 @@
 #else
 typedef testing::Test StackTraceTest;
 #endif
+typedef testing::Test StackTraceDeathTest;
 
 #if !defined(__UCLIBC__) && !defined(_AIX)
 // StackTrace::OutputToStream() is not implemented under uclibc, nor AIX.
@@ -153,31 +161,91 @@
 #endif  // !defined(__UCLIBC__) && !defined(_AIX)
 
 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
-#if !BUILDFLAG(IS_IOS)
-static char* newArray() {
-  // Clang warns about the mismatched new[]/delete if they occur in the same
-  // function.
-  return new char[10];
+// Since Mac's base::debug::StackTrace().Print() is not malloc-free, skip
+// StackDumpSignalHandlerIsMallocFree if BUILDFLAG(IS_MAC).
+#if BUILDFLAG(USE_ALLOCATOR_SHIM) && !BUILDFLAG(IS_MAC)
+
+namespace {
+
+// ImmediateCrash if a signal handler incorrectly uses malloc().
+// In an actual implementation, this could cause infinite recursion into the
+// signal handler or other problems. Because malloc() is not guaranteed to be
+// async signal safe.
+void* BadMalloc(const allocator_shim::AllocatorDispatch*, size_t, void*) {
+  base::ImmediateCrash();
 }
 
-MULTIPROCESS_TEST_MAIN(MismatchedMallocChildProcess) {
-  char* pointer = newArray();
-  delete pointer;
-  return 2;
+void* BadCalloc(const allocator_shim::AllocatorDispatch*,
+                size_t,
+                size_t,
+                void* context) {
+  base::ImmediateCrash();
 }
 
-// Regression test for StackDumpingSignalHandler async-signal unsafety.
-// Combined with tcmalloc's debugallocation, that signal handler
-// and e.g. mismatched new[]/delete would cause a hang because
-// of re-entering malloc.
-TEST_F(StackTraceTest, AsyncSignalUnsafeSignalHandlerHang) {
-  Process child = SpawnChild("MismatchedMallocChildProcess");
-  ASSERT_TRUE(child.IsValid());
-  int exit_code;
-  ASSERT_TRUE(
-      child.WaitForExitWithTimeout(TestTimeouts::action_timeout(), &exit_code));
+void* BadAlignedAlloc(const allocator_shim::AllocatorDispatch*,
+                      size_t,
+                      size_t,
+                      void*) {
+  base::ImmediateCrash();
 }
-#endif  // !BUILDFLAG(IS_IOS)
+
+void* BadAlignedRealloc(const allocator_shim::AllocatorDispatch*,
+                        void*,
+                        size_t,
+                        size_t,
+                        void*) {
+  base::ImmediateCrash();
+}
+
+void* BadRealloc(const allocator_shim::AllocatorDispatch*,
+                 void*,
+                 size_t,
+                 void*) {
+  base::ImmediateCrash();
+}
+
+void BadFree(const allocator_shim::AllocatorDispatch*, void*, void*) {
+  base::ImmediateCrash();
+}
+
+allocator_shim::AllocatorDispatch g_bad_malloc_dispatch = {
+    &BadMalloc,         /* alloc_function */
+    &BadMalloc,         /* alloc_unchecked_function */
+    &BadCalloc,         /* alloc_zero_initialized_function */
+    &BadAlignedAlloc,   /* alloc_aligned_function */
+    &BadRealloc,        /* realloc_function */
+    &BadFree,           /* free_function */
+    nullptr,            /* get_size_estimate_function */
+    nullptr,            /* claimed_address_function */
+    nullptr,            /* batch_malloc_function */
+    nullptr,            /* batch_free_function */
+    nullptr,            /* free_definite_size_function */
+    nullptr,            /* try_free_default_function */
+    &BadAlignedAlloc,   /* aligned_malloc_function */
+    &BadAlignedRealloc, /* aligned_realloc_function */
+    &BadFree,           /* aligned_free_function */
+    nullptr,            /* next */
+};
+
+}  // namespace
+
+// Regression test for StackDumpSignalHandler async-signal unsafety.
+// Since malloc() is not guaranteed to be async signal safe, it is not allowed
+// to use malloc() inside StackDumpSignalHandler().
+TEST_F(StackTraceDeathTest, StackDumpSignalHandlerIsMallocFree) {
+  EXPECT_DEATH_IF_SUPPORTED(
+      [] {
+        // On Android, base::debug::EnableInProcessStackDumping() does not
+        // change any actions taken by signals to be StackDumpSignalHandler. So
+        // the StackDumpSignalHandlerIsMallocFree test doesn't work on Android.
+        EnableInProcessStackDumping();
+        allocator_shim::InsertAllocatorDispatch(&g_bad_malloc_dispatch);
+        // Raise SIGSEGV to invoke StackDumpSignalHandler().
+        kill(getpid(), SIGSEGV);
+      }(),
+      "\\[end of stack trace\\]\n");
+}
+#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM)
 
 namespace {