1 /* Emergency actions in case of a fatal signal.
2 Copyright (C) 2003-2004, 2006-2020 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
22 #include "fatal-signal.h"
29 #include "glthread/lock.h"
30 #include "thread-optim.h"
31 #include "sig-handler.h"
34 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
36 /* ========================================================================= */
39 /* The list of fatal signals.
40 These are those signals whose default action is to terminate the process
41 without a core dump, except
42 SIGKILL - because it cannot be caught,
43 SIGALRM SIGUSR1 SIGUSR2 SIGPOLL SIGIO SIGLOST - because applications
44 often use them for their own purpose,
45 SIGPROF SIGVTALRM - because they are used for profiling,
46 SIGSTKFLT - because it is more similar to SIGFPE, SIGSEGV, SIGBUS,
47 SIGSYS - because it is more similar to SIGABRT, SIGSEGV,
48 SIGPWR - because it of too special use,
49 SIGRTMIN...SIGRTMAX - because they are reserved for application use.
51 SIGXCPU, SIGXFSZ - because they are quite similar to SIGTERM. */
53 static int fatal_signals
[] =
55 /* ISO C 99 signals. */
62 /* POSIX:2001 signals. */
76 /* Native Windows signals. */
83 #define num_fatal_signals (SIZEOF (fatal_signals) - 1)
85 /* Eliminate signals whose signal handler is SIG_IGN. */
88 init_fatal_signals (void)
90 /* This function is multithread-safe even without synchronization, because
91 if two threads execute it simultaneously, the fatal_signals[] array will
92 not change any more after the first of the threads has completed this
94 static bool fatal_signals_initialized
= false;
95 if (!fatal_signals_initialized
)
99 for (i
= 0; i
< num_fatal_signals
; i
++)
101 struct sigaction action
;
103 if (sigaction (fatal_signals
[i
], NULL
, &action
) >= 0
104 && get_handler (&action
) == SIG_IGN
)
105 fatal_signals
[i
] = -1;
108 fatal_signals_initialized
= true;
113 /* ========================================================================= */
116 typedef _GL_ASYNC_SAFE
void (*action_t
) (int sig
);
118 /* Type of an entry in the actions array.
119 The 'action' field is accessed from within the fatal_signal_handler(),
120 therefore we mark it as 'volatile'. */
123 volatile action_t action
;
127 /* The registered cleanup actions. */
128 static actions_entry_t static_actions
[32];
129 static actions_entry_t
* volatile actions
= static_actions
;
130 static sig_atomic_t volatile actions_count
= 0;
131 static size_t actions_allocated
= SIZEOF (static_actions
);
134 /* The saved signal handlers.
135 Size 32 would not be sufficient: On HP-UX, SIGXCPU = 33, SIGXFSZ = 34. */
136 static struct sigaction saved_sigactions
[64];
139 /* Uninstall the handlers. */
140 static _GL_ASYNC_SAFE
void
141 uninstall_handlers (void)
145 for (i
= 0; i
< num_fatal_signals
; i
++)
146 if (fatal_signals
[i
] >= 0)
148 int sig
= fatal_signals
[i
];
149 if (saved_sigactions
[sig
].sa_handler
== SIG_IGN
)
150 saved_sigactions
[sig
].sa_handler
= SIG_DFL
;
151 sigaction (sig
, &saved_sigactions
[sig
], NULL
);
156 /* The signal handler. It gets called asynchronously. */
157 static _GL_ASYNC_SAFE
void
158 fatal_signal_handler (int sig
)
162 /* Get the last registered cleanup action, in a reentrant way. */
164 size_t n
= actions_count
;
169 action
= actions
[n
].action
;
170 /* Execute the action. */
174 /* Now execute the signal's default action.
175 If the signal being delivered was blocked, the re-raised signal would be
176 delivered when this handler returns. But the way we install this handler,
177 no signal is blocked, and the re-raised signal is delivered already
179 uninstall_handlers ();
184 /* Install the handlers. */
186 install_handlers (void)
189 struct sigaction action
;
191 action
.sa_handler
= &fatal_signal_handler
;
192 /* If we get a fatal signal while executing fatal_signal_handler, enter
193 fatal_signal_handler recursively, since it is reentrant. Hence no
195 action
.sa_flags
= SA_NODEFER
;
196 sigemptyset (&action
.sa_mask
);
197 for (i
= 0; i
< num_fatal_signals
; i
++)
198 if (fatal_signals
[i
] >= 0)
200 int sig
= fatal_signals
[i
];
202 if (!(sig
< sizeof (saved_sigactions
) / sizeof (saved_sigactions
[0])))
204 sigaction (sig
, &action
, &saved_sigactions
[sig
]);
209 /* Lock that makes at_fatal_signal multi-thread safe. */
210 gl_lock_define_initialized (static, at_fatal_signal_lock
)
212 /* Register a cleanup function to be executed when a catchable fatal signal
215 at_fatal_signal (action_t action
)
217 bool mt
= gl_multithreaded ();
219 if (mt
) gl_lock_lock (at_fatal_signal_lock
);
221 static bool cleanup_initialized
= false;
222 if (!cleanup_initialized
)
224 init_fatal_signals ();
226 cleanup_initialized
= true;
229 if (actions_count
== actions_allocated
)
231 /* Extend the actions array. Note that we cannot use xrealloc(),
232 because then the cleanup() function could access an already
233 deallocated array. */
234 actions_entry_t
*old_actions
= actions
;
235 size_t old_actions_allocated
= actions_allocated
;
236 size_t new_actions_allocated
= 2 * actions_allocated
;
237 actions_entry_t
*new_actions
=
238 XNMALLOC (new_actions_allocated
, actions_entry_t
);
241 /* Don't use memcpy() here, because memcpy takes non-volatile arguments
242 and is therefore not guaranteed to complete all memory stores before
243 the next statement. */
244 for (k
= 0; k
< old_actions_allocated
; k
++)
245 new_actions
[k
] = old_actions
[k
];
246 actions
= new_actions
;
247 actions_allocated
= new_actions_allocated
;
248 /* Now we can free the old actions array. */
249 /* No, we can't do that. If fatal_signal_handler is running in a
250 different thread and has already fetched the actions pointer (getting
251 old_actions) but not yet accessed its n-th element, that thread may
252 crash when accessing an element of the already freed old_actions
255 if (old_actions
!= static_actions
)
259 /* The two uses of 'volatile' in the types above (and ISO C 99 section
260 5.1.2.3.(5)) ensure that we increment the actions_count only after
261 the new action has been written to the memory location
262 actions[actions_count]. */
263 actions
[actions_count
].action
= action
;
266 if (mt
) gl_lock_unlock (at_fatal_signal_lock
);
270 /* ========================================================================= */
273 static sigset_t fatal_signal_set
;
276 do_init_fatal_signal_set (void)
280 init_fatal_signals ();
282 sigemptyset (&fatal_signal_set
);
283 for (i
= 0; i
< num_fatal_signals
; i
++)
284 if (fatal_signals
[i
] >= 0)
285 sigaddset (&fatal_signal_set
, fatal_signals
[i
]);
288 /* Ensure that do_init_fatal_signal_set is called once only. */
289 gl_once_define(static, fatal_signal_set_once
)
292 init_fatal_signal_set (void)
294 gl_once (fatal_signal_set_once
, do_init_fatal_signal_set
);
297 /* Lock and counter that allow block_fatal_signals/unblock_fatal_signals pairs
298 to occur in different threads and even overlap in time. */
299 gl_lock_define_initialized (static, fatal_signals_block_lock
)
300 static unsigned int fatal_signals_block_counter
= 0;
302 /* Temporarily delay the catchable fatal signals. */
304 block_fatal_signals (void)
306 bool mt
= gl_multithreaded ();
308 if (mt
) gl_lock_lock (fatal_signals_block_lock
);
310 if (fatal_signals_block_counter
++ == 0)
312 init_fatal_signal_set ();
313 sigprocmask (SIG_BLOCK
, &fatal_signal_set
, NULL
);
316 if (mt
) gl_lock_unlock (fatal_signals_block_lock
);
319 /* Stop delaying the catchable fatal signals. */
321 unblock_fatal_signals (void)
323 bool mt
= gl_multithreaded ();
325 if (mt
) gl_lock_lock (fatal_signals_block_lock
);
327 if (fatal_signals_block_counter
== 0)
328 /* There are more calls to unblock_fatal_signals() than to
329 block_fatal_signals(). */
331 if (--fatal_signals_block_counter
== 0)
333 init_fatal_signal_set ();
334 sigprocmask (SIG_UNBLOCK
, &fatal_signal_set
, NULL
);
337 if (mt
) gl_lock_unlock (fatal_signals_block_lock
);
342 get_fatal_signals (int signals
[64])
344 init_fatal_signal_set ();
350 for (i
= 0; i
< num_fatal_signals
; i
++)
351 if (fatal_signals
[i
] >= 0)
352 *p
++ = fatal_signals
[i
];
358 get_fatal_signal_set (void)
360 init_fatal_signal_set ();
361 return &fatal_signal_set
;