1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <signalshared.hxx>
24 #include <config_features.h>
26 #include "soffice.hxx"
28 #include "backtrace.h"
30 #define MAX_STACK_FRAMES 256
32 #include <osl/diagnose.h>
33 #include <osl/signal.h>
34 #include <sal/log.hxx>
35 #include <sal/macros.h>
36 #include <sal/backtrace.hxx>
44 #if defined HAVE_VALGRIND_HEADERS
45 #include <valgrind/memcheck.h>
53 extern "C" using Handler1_t
= void (*)(int);
54 extern "C" using Handler2_t
= void (*)(int, siginfo_t
*, void *);
63 bool siginfo
; // Handler2 is active
66 { SIGHUP
, ACT_HIDE
, SIG_DFL
, false }, /* hangup */
67 { SIGINT
, ACT_EXIT
, SIG_DFL
, false }, /* interrupt (rubout) */
68 { SIGQUIT
, ACT_EXIT
, SIG_DFL
, false }, /* quit (ASCII FS) */
69 { SIGILL
, ACT_SYSTEM
, SIG_DFL
, false }, /* illegal instruction (not reset when caught) */
70 /* changed from ACT_ABOUT to ACT_SYSTEM to try and get collector to run*/
71 { SIGTRAP
, ACT_ABORT
, SIG_DFL
, false }, /* trace trap (not reset when caught) */
72 #if ( SIGIOT != SIGABRT )
73 { SIGIOT
, ACT_ABORT
, SIG_DFL
, false }, /* IOT instruction */
75 #if defined(FORCE_DEFAULT_SIGNAL)
76 { SIGABRT
, ACT_SYSTEM
, SIG_DFL
, false }, /* used by abort, replace SIGIOT in the future */
78 { SIGABRT
, ACT_ABORT
, SIG_DFL
, false }, /* used by abort, replace SIGIOT in the future */
81 { SIGEMT
, ACT_SYSTEM
, SIG_DFL
, false }, /* EMT instruction */
82 /* changed from ACT_ABORT to ACT_SYSTEM to remove handler*/
83 /* SIGEMT may also be used by the profiler - so it is probably not a good
84 plan to have the new handler use this signal*/
86 { SIGFPE
, ACT_ABORT
, SIG_DFL
, false }, /* floating point exception */
87 { SIGKILL
, ACT_SYSTEM
, SIG_DFL
, false }, /* kill (cannot be caught or ignored) */
88 { SIGBUS
, ACT_ABORT
, SIG_DFL
, false }, /* bus error */
89 #if defined(FORCE_DEFAULT_SIGNAL)
90 { SIGSEGV
, ACT_SYSTEM
, SIG_DFL
, false }, /* segmentation violation */
92 { SIGSEGV
, ACT_ABORT
, SIG_DFL
, false }, /* segmentation violation */
95 { SIGSYS
, ACT_ABORT
, SIG_DFL
, false }, /* bad argument to system call */
97 { SIGPIPE
, ACT_HIDE
, SIG_DFL
, false }, /* write on a pipe with no one to read it */
98 #if defined(FORCE_DEFAULT_SIGNAL)
99 { SIGALRM
, ACT_SYSTEM
, SIG_DFL
, false }, /* alarm clock */
101 { SIGALRM
, ACT_EXIT
, SIG_DFL
, false }, /* alarm clock */
103 { SIGTERM
, ACT_EXIT
, SIG_DFL
, false }, /* software termination signal from kill */
104 { SIGUSR1
, ACT_SYSTEM
, SIG_DFL
, false }, /* user defined signal 1 */
105 { SIGUSR2
, ACT_SYSTEM
, SIG_DFL
, false }, /* user defined signal 2 */
106 { SIGCHLD
, ACT_SYSTEM
, SIG_DFL
, false }, /* child status change */
108 { SIGPWR
, ACT_IGNORE
, SIG_DFL
, false }, /* power-fail restart */
110 { SIGWINCH
, ACT_IGNORE
, SIG_DFL
, false }, /* window size change */
111 { SIGURG
, ACT_EXIT
, SIG_DFL
, false }, /* urgent socket condition */
113 { SIGPOLL
, ACT_EXIT
, SIG_DFL
, false }, /* pollable event occurred */
115 { SIGSTOP
, ACT_SYSTEM
, SIG_DFL
, false }, /* stop (cannot be caught or ignored) */
116 { SIGTSTP
, ACT_SYSTEM
, SIG_DFL
, false }, /* user stop requested from tty */
117 { SIGCONT
, ACT_SYSTEM
, SIG_DFL
, false }, /* stopped process has been continued */
118 { SIGTTIN
, ACT_SYSTEM
, SIG_DFL
, false }, /* background tty read attempted */
119 { SIGTTOU
, ACT_SYSTEM
, SIG_DFL
, false }, /* background tty write attempted */
120 { SIGVTALRM
, ACT_EXIT
, SIG_DFL
, false }, /* virtual timer expired */
121 { SIGPROF
, ACT_SYSTEM
, SIG_DFL
, false }, /* profiling timer expired */
122 /*Change from ACT_EXIT to ACT_SYSTEM for SIGPROF is so that profiling signals do
123 not get taken by the new handler - the new handler does not pass on context
124 information which causes 'collect' to crash. This is a way of avoiding
125 what looks like a bug in the new handler*/
126 { SIGXCPU
, ACT_ABORT
, SIG_DFL
, false }, /* exceeded cpu limit */
127 { SIGXFSZ
, ACT_ABORT
, SIG_DFL
, false } /* exceeded file size limit */
129 const int NoSignals
= SAL_N_ELEMENTS(Signals
);
131 bool bSetSEGVHandler
= false;
132 bool bSetWINCHHandler
= false;
133 bool bSetILLHandler
= false;
135 void signalHandlerFunction(int, siginfo_t
*, void *);
137 #if HAVE_FEATURE_BREAKPAD
138 bool is_unset_signal(int signal
)
141 return (!bSetSEGVHandler
&& signal
== SIGSEGV
) ||
142 (!bSetWINCHHandler
&& signal
== SIGWINCH
) ||
143 (!bSetILLHandler
&& signal
== SIGILL
);
155 if (sal::detail::isSoffice())
157 // WORKAROUND FOR SEGV HANDLER CONFLICT
159 // the java jit needs SIGSEGV for proper work
160 // and we need SIGSEGV for the office crashguard
162 // TEMPORARY SOLUTION:
163 // the office sets the signal handler during startup
164 // java can than overwrite it, if needed
165 bSetSEGVHandler
= true;
167 // WORKAROUND FOR WINCH HANDLER (SEE ABOVE)
168 bSetWINCHHandler
= true;
170 // WORKAROUND FOR ILLEGAL INSTRUCTION HANDLER (SEE ABOVE)
171 bSetILLHandler
= true;
175 bSetSEGVHandler
= bSetWINCHHandler
= bSetILLHandler
= false;
178 struct sigaction act
;
179 act
.sa_sigaction
= signalHandlerFunction
;
180 act
.sa_flags
= SA_RESTART
| SA_SIGINFO
;
182 sigfillset(&(act
.sa_mask
));
184 /* Initialize the rest of the signals */
185 for (SignalAction
& rSignal
: Signals
)
187 #if defined HAVE_VALGRIND_HEADERS
188 if (rSignal
.Signal
== SIGUSR2
&& RUNNING_ON_VALGRIND
)
189 rSignal
.Action
= ACT_IGNORE
;
192 /* hack: stomcatd is attaching JavaVM which does not work with an sigaction(SEGV) */
193 if ((bSetSEGVHandler
|| rSignal
.Signal
!= SIGSEGV
)
194 && (bSetWINCHHandler
|| rSignal
.Signal
!= SIGWINCH
)
195 && (bSetILLHandler
|| rSignal
.Signal
!= SIGILL
))
197 if (rSignal
.Action
!= ACT_SYSTEM
)
199 if (rSignal
.Action
== ACT_HIDE
)
201 struct sigaction ign
;
203 ign
.sa_handler
= SIG_IGN
;
205 sigemptyset(&ign
.sa_mask
);
207 struct sigaction oact
;
208 if (sigaction(rSignal
.Signal
, &ign
, &oact
) == 0) {
209 rSignal
.siginfo
= (oact
.sa_flags
& SA_SIGINFO
) != 0;
210 if (rSignal
.siginfo
) {
214 rSignal
.Handler1
= oact
.sa_handler
;
217 rSignal
.Handler1
= SIG_DFL
;
218 rSignal
.siginfo
= false;
223 struct sigaction oact
;
224 if (sigaction(rSignal
.Signal
, &act
, &oact
) == 0) {
225 rSignal
.siginfo
= (oact
.sa_flags
& SA_SIGINFO
) != 0;
226 if (rSignal
.siginfo
) {
230 rSignal
.Handler1
= oact
.sa_handler
;
233 rSignal
.Handler1
= SIG_DFL
;
234 rSignal
.siginfo
= false;
241 /* Clear signal mask inherited from parent process (on macOS, upon a
242 crash soffice re-execs itself from within the signal handler, so the
243 second soffice would have the guilty signal blocked and would freeze upon
244 encountering a similar crash again): */
246 if (sigemptyset(&unset
) < 0 ||
247 pthread_sigmask(SIG_SETMASK
, &unset
, nullptr) < 0)
249 SAL_WARN("sal.osl", "sigemptyset or pthread_sigmask failed");
255 bool onDeInitSignal()
257 struct sigaction act
;
259 sigemptyset(&(act
.sa_mask
));
261 /* Initialize the rest of the signals */
262 for (int i
= NoSignals
- 1; i
>= 0; i
--)
263 if (Signals
[i
].Action
!= ACT_SYSTEM
264 && ((bSetSEGVHandler
|| Signals
[i
].Signal
!= SIGSEGV
)
265 && (bSetWINCHHandler
|| Signals
[i
].Signal
!= SIGWINCH
)
266 && (bSetILLHandler
|| Signals
[i
].Signal
!= SIGILL
)))
268 if (Signals
[i
].siginfo
) {
271 act
.sa_flags
= SA_SIGINFO
;
273 act
.sa_handler
= Signals
[i
].Handler1
;
277 sigaction(Signals
[i
].Signal
, &act
, nullptr);
285 void printStack(int sig
)
287 std::unique_ptr
<sal::BacktraceState
> bs
= sal::backtrace_get(MAX_STACK_FRAMES
);
289 fprintf( stderr
, "\n\nFatal exception: Signal %d\n", sig
);
291 #if ! HAVE_FEATURE_BACKTRACE && defined( MACOSX ) && !defined( INTEL )
292 fprintf( stderr
, "Please turn on Enable Crash Reporting and\nAutomatic Display of Crashlogs in the Console application\n" );
295 fputs( "Stack:\n", stderr
);
296 fprintf( stderr
, "%s\n", OUStringToOString( sal::backtrace_to_string(bs
.get()), RTL_TEXTENCODING_UTF8
).getStr() );
299 void callSystemHandler(int signal
, siginfo_t
* info
, void * context
)
303 for (i
= 0; i
< NoSignals
; i
++)
305 if (Signals
[i
].Signal
== signal
)
312 if ((Signals
[i
].Handler1
== SIG_DFL
) ||
313 (Signals
[i
].Handler1
== SIG_IGN
) ||
314 (Signals
[i
].Handler1
== SIG_ERR
))
316 switch (Signals
[i
].Action
)
318 case ACT_EXIT
: /* terminate */
319 /* prevent dumping core on exit() */
323 case ACT_ABORT
: /* terminate with core dump */
324 struct sigaction act
;
325 act
.sa_handler
= SIG_DFL
;
327 sigemptyset(&(act
.sa_mask
));
328 sigaction(SIGABRT
, &act
, nullptr);
329 printStack( signal
);
333 case ACT_IGNORE
: /* ignore */
336 default: /* should never happen */
340 else if (Signals
[i
].siginfo
) {
341 (*Signals
[i
].Handler2
)(
342 signal
, info
, context
);
344 (*Signals
[i
].Handler1
)(signal
);
348 #if defined HAVE_VALGRIND_HEADERS
349 void DUMPCURRENTALLOCS()
351 VALGRIND_PRINTF( "=== start memcheck dump of active allocations ===\n" );
353 #if __GNUC__ && !defined(__clang__)
354 # pragma GCC diagnostic push
355 # pragma GCC diagnostic ignored "-Wunused-but-set-variable"
358 VALGRIND_DO_LEAK_CHECK
;
360 #if __GNUC__ && !defined(__clang__)
361 # pragma GCC diagnostic pop
364 VALGRIND_PRINTF( "=== end memcheck dump of active allocations ===\n" );
368 void signalHandlerFunction(int signal
, siginfo_t
* info
, void * context
)
372 Info
.UserSignal
= signal
;
373 Info
.UserData
= nullptr;
381 #if ( SIGIOT != SIGABRT )
384 Info
.Signal
= osl_Signal_AccessViolation
;
388 Info
.Signal
= osl_Signal_IntegerDivideByZero
;
392 Info
.Signal
= osl_Signal_FloatDivideByZero
;
398 Info
.Signal
= osl_Signal_Terminate
;
401 #if defined HAVE_VALGRIND_HEADERS
403 if (RUNNING_ON_VALGRIND
)
405 Info
.Signal
= osl_Signal_System
;
410 Info
.Signal
= osl_Signal_System
;
414 #if HAVE_FEATURE_BREAKPAD
415 if ((Info
.Signal
== osl_Signal_AccessViolation
||
416 Info
.Signal
== osl_Signal_IntegerDivideByZero
||
417 Info
.Signal
== osl_Signal_FloatDivideByZero
) && !is_unset_signal(signal
))
419 callSystemHandler(signal
, info
, context
);
423 switch (callSignalHandler(&Info
))
425 case osl_Signal_ActCallNextHdl
:
426 callSystemHandler(signal
, info
, context
);
429 case osl_Signal_ActAbortApp
:
430 struct sigaction act
;
431 act
.sa_handler
= SIG_DFL
;
433 sigemptyset(&(act
.sa_mask
));
434 sigaction(SIGABRT
, &act
, nullptr);
435 printStack( signal
);
439 case osl_Signal_ActKillApp
:
440 /* prevent dumping core on exit() */
450 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */