Bug 1888590 - Mark some subtests on trusted-types-event-handlers.html as failing...
[gecko.git] / toolkit / xre / nsSigHandlers.cpp
bloba9679eab753297f1ce77313c3635328f9cc505ce
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/. */
6 /*
7 * This module is supposed to abstract signal handling away from the other
8 * platforms that do not support it.
9 */
11 #include "nsSigHandlers.h"
13 #ifdef XP_UNIX
15 # include <signal.h>
16 # include <stdio.h>
17 # include <string.h>
18 # include "prthread.h"
19 # include "prenv.h"
20 # include "nsDebug.h"
21 # include "nsString.h"
22 # include "nsXULAppAPI.h"
24 # if defined(LINUX)
25 # include <sys/time.h>
26 # include <sys/resource.h>
27 # include <unistd.h>
28 # include <stdlib.h> // atoi
29 # include <sys/prctl.h>
30 # ifndef ANDROID // no Android impl
31 # include <ucontext.h>
32 # endif
33 # endif
35 # if defined(SOLARIS)
36 # include <sys/resource.h>
37 # include <ucontext.h>
38 # endif
40 # ifdef MOZ_WIDGET_GTK
41 # include <dlfcn.h>
42 # endif
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
50 # endif
52 # ifndef PR_SET_PTRACER
53 # define PR_SET_PTRACER 0x59616d61
54 # endif
55 # ifndef PR_SET_PTRACER_ANY
56 # define PR_SET_PTRACER_ANY ((unsigned long)-1)
57 # endif
59 # if defined(CRAWL_STACK_ON_SIGSEGV)
61 # include <unistd.h>
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;
72 extern "C" {
74 static void PrintStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP,
75 void* aClosure) {
76 char buf[1024];
77 MozCodeAddressDetails details;
79 MozDescribeCodeAddress(aPC, &details);
80 MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
81 fprintf(stdout, "%s\n", buf);
82 fflush(stdout);
86 void common_crap_handler(int signum, const void* aFirstFramePC) {
87 printf("\nProgram %s (pid = %d) received signal %d.\n", gProgname, getpid(),
88 signum);
90 printf("Stack:\n");
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",
95 gProgname, getpid());
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");
104 _exit(signum);
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.
121 # include <glib.h>
122 # endif
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+
128 typedef enum {
129 G_LOG_WRITER_HANDLED = 1,
130 G_LOG_WRITER_UNHANDLED = 0,
131 } GLogWriterOutput;
132 typedef struct _GLogField GLogField;
133 struct _GLogField {
134 const gchar* key;
135 gconstpointer value;
136 gssize length;
138 typedef GLogWriterOutput (*GLogWriterFunc)(GLogLevelFlags log_level,
139 const GLogField* fields,
140 gsize n_fields, gpointer user_data);
141 # endif
143 static GLogFunc orig_log_func = nullptr;
145 extern "C" {
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*,
149 gsize, gpointer);
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
157 return true;
159 if (StringBeginsWith(aMessage, "Error flushing display: "_ns)) {
160 // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/wayland/gdkeventsource.c#L68
161 return true;
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
165 return true;
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
170 return true;
172 return false;
175 static void HandleGLibMessage(GLogLevelFlags aLogLevel,
176 const nsDependentCString& aMessage) {
177 if (MOZ_UNLIKELY(IsCrashyGtkMessage(aMessage))) {
178 MOZ_CRASH_UNSAFE(strdup(aMessage.get()));
181 if (aLogLevel &
182 (G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION)) {
183 NS_DebugBreak(NS_DEBUG_ASSERTION, aMessage.get(), "glib assertion",
184 __FILE__, __LINE__);
185 } else if (aLogLevel & (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)) {
186 NS_DebugBreak(NS_DEBUG_WARNING, aMessage.get(), "glib warning", __FILE__,
187 __LINE__);
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));
207 break;
210 return sLogWriterDefault(flags, fields, n_fields, user_data);
213 # endif
215 # ifdef SA_SIGINFO
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__,
221 __LINE__);
224 # ifdef XP_MACOSX
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 */
240 # endif
241 # endif
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;
255 # endif
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 */
268 # endif
269 # endif
270 # ifdef SOLARIS
271 ucontext_t* uc = (ucontext_t*)context;
273 # if defined(__i386)
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;
283 # endif
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 */
294 # endif
295 # endif
297 # endif
299 void InstallSignalHandlers(const char* aProgname) {
300 # if defined(CRAWL_STACK_ON_SIGSEGV)
301 if (aProgname) {
302 const char* tmp = strdup(aProgname);
303 if (tmp) {
304 gProgname = tmp;
307 # endif // CRAWL_STACK_ON_SIGSEGV
309 const char* gdbSleep = PR_GetEnv("MOZ_GDB_SLEEP");
310 if (gdbSleep && *gdbSleep) {
311 unsigned int s;
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
321 : ah_crap_handler;
322 signal(SIGSEGV, crap_handler);
323 signal(SIGILL, crap_handler);
324 signal(SIGABRT, crap_handler);
326 # endif // CRAWL_STACK_ON_SIGSEGV
328 # ifdef SA_SIGINFO
329 /* Install a handler for floating point exceptions and disable them if they
330 * occur. */
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);
336 # endif
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);
351 m *= (1024 * 1024);
352 struct rlimit r;
353 r.rlim_cur = m;
354 r.rlim_max = m;
355 setrlimit(RLIMIT_AS, &r);
357 # endif
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");
370 if (sSetLogWriter) {
371 sSetLogWriter(glib_log_writer_func, nullptr, nullptr);
372 } else {
373 orig_log_func = g_log_set_default_handler(glib_log_func, nullptr);
375 # endif
378 #elif XP_WIN
380 # include <windows.h>
382 # ifdef _M_IX86
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))
388 # endif
390 # ifdef _M_X64
391 # define MXCSR(ctx) (ctx)->MxCsr
392 # endif
394 # if defined(_M_IX86) || defined(_M_X64)
396 # ifdef _M_X64
397 # define X87CW(ctx) (ctx)->FltSave.ControlWord
398 # define X87SW(ctx) (ctx)->FltSave.StatusWord
399 # else
400 # define X87CW(ctx) (ctx)->FloatSave.ControlWord
401 # define X87SW(ctx) (ctx)->FloatSave.StatusWord
402 # endif
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 */
422 # ifdef _M_IX86
423 if (c->ContextFlags & CONTEXT_EXTENDED_REGISTERS) {
424 # endif
425 MXCSR(c) |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */
426 MXCSR(c) &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */
427 # ifdef _M_IX86
429 # endif
430 return EXCEPTION_CONTINUE_EXECUTION;
432 LONG action = EXCEPTION_CONTINUE_SEARCH;
433 if (gFPEPreviousFilter) action = gFPEPreviousFilter(pe);
435 return action;
438 void InstallSignalHandlers(const char* aProgname) {
439 gFPEPreviousFilter = SetUnhandledExceptionFilter(FpeHandler);
442 # else
444 void InstallSignalHandlers(const char* aProgname) {}
446 # endif
448 #else
449 # error No signal handling implementation for this platform.
450 #endif