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 {