/src/libreoffice/sal/osl/unx/signal.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | #include <sal/config.h> |
21 | | |
22 | | #include <signalshared.hxx> |
23 | | |
24 | | #include <config_features.h> |
25 | | |
26 | | #include "soffice.hxx" |
27 | | |
28 | | #include "backtrace.h" |
29 | | |
30 | 0 | #define MAX_STACK_FRAMES 256 |
31 | | |
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> |
37 | | |
38 | 0 | #define ACT_IGNORE 1 |
39 | 0 | #define ACT_EXIT 2 |
40 | 2.91k | #define ACT_SYSTEM 3 |
41 | 1.62k | #define ACT_HIDE 4 |
42 | 0 | #define ACT_ABORT 5 |
43 | | |
44 | | #if defined HAVE_VALGRIND_HEADERS |
45 | | #include <valgrind/memcheck.h> |
46 | | #endif |
47 | | |
48 | | #include <signal.h> |
49 | | #include <unistd.h> |
50 | | |
51 | | namespace |
52 | | { |
53 | | extern "C" using Handler1_t = void (*)(int); |
54 | | extern "C" using Handler2_t = void (*)(int, siginfo_t *, void *); |
55 | | struct SignalAction |
56 | | { |
57 | | int Signal; |
58 | | int Action; |
59 | | union { |
60 | | Handler1_t Handler1; |
61 | | Handler2_t Handler2; |
62 | | }; |
63 | | bool siginfo; // Handler2 is active |
64 | | } Signals[] = |
65 | | { |
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 */ |
74 | | #endif |
75 | | #if defined(FORCE_DEFAULT_SIGNAL) |
76 | | { SIGABRT, ACT_SYSTEM, SIG_DFL, false }, /* used by abort, replace SIGIOT in the future */ |
77 | | #else |
78 | | { SIGABRT, ACT_ABORT, SIG_DFL, false }, /* used by abort, replace SIGIOT in the future */ |
79 | | #endif |
80 | | #ifdef SIGEMT |
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*/ |
85 | | #endif |
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 */ |
91 | | #else |
92 | | { SIGSEGV, ACT_ABORT, SIG_DFL, false }, /* segmentation violation */ |
93 | | #endif |
94 | | #ifdef SIGSYS |
95 | | { SIGSYS, ACT_ABORT, SIG_DFL, false }, /* bad argument to system call */ |
96 | | #endif |
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 */ |
100 | | #else |
101 | | { SIGALRM, ACT_EXIT, SIG_DFL, false }, /* alarm clock */ |
102 | | #endif |
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 */ |
107 | | #ifdef SIGPWR |
108 | | { SIGPWR, ACT_IGNORE, SIG_DFL, false }, /* power-fail restart */ |
109 | | #endif |
110 | | { SIGWINCH, ACT_IGNORE, SIG_DFL, false }, /* window size change */ |
111 | | { SIGURG, ACT_EXIT, SIG_DFL, false }, /* urgent socket condition */ |
112 | | #ifdef SIGPOLL |
113 | | { SIGPOLL, ACT_EXIT, SIG_DFL, false }, /* pollable event occurred */ |
114 | | #endif |
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 */ |
128 | | }; |
129 | | const auto NoSignals = std::ssize(Signals); |
130 | | |
131 | | bool bSetSEGVHandler = false; |
132 | | bool bSetWINCHHandler = false; |
133 | | bool bSetILLHandler = false; |
134 | | |
135 | | void signalHandlerFunction(int, siginfo_t *, void *); |
136 | | |
137 | | #if HAVE_FEATURE_BREAKPAD |
138 | | bool is_unset_signal(int signal) |
139 | | { |
140 | | #ifdef DBG_UTIL |
141 | | return (!bSetSEGVHandler && signal == SIGSEGV) || |
142 | | (!bSetWINCHHandler && signal == SIGWINCH) || |
143 | | (!bSetILLHandler && signal == SIGILL); |
144 | | #else |
145 | | (void) signal; |
146 | | return false; |
147 | | #endif |
148 | | } |
149 | | #endif |
150 | | |
151 | | } |
152 | | |
153 | | bool onInitSignal() |
154 | 108 | { |
155 | 108 | if (sal::detail::isSoffice()) |
156 | 0 | { |
157 | | // WORKAROUND FOR SEGV HANDLER CONFLICT |
158 | | // |
159 | | // the java jit needs SIGSEGV for proper work |
160 | | // and we need SIGSEGV for the office crashguard |
161 | | // |
162 | | // TEMPORARY SOLUTION: |
163 | | // the office sets the signal handler during startup |
164 | | // java can than overwrite it, if needed |
165 | 0 | bSetSEGVHandler = true; |
166 | | |
167 | | // WORKAROUND FOR WINCH HANDLER (SEE ABOVE) |
168 | 0 | bSetWINCHHandler = true; |
169 | | |
170 | | // WORKAROUND FOR ILLEGAL INSTRUCTION HANDLER (SEE ABOVE) |
171 | 0 | bSetILLHandler = true; |
172 | 0 | } |
173 | | |
174 | | #ifdef DBG_UTIL |
175 | | bSetSEGVHandler = bSetWINCHHandler = bSetILLHandler = false; |
176 | | #endif |
177 | | |
178 | 108 | struct sigaction act; |
179 | 108 | act.sa_sigaction = signalHandlerFunction; |
180 | 108 | act.sa_flags = SA_RESTART | SA_SIGINFO; |
181 | | |
182 | 108 | sigfillset(&(act.sa_mask)); |
183 | | |
184 | | /* Initialize the rest of the signals */ |
185 | 108 | for (SignalAction & rSignal : Signals) |
186 | 3.24k | { |
187 | | #if defined HAVE_VALGRIND_HEADERS |
188 | | if (rSignal.Signal == SIGUSR2 && RUNNING_ON_VALGRIND) |
189 | | rSignal.Action = ACT_IGNORE; |
190 | | #endif |
191 | | |
192 | | /* hack: stomcatd is attaching JavaVM which does not work with an sigaction(SEGV) */ |
193 | 3.24k | if ((bSetSEGVHandler || rSignal.Signal != SIGSEGV) |
194 | 3.13k | && (bSetWINCHHandler || rSignal.Signal != SIGWINCH) |
195 | 3.02k | && (bSetILLHandler || rSignal.Signal != SIGILL)) |
196 | 2.91k | { |
197 | 2.91k | if (rSignal.Action != ACT_SYSTEM) |
198 | 1.62k | { |
199 | 1.62k | if (rSignal.Action == ACT_HIDE) |
200 | 216 | { |
201 | 216 | struct sigaction ign; |
202 | | |
203 | 216 | ign.sa_handler = SIG_IGN; |
204 | 216 | ign.sa_flags = 0; |
205 | 216 | sigemptyset(&ign.sa_mask); |
206 | | |
207 | 216 | struct sigaction oact; |
208 | 216 | if (sigaction(rSignal.Signal, &ign, &oact) == 0) { |
209 | 216 | rSignal.siginfo = (oact.sa_flags & SA_SIGINFO) != 0; |
210 | 216 | if (rSignal.siginfo) { |
211 | 0 | rSignal.Handler2 = |
212 | 0 | oact.sa_sigaction; |
213 | 216 | } else { |
214 | 216 | rSignal.Handler1 = oact.sa_handler; |
215 | 216 | } |
216 | 216 | } else { |
217 | 0 | rSignal.Handler1 = SIG_DFL; |
218 | 0 | rSignal.siginfo = false; |
219 | 0 | } |
220 | 216 | } |
221 | 1.40k | else |
222 | 1.40k | { |
223 | 1.40k | struct sigaction oact; |
224 | 1.40k | if (sigaction(rSignal.Signal, &act, &oact) == 0) { |
225 | 1.40k | rSignal.siginfo = (oact.sa_flags & SA_SIGINFO) != 0; |
226 | 1.40k | if (rSignal.siginfo) { |
227 | 216 | rSignal.Handler2 = |
228 | 216 | oact.sa_sigaction; |
229 | 1.18k | } else { |
230 | 1.18k | rSignal.Handler1 = oact.sa_handler; |
231 | 1.18k | } |
232 | 1.40k | } else { |
233 | 0 | rSignal.Handler1 = SIG_DFL; |
234 | 0 | rSignal.siginfo = false; |
235 | 0 | } |
236 | 1.40k | } |
237 | 1.62k | } |
238 | 2.91k | } |
239 | 3.24k | } |
240 | | |
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): */ |
245 | 108 | sigset_t unset; |
246 | 108 | if (sigemptyset(&unset) < 0 || |
247 | 108 | pthread_sigmask(SIG_SETMASK, &unset, nullptr) < 0) |
248 | 0 | { |
249 | 0 | SAL_WARN("sal.osl", "sigemptyset or pthread_sigmask failed"); |
250 | 0 | } |
251 | | |
252 | 108 | return true; |
253 | 108 | } |
254 | | |
255 | | bool onDeInitSignal() |
256 | 0 | { |
257 | 0 | struct sigaction act; |
258 | |
|
259 | 0 | sigemptyset(&(act.sa_mask)); |
260 | | |
261 | | /* Initialize the rest of the signals */ |
262 | 0 | for (int i = NoSignals - 1; i >= 0; i--) |
263 | 0 | if (Signals[i].Action != ACT_SYSTEM |
264 | 0 | && ((bSetSEGVHandler || Signals[i].Signal != SIGSEGV) |
265 | 0 | && (bSetWINCHHandler || Signals[i].Signal != SIGWINCH) |
266 | 0 | && (bSetILLHandler || Signals[i].Signal != SIGILL))) |
267 | 0 | { |
268 | 0 | if (Signals[i].siginfo) { |
269 | 0 | act.sa_sigaction = |
270 | 0 | Signals[i].Handler2; |
271 | 0 | act.sa_flags = SA_SIGINFO; |
272 | 0 | } else { |
273 | 0 | act.sa_handler = Signals[i].Handler1; |
274 | 0 | act.sa_flags = 0; |
275 | 0 | } |
276 | |
|
277 | 0 | sigaction(Signals[i].Signal, &act, nullptr); |
278 | 0 | } |
279 | |
|
280 | 0 | return false; |
281 | 0 | } |
282 | | |
283 | | namespace |
284 | | { |
285 | | void printStack(int sig) |
286 | 0 | { |
287 | 0 | std::unique_ptr<sal::BacktraceState> bs = sal::backtrace_get(MAX_STACK_FRAMES); |
288 | |
|
289 | 0 | fprintf( stderr, "\n\nFatal exception: Signal %d\n", sig ); |
290 | |
|
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" ); |
293 | | #endif |
294 | |
|
295 | 0 | fputs( "Stack:\n", stderr ); |
296 | 0 | fprintf( stderr, "%s\n", OUStringToOString( sal::backtrace_to_string(bs.get()), RTL_TEXTENCODING_UTF8 ).getStr() ); |
297 | 0 | } |
298 | | |
299 | | void callSystemHandler(int signal, siginfo_t * info, void * context) |
300 | 0 | { |
301 | 0 | int i; |
302 | |
|
303 | 0 | for (i = 0; i < NoSignals; i++) |
304 | 0 | { |
305 | 0 | if (Signals[i].Signal == signal) |
306 | 0 | break; |
307 | 0 | } |
308 | |
|
309 | 0 | if (i >= NoSignals) |
310 | 0 | return; |
311 | | |
312 | 0 | if ((Signals[i].Handler1 == SIG_DFL) || |
313 | 0 | (Signals[i].Handler1 == SIG_IGN) || |
314 | 0 | (Signals[i].Handler1 == SIG_ERR)) |
315 | 0 | { |
316 | 0 | switch (Signals[i].Action) |
317 | 0 | { |
318 | 0 | case ACT_EXIT: /* terminate */ |
319 | | /* prevent dumping core on exit() */ |
320 | 0 | _exit(255); |
321 | 0 | break; |
322 | | |
323 | 0 | case ACT_ABORT: /* terminate with core dump */ |
324 | 0 | struct sigaction act; |
325 | 0 | act.sa_handler = SIG_DFL; |
326 | 0 | act.sa_flags = 0; |
327 | 0 | sigemptyset(&(act.sa_mask)); |
328 | 0 | sigaction(SIGABRT, &act, nullptr); |
329 | 0 | printStack( signal ); |
330 | 0 | abort(); |
331 | 0 | break; |
332 | | |
333 | 0 | case ACT_IGNORE: /* ignore */ |
334 | 0 | break; |
335 | | |
336 | 0 | default: /* should never happen */ |
337 | 0 | OSL_ASSERT(false); |
338 | 0 | } |
339 | 0 | } |
340 | 0 | else if (Signals[i].siginfo) { |
341 | 0 | (*Signals[i].Handler2)( |
342 | 0 | signal, info, context); |
343 | 0 | } else { |
344 | 0 | (*Signals[i].Handler1)(signal); |
345 | 0 | } |
346 | 0 | } |
347 | | |
348 | | #if defined HAVE_VALGRIND_HEADERS |
349 | | void DUMPCURRENTALLOCS() |
350 | | { |
351 | | VALGRIND_PRINTF( "=== start memcheck dump of active allocations ===\n" ); |
352 | | |
353 | | #if __GNUC__ && !defined(__clang__) |
354 | | # pragma GCC diagnostic push |
355 | | # pragma GCC diagnostic ignored "-Wunused-but-set-variable" |
356 | | #endif |
357 | | |
358 | | VALGRIND_DO_LEAK_CHECK; |
359 | | |
360 | | #if __GNUC__ && !defined(__clang__) |
361 | | # pragma GCC diagnostic pop |
362 | | #endif |
363 | | |
364 | | VALGRIND_PRINTF( "=== end memcheck dump of active allocations ===\n" ); |
365 | | } |
366 | | #endif |
367 | | |
368 | | void signalHandlerFunction(int signal, siginfo_t * info, void * context) |
369 | 0 | { |
370 | 0 | oslSignalInfo Info; |
371 | |
|
372 | 0 | Info.UserSignal = signal; |
373 | 0 | Info.UserData = nullptr; |
374 | |
|
375 | 0 | switch (signal) |
376 | 0 | { |
377 | 0 | case SIGBUS: |
378 | 0 | case SIGILL: |
379 | 0 | case SIGSEGV: |
380 | 0 | case SIGIOT: |
381 | | #if ( SIGIOT != SIGABRT ) |
382 | | case SIGABRT: |
383 | | #endif |
384 | 0 | Info.Signal = osl_Signal_AccessViolation; |
385 | 0 | break; |
386 | | |
387 | 0 | case -1: |
388 | 0 | Info.Signal = osl_Signal_IntegerDivideByZero; |
389 | 0 | break; |
390 | | |
391 | 0 | case SIGFPE: |
392 | 0 | Info.Signal = osl_Signal_FloatDivideByZero; |
393 | 0 | break; |
394 | | |
395 | 0 | case SIGINT: |
396 | 0 | case SIGTERM: |
397 | 0 | case SIGQUIT: |
398 | 0 | Info.Signal = osl_Signal_Terminate; |
399 | 0 | break; |
400 | | |
401 | | #if defined HAVE_VALGRIND_HEADERS |
402 | | case SIGUSR2: |
403 | | if (RUNNING_ON_VALGRIND) |
404 | | DUMPCURRENTALLOCS(); |
405 | | Info.Signal = osl_Signal_System; |
406 | | break; |
407 | | #endif |
408 | | |
409 | 0 | default: |
410 | 0 | Info.Signal = osl_Signal_System; |
411 | 0 | break; |
412 | 0 | } |
413 | | |
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)) |
418 | | { |
419 | | callSystemHandler(signal, info, context); |
420 | | } |
421 | | #endif |
422 | | |
423 | 0 | switch (callSignalHandler(&Info)) |
424 | 0 | { |
425 | 0 | case osl_Signal_ActCallNextHdl: |
426 | 0 | callSystemHandler(signal, info, context); |
427 | 0 | break; |
428 | | |
429 | 0 | case osl_Signal_ActAbortApp: |
430 | 0 | struct sigaction act; |
431 | 0 | act.sa_handler = SIG_DFL; |
432 | 0 | act.sa_flags = 0; |
433 | 0 | sigemptyset(&(act.sa_mask)); |
434 | 0 | sigaction(SIGABRT, &act, nullptr); |
435 | 0 | printStack( signal ); |
436 | 0 | abort(); |
437 | 0 | break; |
438 | | |
439 | 0 | case osl_Signal_ActKillApp: |
440 | | /* prevent dumping core on exit() */ |
441 | 0 | _exit(255); |
442 | 0 | break; |
443 | 0 | default: |
444 | 0 | break; |
445 | 0 | } |
446 | 0 | } |
447 | | |
448 | | } |
449 | | |
450 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |