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
42 # include "WidgetUtilsGtk.h"
46 # include "wayland-proxy.h"
49 // Note: some tests manipulate this value.
50 unsigned int _gdb_sleep_duration
= 300;
52 # if defined(LINUX) && !defined(ANDROID) && defined(DEBUG) && \
53 (defined(__i386) || defined(__x86_64) || defined(PPC))
54 # define CRAWL_STACK_ON_SIGSEGV
57 # ifndef PR_SET_PTRACER
58 # define PR_SET_PTRACER 0x59616d61
60 # ifndef PR_SET_PTRACER_ANY
61 # define PR_SET_PTRACER_ANY ((unsigned long)-1)
64 # if defined(CRAWL_STACK_ON_SIGSEGV)
67 # include "nsISupportsUtils.h"
68 # include "mozilla/Attributes.h"
69 # include "mozilla/StackWalk.h"
71 static const char* gProgname
= "huh?";
73 // NB: keep me up to date with the same variable in
74 // ipc/chromium/chrome/common/ipc_channel_posix.cc
75 static const int kClientChannelFd
= 3;
79 static void PrintStackFrame(uint32_t aFrameNumber
, void* aPC
, void* aSP
,
82 MozCodeAddressDetails details
;
84 MozDescribeCodeAddress(aPC
, &details
);
85 MozFormatCodeAddressDetails(buf
, sizeof(buf
), aFrameNumber
, aPC
, &details
);
86 fprintf(stdout
, "%s\n", buf
);
91 void common_crap_handler(int signum
, const void* aFirstFramePC
) {
92 printf("\nProgram %s (pid = %d) received signal %d.\n", gProgname
, getpid(),
96 MozStackWalk(PrintStackFrame
, aFirstFramePC
, /* maxFrames */ 0, nullptr);
98 printf("Sleeping for %d seconds.\n", _gdb_sleep_duration
);
99 printf("Type 'gdb %s %d' to attach your debugger to this thread.\n",
100 gProgname
, getpid());
102 // Allow us to be ptraced by gdb on Linux with Yama restrictions enabled.
103 prctl(PR_SET_PTRACER
, PR_SET_PTRACER_ANY
);
105 sleep(_gdb_sleep_duration
);
107 printf("Done sleeping...\n");
112 MOZ_NEVER_INLINE
void ah_crap_handler(int signum
) {
113 common_crap_handler(signum
, CallerPC());
116 MOZ_NEVER_INLINE
void child_ah_crap_handler(int signum
) {
117 if (!getenv("MOZ_DONT_UNBLOCK_PARENT_ON_CHILD_CRASH"))
118 close(kClientChannelFd
);
119 common_crap_handler(signum
, CallerPC());
122 # endif // CRAWL_STACK_ON_SIGSEGV
124 # ifdef MOZ_WIDGET_GTK
125 // Need this include for version test below.
129 # if defined(MOZ_WIDGET_GTK)
131 # if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 50
132 // These types are only available in glib 2.50+
134 G_LOG_WRITER_HANDLED
= 1,
135 G_LOG_WRITER_UNHANDLED
= 0,
137 typedef struct _GLogField GLogField
;
143 typedef GLogWriterOutput (*GLogWriterFunc
)(GLogLevelFlags log_level
,
144 const GLogField
* fields
,
145 gsize n_fields
, gpointer user_data
);
148 static GLogFunc orig_log_func
= nullptr;
151 static void glib_log_func(const gchar
* log_domain
, GLogLevelFlags log_level
,
152 const gchar
* message
, gpointer user_data
);
153 static GLogWriterOutput
glib_log_writer_func(GLogLevelFlags
, const GLogField
*,
157 // GDK sometimes avoids calling exit handlers, but we still want to know when we
158 // crash, see https://gitlab.gnome.org/GNOME/gtk/-/issues/4514 and bug 1743144.
159 static bool IsCrashyGtkMessage(const nsACString
& aMessage
) {
160 if (aMessage
.EqualsLiteral("Lost connection to Wayland compositor.")) {
161 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L210
164 if (StringBeginsWith(aMessage
, "Error flushing display: "_ns
)) {
165 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L68
168 if (StringBeginsWith(aMessage
, "Error reading events from display: "_ns
)) {
169 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L97
172 if (StringBeginsWith(aMessage
, "Error "_ns
) &&
173 StringEndsWith(aMessage
, " dispatching to Wayland display."_ns
)) {
174 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L205
180 static void HandleGLibMessage(GLogLevelFlags aLogLevel
,
181 const nsDependentCString
& aMessage
) {
182 if (MOZ_UNLIKELY(IsCrashyGtkMessage(aMessage
))) {
184 MOZ_CRASH_UNSAFE_PRINTF(
186 mozilla::widget::GetDesktopEnvironmentIdentifier().get(),
187 strdup(aMessage
.get()), WaylandProxy::GetState());
189 MOZ_CRASH_UNSAFE_PRINTF(
190 "(%s) %s", mozilla::widget::GetDesktopEnvironmentIdentifier().get(),
191 strdup(aMessage
.get()));
196 (G_LOG_LEVEL_ERROR
| G_LOG_FLAG_FATAL
| G_LOG_FLAG_RECURSION
)) {
197 NS_DebugBreak(NS_DEBUG_ASSERTION
, aMessage
.get(), "glib assertion",
199 } else if (aLogLevel
& (G_LOG_LEVEL_CRITICAL
| G_LOG_LEVEL_WARNING
)) {
200 NS_DebugBreak(NS_DEBUG_WARNING
, aMessage
.get(), "glib warning", __FILE__
,
205 /* static */ void glib_log_func(const gchar
* log_domain
,
206 GLogLevelFlags log_level
, const gchar
* message
,
207 gpointer user_data
) {
208 HandleGLibMessage(log_level
, nsDependentCString(message
));
209 orig_log_func(log_domain
, log_level
, message
, nullptr);
212 GLogWriterOutput
glib_log_writer_func(GLogLevelFlags flags
,
213 const GLogField
* fields
, gsize n_fields
,
214 gpointer user_data
) {
215 static const GLogWriterFunc sLogWriterDefault
=
216 (GLogWriterFunc
)dlsym(RTLD_DEFAULT
, "g_log_writer_default");
217 for (gsize i
= 0; i
< n_fields
; ++i
) {
218 if (!strcmp(fields
[i
].key
, "MESSAGE") && fields
[i
].length
< 0) {
219 HandleGLibMessage(flags
,
220 nsDependentCString((const char*)fields
[i
].value
));
224 return sLogWriterDefault(flags
, fields
, n_fields
, user_data
);
230 static void fpehandler(int signum
, siginfo_t
* si
, void* context
) {
231 /* Integer divide by zero or integer overflow. */
232 /* Note: FPE_INTOVF is ignored on Intel, PowerPC and SPARC systems. */
233 if (si
->si_code
== FPE_INTDIV
|| si
->si_code
== FPE_INTOVF
) {
234 NS_DebugBreak(NS_DEBUG_ABORT
, "Divide by zero", nullptr, __FILE__
,
239 # if defined(__i386__) || defined(__amd64__)
240 ucontext_t
* uc
= (ucontext_t
*)context
;
242 _STRUCT_FP_CONTROL
* ctrl
= &uc
->uc_mcontext
->__fs
.__fpu_fcw
;
243 ctrl
->__invalid
= ctrl
->__denorm
= ctrl
->__zdiv
= ctrl
->__ovrfl
=
244 ctrl
->__undfl
= ctrl
->__precis
= 1;
246 _STRUCT_FP_STATUS
* status
= &uc
->uc_mcontext
->__fs
.__fpu_fsw
;
247 status
->__invalid
= status
->__denorm
= status
->__zdiv
= status
->__ovrfl
=
248 status
->__undfl
= status
->__precis
= status
->__stkflt
=
249 status
->__errsumm
= 0;
251 uint32_t* mxcsr
= &uc
->uc_mcontext
->__fs
.__fpu_mxcsr
;
252 *mxcsr
|= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
253 *mxcsr
&= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
256 # if defined(LINUX) && !defined(ANDROID)
258 # if defined(__i386__)
259 ucontext_t
* uc
= (ucontext_t
*)context
;
261 * It seems that we have no access to mxcsr on Linux. libc
262 * seems to be translating cw/sw to mxcsr.
264 unsigned long int* cw
= &uc
->uc_mcontext
.fpregs
->cw
;
265 *cw
|= FPU_EXCEPTION_MASK
;
267 unsigned long int* sw
= &uc
->uc_mcontext
.fpregs
->sw
;
268 *sw
&= ~FPU_STATUS_FLAGS
;
270 # if defined(__amd64__)
271 ucontext_t
* uc
= (ucontext_t
*)context
;
273 uint16_t* cw
= &uc
->uc_mcontext
.fpregs
->cwd
;
274 *cw
|= FPU_EXCEPTION_MASK
;
276 uint16_t* sw
= &uc
->uc_mcontext
.fpregs
->swd
;
277 *sw
&= ~FPU_STATUS_FLAGS
;
279 uint32_t* mxcsr
= &uc
->uc_mcontext
.fpregs
->mxcsr
;
280 *mxcsr
|= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
281 *mxcsr
&= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
285 ucontext_t
* uc
= (ucontext_t
*)context
;
288 uint32_t* cw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[0];
289 *cw
|= FPU_EXCEPTION_MASK
;
291 uint32_t* sw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[1];
292 *sw
&= ~FPU_STATUS_FLAGS
;
294 /* address of the instruction that caused the exception */
295 uint32_t* ip
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[3];
296 uc
->uc_mcontext
.gregs
[REG_PC
] = *ip
;
298 # if defined(__amd64__)
299 uint16_t* cw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.cw
;
300 *cw
|= FPU_EXCEPTION_MASK
;
302 uint16_t* sw
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.sw
;
303 *sw
&= ~FPU_STATUS_FLAGS
;
305 uint32_t* mxcsr
= &uc
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.mxcsr
;
306 *mxcsr
|= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
307 *mxcsr
&= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
313 void InstallSignalHandlers(const char* aProgname
) {
314 # if defined(CRAWL_STACK_ON_SIGSEGV)
316 const char* tmp
= strdup(aProgname
);
321 # endif // CRAWL_STACK_ON_SIGSEGV
323 const char* gdbSleep
= PR_GetEnv("MOZ_GDB_SLEEP");
324 if (gdbSleep
&& *gdbSleep
) {
326 if (1 == sscanf(gdbSleep
, "%u", &s
)) {
327 _gdb_sleep_duration
= s
;
331 # if defined(CRAWL_STACK_ON_SIGSEGV)
332 if (!getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) {
333 void (*crap_handler
)(int) = GeckoProcessType_Default
!= XRE_GetProcessType()
334 ? child_ah_crap_handler
336 signal(SIGSEGV
, crap_handler
);
337 signal(SIGILL
, crap_handler
);
338 signal(SIGABRT
, crap_handler
);
340 # endif // CRAWL_STACK_ON_SIGSEGV
343 /* Install a handler for floating point exceptions and disable them if they
345 struct sigaction sa
, osa
;
346 sa
.sa_flags
= SA_ONSTACK
| SA_RESTART
| SA_SIGINFO
;
347 sa
.sa_sigaction
= fpehandler
;
348 sigemptyset(&sa
.sa_mask
);
349 sigaction(SIGFPE
, &sa
, &osa
);
352 if (!XRE_IsParentProcess()) {
354 * If the user is debugging a Gecko parent process in gdb and hits ^C to
355 * suspend, a SIGINT signal will be sent to the child. We ignore this signal
356 * so the child isn't killed.
358 signal(SIGINT
, SIG_IGN
);
361 # if defined(DEBUG) && defined(LINUX)
362 const char* memLimit
= PR_GetEnv("MOZ_MEM_LIMIT");
363 if (memLimit
&& *memLimit
) {
364 long m
= atoi(memLimit
);
369 setrlimit(RLIMIT_AS
, &r
);
373 # ifdef MOZ_WIDGET_GTK
374 // Override the default glib logging function to intercept some crashes that
375 // are uninterceptable otherwise. Also, when XPCOM_DEBUG_BREAK is set, we can
376 // also get stacks for them, so we get stacks for it too.
378 // If we can hook via g_log_set_writer_func, then we don't need to hook via
379 // g_log_set_default_handler, because the GTK default handler uses structured
380 // logging and thus will end up in our log writer function anyways.
381 static const auto sSetLogWriter
=
382 (void (*)(GLogWriterFunc
, gpointer
, GDestroyNotify
))dlsym(
383 RTLD_DEFAULT
, "g_log_set_writer_func");
385 sSetLogWriter(glib_log_writer_func
, nullptr, nullptr);
387 orig_log_func
= g_log_set_default_handler(glib_log_func
, nullptr);
394 # include <windows.h>
398 * WinNT.h prior to SDK7 does not expose the structure of the ExtendedRegisters
399 * for ia86. We known that MxCsr is at offset 0x18 and is a DWORD.
401 # define MXCSR(ctx) (*(DWORD*)(((BYTE*)(ctx)->ExtendedRegisters) + 0x18))
405 # define MXCSR(ctx) (ctx)->MxCsr
408 # if defined(_M_IX86) || defined(_M_X64)
411 # define X87CW(ctx) (ctx)->FltSave.ControlWord
412 # define X87SW(ctx) (ctx)->FltSave.StatusWord
414 # define X87CW(ctx) (ctx)->FloatSave.ControlWord
415 # define X87SW(ctx) (ctx)->FloatSave.StatusWord
418 static LPTOP_LEVEL_EXCEPTION_FILTER gFPEPreviousFilter
;
420 LONG __stdcall
FpeHandler(PEXCEPTION_POINTERS pe
) {
421 PEXCEPTION_RECORD e
= (PEXCEPTION_RECORD
)pe
->ExceptionRecord
;
422 CONTEXT
* c
= (CONTEXT
*)pe
->ContextRecord
;
424 switch (e
->ExceptionCode
) {
425 case STATUS_FLOAT_DENORMAL_OPERAND
:
426 case STATUS_FLOAT_DIVIDE_BY_ZERO
:
427 case STATUS_FLOAT_INEXACT_RESULT
:
428 case STATUS_FLOAT_INVALID_OPERATION
:
429 case STATUS_FLOAT_OVERFLOW
:
430 case STATUS_FLOAT_STACK_CHECK
:
431 case STATUS_FLOAT_UNDERFLOW
:
432 case STATUS_FLOAT_MULTIPLE_FAULTS
:
433 case STATUS_FLOAT_MULTIPLE_TRAPS
:
434 X87CW(c
) |= FPU_EXCEPTION_MASK
; /* disable all FPU exceptions */
435 X87SW(c
) &= ~FPU_STATUS_FLAGS
; /* clear all pending FPU exceptions */
437 if (c
->ContextFlags
& CONTEXT_EXTENDED_REGISTERS
) {
439 MXCSR(c
) |= SSE_EXCEPTION_MASK
; /* disable all SSE exceptions */
440 MXCSR(c
) &= ~SSE_STATUS_FLAGS
; /* clear all pending SSE exceptions */
444 return EXCEPTION_CONTINUE_EXECUTION
;
446 LONG action
= EXCEPTION_CONTINUE_SEARCH
;
447 if (gFPEPreviousFilter
) action
= gFPEPreviousFilter(pe
);
452 void InstallSignalHandlers(const char* aProgname
) {
453 gFPEPreviousFilter
= SetUnhandledExceptionFilter(FpeHandler
);
458 void InstallSignalHandlers(const char* aProgname
) {}
463 # error No signal handling implementation for this platform.