stacktrace_x86: Handle nested signals on altstack

PiperOrigin-RevId: 758147101
Change-Id: I569e095b4820862a7ebba68bd22ab205f43693e2
This commit is contained in:
Wiktor Garbacz
2025-05-13 03:48:50 -07:00
committed by Copybara-Service
parent 9c8e8f948b
commit d55845e6c1
2 changed files with 79 additions and 5 deletions

View File

@@ -18,6 +18,9 @@
#include <stdint.h>
#include <algorithm>
#include <cerrno>
#include <csignal>
#include <cstring>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -295,4 +298,74 @@ TEST(StackTrace, CanonicalFrameAddresses) {
}
#endif
// This test is Linux specific.
#if defined(__linux__)
const void* g_return_address = nullptr;
bool g_sigusr2_raised = false;
void SigUsr2Handler(int, siginfo_t*, void* uc) {
// Many platforms don't support this by default.
bool support_is_expected = false;
constexpr int kMaxStackDepth = 64;
void* result[kMaxStackDepth];
int depth =
absl::GetStackTraceWithContext(result, kMaxStackDepth, 0, uc, nullptr);
// Verify we can unwind past the nested signal handlers.
if (support_is_expected) {
EXPECT_THAT(absl::MakeSpan(result, static_cast<size_t>(depth)),
Contains(g_return_address).Times(1));
}
depth = absl::GetStackTrace(result, kMaxStackDepth, 0);
if (support_is_expected) {
EXPECT_THAT(absl::MakeSpan(result, static_cast<size_t>(depth)),
Contains(g_return_address).Times(1));
}
g_sigusr2_raised = true;
}
void SigUsr1Handler(int, siginfo_t*, void*) {
raise(SIGUSR2);
ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
}
ABSL_ATTRIBUTE_NOINLINE void RaiseSignal() {
g_return_address = __builtin_return_address(0);
raise(SIGUSR1);
ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
}
ABSL_ATTRIBUTE_NOINLINE void TestNestedSignal() {
constexpr size_t kAltstackSize = 1 << 14;
// Allocate altstack on regular stack to make sure it'll have a higher
// address than some of the regular stack frames.
char space[kAltstackSize];
stack_t altstack;
stack_t old_stack;
altstack.ss_sp = space;
altstack.ss_size = kAltstackSize;
altstack.ss_flags = 0;
ASSERT_EQ(sigaltstack(&altstack, &old_stack), 0) << strerror(errno);
struct sigaction act;
struct sigaction oldusr1act;
struct sigaction oldusr2act;
act.sa_sigaction = SigUsr1Handler;
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigemptyset(&act.sa_mask);
ASSERT_EQ(sigaction(SIGUSR1, &act, &oldusr1act), 0) << strerror(errno);
act.sa_sigaction = SigUsr2Handler;
ASSERT_EQ(sigaction(SIGUSR2, &act, &oldusr2act), 0) << strerror(errno);
RaiseSignal();
ASSERT_EQ(sigaltstack(&old_stack, nullptr), 0) << strerror(errno);
ASSERT_EQ(sigaction(SIGUSR1, &oldusr1act, nullptr), 0) << strerror(errno);
ASSERT_EQ(sigaction(SIGUSR2, &oldusr2act, nullptr), 0) << strerror(errno);
ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
}
TEST(StackTrace, NestedSignal) {
// Verify we can unwind past the nested signal handlers.
TestNestedSignal();
EXPECT_TRUE(g_sigusr2_raised);
}
#endif
} // namespace