1//
2// Copyright 2018 The Abseil Authors.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// https://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "absl/debugging/failure_signal_handler.h"
18
19#include "absl/base/config.h"
20
21#ifdef _WIN32
22#include <windows.h>
23#else
24#include <unistd.h>
25#endif
26
27#ifdef ABSL_HAVE_MMAP
28#include <sys/mman.h>
29#endif
30
31#include <algorithm>
32#include <atomic>
33#include <cerrno>
34#include <csignal>
35#include <cstdio>
36#include <cstring>
37#include <ctime>
38
39#include "absl/base/attributes.h"
40#include "absl/base/internal/raw_logging.h"
41#include "absl/base/internal/sysinfo.h"
42#include "absl/debugging/internal/examine_stack.h"
43#include "absl/debugging/stacktrace.h"
44
45#ifndef _WIN32
46#define ABSL_HAVE_SIGACTION
47#endif
48
49namespace absl {
50
51ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options;
52
53// Resets the signal handler for signo to the default action for that
54// signal, then raises the signal.
55static void RaiseToDefaultHandler(int signo) {
56 signal(signo, SIG_DFL);
57 raise(signo);
58}
59
60struct FailureSignalData {
61 const int signo;
62 const char* const as_string;
63#ifdef ABSL_HAVE_SIGACTION
64 struct sigaction previous_action;
65 // StructSigaction is used to silence -Wmissing-field-initializers.
66 using StructSigaction = struct sigaction;
67 #define FSD_PREVIOUS_INIT FailureSignalData::StructSigaction()
68#else
69 void (*previous_handler)(int);
70 #define FSD_PREVIOUS_INIT SIG_DFL
71#endif
72};
73
74ABSL_CONST_INIT static FailureSignalData failure_signal_data[] = {
75 {SIGSEGV, "SIGSEGV", FSD_PREVIOUS_INIT},
76 {SIGILL, "SIGILL", FSD_PREVIOUS_INIT},
77 {SIGFPE, "SIGFPE", FSD_PREVIOUS_INIT},
78 {SIGABRT, "SIGABRT", FSD_PREVIOUS_INIT},
79 {SIGTERM, "SIGTERM", FSD_PREVIOUS_INIT},
80#ifndef _WIN32
81 {SIGBUS, "SIGBUS", FSD_PREVIOUS_INIT},
82 {SIGTRAP, "SIGTRAP", FSD_PREVIOUS_INIT},
83#endif
84};
85
86#undef FSD_PREVIOUS_INIT
87
88static void RaiseToPreviousHandler(int signo) {
89 // Search for the previous handler.
90 for (const auto& it : failure_signal_data) {
91 if (it.signo == signo) {
92#ifdef ABSL_HAVE_SIGACTION
93 sigaction(signo, &it.previous_action, nullptr);
94#else
95 signal(signo, it.previous_handler);
96#endif
97 raise(signo);
98 return;
99 }
100 }
101
102 // Not found, use the default handler.
103 RaiseToDefaultHandler(signo);
104}
105
106namespace debugging_internal {
107
108const char* FailureSignalToString(int signo) {
109 for (const auto& it : failure_signal_data) {
110 if (it.signo == signo) {
111 return it.as_string;
112 }
113 }
114 return "";
115}
116
117} // namespace debugging_internal
118
119#ifndef _WIN32
120
121static bool SetupAlternateStackOnce() {
122#if defined(__wasm__) || defined (__asjms__)
123 const size_t page_mask = getpagesize() - 1;
124#else
125 const size_t page_mask = sysconf(_SC_PAGESIZE) - 1;
126#endif
127 size_t stack_size = (std::max(SIGSTKSZ, 65536) + page_mask) & ~page_mask;
128#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
129 defined(THREAD_SANITIZER)
130 // Account for sanitizer instrumentation requiring additional stack space.
131 stack_size *= 5;
132#endif
133
134 stack_t sigstk;
135 memset(&sigstk, 0, sizeof(sigstk));
136 sigstk.ss_size = stack_size;
137
138#ifdef ABSL_HAVE_MMAP
139#ifndef MAP_STACK
140#define MAP_STACK 0
141#endif
142#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
143#define MAP_ANONYMOUS MAP_ANON
144#endif
145 sigstk.ss_sp = mmap(nullptr, sigstk.ss_size, PROT_READ | PROT_WRITE,
146 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
147 if (sigstk.ss_sp == MAP_FAILED) {
148 ABSL_RAW_LOG(FATAL, "mmap() for alternate signal stack failed");
149 }
150#else
151 sigstk.ss_sp = malloc(sigstk.ss_size);
152 if (sigstk.ss_sp == nullptr) {
153 ABSL_RAW_LOG(FATAL, "malloc() for alternate signal stack failed");
154 }
155#endif
156
157 if (sigaltstack(&sigstk, nullptr) != 0) {
158 ABSL_RAW_LOG(FATAL, "sigaltstack() failed with errno=%d", errno);
159 }
160 return true;
161}
162
163#endif
164
165#ifdef ABSL_HAVE_SIGACTION
166
167// Sets up an alternate stack for signal handlers once.
168// Returns the appropriate flag for sig_action.sa_flags
169// if the system supports using an alternate stack.
170static int MaybeSetupAlternateStack() {
171#ifndef _WIN32
172 ABSL_ATTRIBUTE_UNUSED static const bool kOnce = SetupAlternateStackOnce();
173 return SA_ONSTACK;
174#else
175 return 0;
176#endif
177}
178
179static void InstallOneFailureHandler(FailureSignalData* data,
180 void (*handler)(int, siginfo_t*, void*)) {
181 struct sigaction act;
182 memset(&act, 0, sizeof(act));
183 sigemptyset(&act.sa_mask);
184 act.sa_flags |= SA_SIGINFO;
185 // SA_NODEFER is required to handle SIGABRT from
186 // ImmediateAbortSignalHandler().
187 act.sa_flags |= SA_NODEFER;
188 if (fsh_options.use_alternate_stack) {
189 act.sa_flags |= MaybeSetupAlternateStack();
190 }
191 act.sa_sigaction = handler;
192 ABSL_RAW_CHECK(sigaction(data->signo, &act, &data->previous_action) == 0,
193 "sigaction() failed");
194}
195
196#else
197
198static void InstallOneFailureHandler(FailureSignalData* data,
199 void (*handler)(int)) {
200 data->previous_handler = signal(data->signo, handler);
201 ABSL_RAW_CHECK(data->previous_handler != SIG_ERR, "signal() failed");
202}
203
204#endif
205
206static void WriteToStderr(const char* data) {
207 int old_errno = errno;
208 absl::raw_logging_internal::SafeWriteToStderr(data, strlen(data));
209 errno = old_errno;
210}
211
212static void WriteSignalMessage(int signo, void (*writerfn)(const char*)) {
213 char buf[64];
214 const char* const signal_string =
215 debugging_internal::FailureSignalToString(signo);
216 if (signal_string != nullptr && signal_string[0] != '\0') {
217 snprintf(buf, sizeof(buf), "*** %s received at time=%ld ***\n",
218 signal_string,
219 static_cast<long>(time(nullptr))); // NOLINT(runtime/int)
220 } else {
221 snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld ***\n",
222 signo, static_cast<long>(time(nullptr))); // NOLINT(runtime/int)
223 }
224 writerfn(buf);
225}
226
227// `void*` might not be big enough to store `void(*)(const char*)`.
228struct WriterFnStruct {
229 void (*writerfn)(const char*);
230};
231
232// Many of the absl::debugging_internal::Dump* functions in
233// examine_stack.h take a writer function pointer that has a void* arg
234// for historical reasons. failure_signal_handler_writer only takes a
235// data pointer. This function converts between these types.
236static void WriterFnWrapper(const char* data, void* arg) {
237 static_cast<WriterFnStruct*>(arg)->writerfn(data);
238}
239
240// Convenient wrapper around DumpPCAndFrameSizesAndStackTrace() for signal
241// handlers. "noinline" so that GetStackFrames() skips the top-most stack
242// frame for this function.
243ABSL_ATTRIBUTE_NOINLINE static void WriteStackTrace(
244 void* ucontext, bool symbolize_stacktrace,
245 void (*writerfn)(const char*, void*), void* writerfn_arg) {
246 constexpr int kNumStackFrames = 32;
247 void* stack[kNumStackFrames];
248 int frame_sizes[kNumStackFrames];
249 int min_dropped_frames;
250 int depth = absl::GetStackFramesWithContext(
251 stack, frame_sizes, kNumStackFrames,
252 1, // Do not include this function in stack trace.
253 ucontext, &min_dropped_frames);
254 absl::debugging_internal::DumpPCAndFrameSizesAndStackTrace(
255 absl::debugging_internal::GetProgramCounter(ucontext), stack, frame_sizes,
256 depth, min_dropped_frames, symbolize_stacktrace, writerfn, writerfn_arg);
257}
258
259// Called by AbslFailureSignalHandler() to write the failure info. It is
260// called once with writerfn set to WriteToStderr() and then possibly
261// with writerfn set to the user provided function.
262static void WriteFailureInfo(int signo, void* ucontext,
263 void (*writerfn)(const char*)) {
264 WriterFnStruct writerfn_struct{writerfn};
265 WriteSignalMessage(signo, writerfn);
266 WriteStackTrace(ucontext, fsh_options.symbolize_stacktrace, WriterFnWrapper,
267 &writerfn_struct);
268}
269
270// absl::SleepFor() can't be used here since AbslInternalSleepFor()
271// may be overridden to do something that isn't async-signal-safe on
272// some platforms.
273static void PortableSleepForSeconds(int seconds) {
274#ifdef _WIN32
275 Sleep(seconds * 1000);
276#else
277 struct timespec sleep_time;
278 sleep_time.tv_sec = seconds;
279 sleep_time.tv_nsec = 0;
280 while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) {}
281#endif
282}
283
284#ifdef ABSL_HAVE_ALARM
285// AbslFailureSignalHandler() installs this as a signal handler for
286// SIGALRM, then sets an alarm to be delivered to the program after a
287// set amount of time. If AbslFailureSignalHandler() hangs for more than
288// the alarm timeout, ImmediateAbortSignalHandler() will abort the
289// program.
290static void ImmediateAbortSignalHandler(int) {
291 RaiseToDefaultHandler(SIGABRT);
292}
293#endif
294
295// absl::base_internal::GetTID() returns pid_t on most platforms, but
296// returns absl::base_internal::pid_t on Windows.
297using GetTidType = decltype(absl::base_internal::GetTID());
298ABSL_CONST_INIT static std::atomic<GetTidType> failed_tid(0);
299
300#ifndef ABSL_HAVE_SIGACTION
301static void AbslFailureSignalHandler(int signo) {
302 void* ucontext = nullptr;
303#else
304static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) {
305#endif
306
307 const GetTidType this_tid = absl::base_internal::GetTID();
308 GetTidType previous_failed_tid = 0;
309 if (!failed_tid.compare_exchange_strong(
310 previous_failed_tid, static_cast<intptr_t>(this_tid),
311 std::memory_order_acq_rel, std::memory_order_relaxed)) {
312 ABSL_RAW_LOG(
313 ERROR,
314 "Signal %d raised at PC=%p while already in AbslFailureSignalHandler()",
315 signo, absl::debugging_internal::GetProgramCounter(ucontext));
316 if (this_tid != previous_failed_tid) {
317 // Another thread is already in AbslFailureSignalHandler(), so wait
318 // a bit for it to finish. If the other thread doesn't kill us,
319 // we do so after sleeping.
320 PortableSleepForSeconds(3);
321 RaiseToDefaultHandler(signo);
322 // The recursively raised signal may be blocked until we return.
323 return;
324 }
325 }
326
327#ifdef ABSL_HAVE_ALARM
328 // Set an alarm to abort the program in case this code hangs or deadlocks.
329 if (fsh_options.alarm_on_failure_secs > 0) {
330 alarm(0); // Cancel any existing alarms.
331 signal(SIGALRM, ImmediateAbortSignalHandler);
332 alarm(fsh_options.alarm_on_failure_secs);
333 }
334#endif
335
336 // First write to stderr.
337 WriteFailureInfo(signo, ucontext, WriteToStderr);
338
339 // Riskier code (because it is less likely to be async-signal-safe)
340 // goes after this point.
341 if (fsh_options.writerfn != nullptr) {
342 WriteFailureInfo(signo, ucontext, fsh_options.writerfn);
343 }
344
345 if (fsh_options.call_previous_handler) {
346 RaiseToPreviousHandler(signo);
347 } else {
348 RaiseToDefaultHandler(signo);
349 }
350}
351
352void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) {
353 fsh_options = options;
354 for (auto& it : failure_signal_data) {
355 InstallOneFailureHandler(&it, AbslFailureSignalHandler);
356 }
357}
358
359} // namespace absl
360