1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * This module is supposed to abstract signal handling away from the other
8 * platforms that do not support it.
11 #include "nsSigHandlers.h"
18 # include "prthread.h"
21 # include "nsString.h"
22 # include "nsXULAppAPI.h"
25 # include <sys/time.h>
26 # include <sys/resource.h>
28 # include <stdlib.h> // atoi
29 # include <sys/prctl.h>
30 # ifndef ANDROID // no Android impl
31 # include <ucontext.h>
36 # include <sys/resource.h>
37 # include <ucontext.h>
40 # ifdef MOZ_WIDGET_GTK
44 // Note: some tests manipulate this value.
45 unsigned int _gdb_sleep_duration
= 300;
47 # if defined(LINUX) && !defined(ANDROID) && defined(DEBUG) && \
48 (defined(__i386) || defined(__x86_64) || defined(PPC))
49 # define CRAWL_STACK_ON_SIGSEGV
52 # ifndef PR_SET_PTRACER
53 # define PR_SET_PTRACER 0x59616d61
55 # ifndef PR_SET_PTRACER_ANY
56 # define PR_SET_PTRACER_ANY ((unsigned long)-1)
59 # if defined(CRAWL_STACK_ON_SIGSEGV)
62 # include "nsISupportsUtils.h"
63 # include "mozilla/Attributes.h"
64 # include "mozilla/StackWalk.h"
66 static const char* gProgname
= "huh?";
68 // NB: keep me up to date with the same variable in
69 // ipc/chromium/chrome/common/ipc_channel_posix.cc
70 static const int kClientChannelFd
= 3;
74 static void PrintStackFrame(uint32_t aFrameNumber
, void* aPC
, void* aSP
,
77 MozCodeAddressDetails details
;
79 MozDescribeCodeAddress(aPC
, &details
);
80 MozFormatCodeAddressDetails(buf
, sizeof(buf
), aFrameNumber
, aPC
, &details
);
81 fprintf(stdout
, "%s\n", buf
);
86 void common_crap_handler(int signum
, const void* aFirstFramePC
) {
87 printf("\nProgram %s (pid = %d) received signal %d.\n", gProgname
, getpid(),
91 MozStackWalk(PrintStackFrame
, aFirstFramePC
, /* maxFrames */ 0, nullptr);
93 printf("Sleeping for %d seconds.\n", _gdb_sleep_duration
);
94 printf("Type 'gdb %s %d' to attach your debugger to this thread.\n",
97 // Allow us to be ptraced by gdb on Linux with Yama restrictions enabled.
98 prctl(PR_SET_PTRACER
, PR_SET_PTRACER_ANY
);
100 sleep(_gdb_sleep_duration
);
102 printf("Done sleeping...\n");
107 MOZ_NEVER_INLINE
void ah_crap_handler(int signum
) {
108 common_crap_handler(signum
, CallerPC());
111 MOZ_NEVER_INLINE
void child_ah_crap_handler(int signum
) {
112 if (!getenv("MOZ_DONT_UNBLOCK_PARENT_ON_CHILD_CRASH"))
113 close(kClientChannelFd
);
114 common_crap_handler(signum
, CallerPC());
117 # endif // CRAWL_STACK_ON_SIGSEGV
119 # ifdef MOZ_WIDGET_GTK
120 // Need this include for version test below.
124 # if defined(MOZ_WIDGET_GTK)
126 # if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 50
127 // These types are only available in glib 2.50+
129 G_LOG_WRITER_HANDLED
= 1,
130 G_LOG_WRITER_UNHANDLED
= 0,
132 typedef struct _GLogField GLogField
;
138 typedef GLogWriterOutput (*GLogWriterFunc
)(GLogLevelFlags log_level
,
139 const GLogField
* fields
,
140 gsize n_fields
, gpointer user_data
);
143 static GLogFunc orig_log_func
= nullptr;
146 static void glib_log_func(const gchar
* log_domain
, GLogLevelFlags log_level
,
147 const gchar
* message
, gpointer user_data
);
148 static GLogWriterOutput
glib_log_writer_func(GLogLevelFlags
, const GLogField
*,
152 // GDK sometimes avoids calling exit handlers, but we still want to know when we
153 // crash, see https://gitlab.gnome.org/GNOME/gtk/-/issues/4514 and bug 1743144.
154 static bool IsCrashyGtkMessage(const nsACString
& aMessage
) {
155 if (aMessage
.EqualsLiteral("Lost connection to Wayland compositor.")) {
156 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L210
159 if (StringBeginsWith(aMessage
, "Error flushing display: "_ns
)) {
160 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L68
163 if (StringBeginsWith(aMessage
, "Error reading events from display: "_ns
)) {
164 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L97
167 if (StringBeginsWith(aMessage
, "Error "_ns
) &&
168 StringEndsWith(aMessage
, " dispatching to Wayland display."_ns
)) {
169 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L205
175 static void HandleGLibMessage(GLogLevelFlags aLogLevel
,
176 const nsDependentCString
& aMessage
) {
177 if (MOZ_UNLIKELY(IsCrashyGtkMessage(aMessage
))) {
178 MOZ_CRASH_UNSAFE(strdup(aMessage
.get()));
182 (G_LOG_LEVEL_ERROR
| G_LOG_FLAG_FATAL
| G_LOG_FLAG_RECURSION
)) {
183 NS_DebugBreak(NS_DEBUG_ASSERTION
, aMessage
.get(), "glib assertion",
185 } else if (aLogLevel
& (G_LOG_LEVEL_CRITICAL
| G_LOG_LEVEL_WARNING
)) {
186 NS_DebugBreak(NS_DEBUG_WARNING
, aMessage
.get(), "glib warning", __FILE__
,
191 /* static */ void glib_log_func(const gchar
* log_domain
,
192 GLogLevelFlags log_level
, const gchar
* message
,
193 gpointer user_data
) {
194 HandleGLibMessage(log_level
, nsDependentCString(message
));
195 orig_log_func(log_domain
, log_level
, message
, nullptr);
198 GLogWriterOutput
glib_log_writer_func(GLogLevelFlags flags
,
199 const GLogField
* fields
, gsize n_fields
,
200 gpointer user_data
) {
201 static const GLogWriterFunc sLogWriterDefault
=
202 (GLogWriterFunc
)dlsym(RTLD_DEFAULT
, "g_log_writer_default");
203 for (gsize i
= 0; i
< n_fields
; ++i
) {
204 if (!strcmp(fields
[i
].key
, "MESSAGE") && fields
[i
].length
< 0) {
205 HandleGLibMessage(flags
,
206 nsDependentCString((const char*)fields
[i
].value
));
210 return sLogWriterDefault(flags
, fields
, n_fields
, user_data
);
216 static void fpehandler(int signum
, siginfo_t
* si
, void* context
) {
217 /* Integer divide by zero or integer overflow. */
218 /* Note: FPE_INTOVF is ignored on Intel, PowerPC and SPARC systems. */
219 if (si
->si_code
== FPE_INTDIV
|| si
->si_code
== FPE_INTOVF
) {
220 NS_DebugBreak(NS_DEBUG_ABORT
, "Divide by zero", nullptr, __FILE__
,
225 # if defined(__i386__) || defined(__amd64__)
226 ucontext_t
* uc
= (ucontext_t
*)context
;
228 _STRUCT_FP_CONTROL
* ctrl
= &uc
->uc_mcontext
->__fs
.__fpu_fcw
;
229 ctrl
->__invalid
= ctrl
->__denorm
= ctrl
->__zdiv
= ctrl
->__ovrfl
=
230 ctrl
->__undfl
= ctrl
->__precis
= 1;
232 _STRUCT_FP_STATUS
* status
= &uc
->uc_mcontext
->__fs
.__fpu_fsw
;
233 status
->__invalid
= status
->__denorm
= status
->__zdiv
= status
->__ovrfl
=
234 status
->__undfl
= status
->__precis
= status
->__stkflt
=
235 status
->__errsumm
= 0;
237 uint32_t* mxcsr
= &uc
->uc_mcontext
->__fs
.__fpu_mxcsr
;
238 *mxcsr
|= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
239 *mxcsr
&= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
242 # if defined(LINUX) && !defined(ANDROID)
244 # if defined(__i386__)
245 ucontext_t
* uc
= (ucontext_t
*)context
;
247 * It seems that we have no access to mxcsr on Linux. libc
248 * seems to be translating cw/sw to mxcsr.
250 unsigned long int* cw
= &uc
->uc_mcontext
.fpregs
->cw
;
251 *cw
|= FPU_EXCEPTION_MASK
;
253 unsigned long int* sw
= &uc
->uc_mcontext
.fpregs
->sw
;
254 *sw
&= ~FPU_STATUS_FLAGS
;
256 # if defined(__amd64__)
257 ucontext_t
* uc
= (ucontext_t
*)context
;
259 uint16_t* cw
= &uc
->uc_mcontext
.fpregs
->cwd
;
260 *cw
|= FPU_EXCEPTION_MASK
;
262 uint16_t* sw
= &uc
->uc_mcontext
.fpregs
->swd
;
263 *sw
&= ~FPU_STATUS_FLAGS
;
265 uint32_t* mxcsr
= &uc
->uc_mcontext
.fpregs
->mxcsr
;
266 *mxcsr
|= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
267 *mxcsr
&= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
271 ucontext_t
* uc
= (ucontext_t
*)context
;
274 uint32_t* cw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[0];
275 *cw
|= FPU_EXCEPTION_MASK
;
277 uint32_t* sw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[1];
278 *sw
&= ~FPU_STATUS_FLAGS
;
280 /* address of the instruction that caused the exception */
281 uint32_t* ip
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[3];
282 uc
->uc_mcontext
.gregs
[REG_PC
] = *ip
;
284 # if defined(__amd64__)
285 uint16_t* cw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.cw
;
286 *cw
|= FPU_EXCEPTION_MASK
;
288 uint16_t* sw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.sw
;
289 *sw
&= ~FPU_STATUS_FLAGS
;
291 uint32_t* mxcsr
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.mxcsr
;
292 *mxcsr
|= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
293 *mxcsr
&= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
299 void InstallSignalHandlers(const char* aProgname
) {
300 # if defined(CRAWL_STACK_ON_SIGSEGV)
302 const char* tmp
= strdup(aProgname
);
307 # endif // CRAWL_STACK_ON_SIGSEGV
309 const char* gdbSleep
= PR_GetEnv("MOZ_GDB_SLEEP");
310 if (gdbSleep
&& *gdbSleep
) {
312 if (1 == sscanf(gdbSleep
, "%u", &s
)) {
313 _gdb_sleep_duration
= s
;
317 # if defined(CRAWL_STACK_ON_SIGSEGV)
318 if (!getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) {
319 void (*crap_handler
)(int) = GeckoProcessType_Default
!= XRE_GetProcessType()
320 ? child_ah_crap_handler
322 signal(SIGSEGV
, crap_handler
);
323 signal(SIGILL
, crap_handler
);
324 signal(SIGABRT
, crap_handler
);
326 # endif // CRAWL_STACK_ON_SIGSEGV
329 /* Install a handler for floating point exceptions and disable them if they
331 struct sigaction sa
, osa
;
332 sa
.sa_flags
= SA_ONSTACK
| SA_RESTART
| SA_SIGINFO
;
333 sa
.sa_sigaction
= fpehandler
;
334 sigemptyset(&sa
.sa_mask
);
335 sigaction(SIGFPE
, &sa
, &osa
);
338 if (!XRE_IsParentProcess()) {
340 * If the user is debugging a Gecko parent process in gdb and hits ^C to
341 * suspend, a SIGINT signal will be sent to the child. We ignore this signal
342 * so the child isn't killed.
344 signal(SIGINT
, SIG_IGN
);
347 # if defined(DEBUG) && defined(LINUX)
348 const char* memLimit
= PR_GetEnv("MOZ_MEM_LIMIT");
349 if (memLimit
&& *memLimit
) {
350 long m
= atoi(memLimit
);
355 setrlimit(RLIMIT_AS
, &r
);
359 # ifdef MOZ_WIDGET_GTK
360 // Override the default glib logging function to intercept some crashes that
361 // are uninterceptable otherwise. Also, when XPCOM_DEBUG_BREAK is set, we can
362 // also get stacks for them, so we get stacks for it too.
364 // If we can hook via g_log_set_writer_func, then we don't need to hook via
365 // g_log_set_default_handler, because the GTK default handler uses structured
366 // logging and thus will end up in our log writer function anyways.
367 static const auto sSetLogWriter
=
368 (void (*)(GLogWriterFunc
, gpointer
, GDestroyNotify
))dlsym(
369 RTLD_DEFAULT
, "g_log_set_writer_func");
371 sSetLogWriter(glib_log_writer_func
, nullptr, nullptr);
373 orig_log_func
= g_log_set_default_handler(glib_log_func
, nullptr);
380 # include <windows.h>
384 * WinNT.h prior to SDK7 does not expose the structure of the ExtendedRegisters
385 * for ia86. We known that MxCsr is at offset 0x18 and is a DWORD.
387 # define MXCSR(ctx) (*(DWORD*)(((BYTE*)(ctx)->ExtendedRegisters) + 0x18))
391 # define MXCSR(ctx) (ctx)->MxCsr
394 # if defined(_M_IX86) || defined(_M_X64)
397 # define X87CW(ctx) (ctx)->FltSave.ControlWord
398 # define X87SW(ctx) (ctx)->FltSave.StatusWord
400 # define X87CW(ctx) (ctx)->FloatSave.ControlWord
401 # define X87SW(ctx) (ctx)->FloatSave.StatusWord
404 static LPTOP_LEVEL_EXCEPTION_FILTER gFPEPreviousFilter
;
406 LONG __stdcall
FpeHandler(PEXCEPTION_POINTERS pe
) {
407 PEXCEPTION_RECORD e
= (PEXCEPTION_RECORD
)pe
->ExceptionRecord
;
408 CONTEXT
* c
= (CONTEXT
*)pe
->ContextRecord
;
410 switch (e
->ExceptionCode
) {
411 case STATUS_FLOAT_DENORMAL_OPERAND
:
412 case STATUS_FLOAT_DIVIDE_BY_ZERO
:
413 case STATUS_FLOAT_INEXACT_RESULT
:
414 case STATUS_FLOAT_INVALID_OPERATION
:
415 case STATUS_FLOAT_OVERFLOW
:
416 case STATUS_FLOAT_STACK_CHECK
:
417 case STATUS_FLOAT_UNDERFLOW
:
418 case STATUS_FLOAT_MULTIPLE_FAULTS
:
419 case STATUS_FLOAT_MULTIPLE_TRAPS
:
420 X87CW(c
) |= FPU_EXCEPTION_MASK
; /* disable all FPU exceptions */
421 X87SW(c
) &= ~FPU_STATUS_FLAGS
; /* clear all pending FPU exceptions */
423 if (c
->ContextFlags
& CONTEXT_EXTENDED_REGISTERS
) {
425 MXCSR(c
) |= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
426 MXCSR(c
) &= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
430 return EXCEPTION_CONTINUE_EXECUTION
;
432 LONG action
= EXCEPTION_CONTINUE_SEARCH
;
433 if (gFPEPreviousFilter
) action
= gFPEPreviousFilter(pe
);
438 void InstallSignalHandlers(const char* aProgname
) {
439 gFPEPreviousFilter
= SetUnhandledExceptionFilter(FpeHandler
);
444 void InstallSignalHandlers(const char* aProgname
) {}
449 # error No signal handling implementation for this platform.