• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "Python.h"
2 #include "pythread.h"
3 #include <signal.h>
4 #include <object.h>
5 #include <frameobject.h>
6 #include <signal.h>
7 #if defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK)
8 #  include <pthread.h>
9 #endif
10 #ifdef MS_WINDOWS
11 #  include <windows.h>
12 #endif
13 #ifdef HAVE_SYS_RESOURCE_H
14 #  include <sys/resource.h>
15 #endif
16 
17 /* Allocate at maximum 100 MiB of the stack to raise the stack overflow */
18 #define STACK_OVERFLOW_MAX_SIZE (100 * 1024 * 1024)
19 
20 #define FAULTHANDLER_LATER
21 
22 #ifndef MS_WINDOWS
23    /* register() is useless on Windows, because only SIGSEGV, SIGABRT and
24       SIGILL can be handled by the process, and these signals can only be used
25       with enable(), not using register() */
26 #  define FAULTHANDLER_USER
27 #endif
28 
29 #define PUTS(fd, str) _Py_write_noraise(fd, str, strlen(str))
30 
31 _Py_IDENTIFIER(enable);
32 _Py_IDENTIFIER(fileno);
33 _Py_IDENTIFIER(flush);
34 _Py_IDENTIFIER(stderr);
35 
36 #ifdef HAVE_SIGACTION
37 typedef struct sigaction _Py_sighandler_t;
38 #else
39 typedef PyOS_sighandler_t _Py_sighandler_t;
40 #endif
41 
42 typedef struct {
43     int signum;
44     int enabled;
45     const char* name;
46     _Py_sighandler_t previous;
47     int all_threads;
48 } fault_handler_t;
49 
50 static struct {
51     int enabled;
52     PyObject *file;
53     int fd;
54     int all_threads;
55     PyInterpreterState *interp;
56 #ifdef MS_WINDOWS
57     void *exc_handler;
58 #endif
59 } fatal_error = {0, NULL, -1, 0};
60 
61 #ifdef FAULTHANDLER_LATER
62 static struct {
63     PyObject *file;
64     int fd;
65     PY_TIMEOUT_T timeout_us;   /* timeout in microseconds */
66     int repeat;
67     PyInterpreterState *interp;
68     int exit;
69     char *header;
70     size_t header_len;
71     /* The main thread always holds this lock. It is only released when
72        faulthandler_thread() is interrupted before this thread exits, or at
73        Python exit. */
74     PyThread_type_lock cancel_event;
75     /* released by child thread when joined */
76     PyThread_type_lock running;
77 } thread;
78 #endif
79 
80 #ifdef FAULTHANDLER_USER
81 typedef struct {
82     int enabled;
83     PyObject *file;
84     int fd;
85     int all_threads;
86     int chain;
87     _Py_sighandler_t previous;
88     PyInterpreterState *interp;
89 } user_signal_t;
90 
91 static user_signal_t *user_signals;
92 
93 /* the following macros come from Python: Modules/signalmodule.c */
94 #ifndef NSIG
95 # if defined(_NSIG)
96 #  define NSIG _NSIG            /* For BSD/SysV */
97 # elif defined(_SIGMAX)
98 #  define NSIG (_SIGMAX + 1)    /* For QNX */
99 # elif defined(SIGMAX)
100 #  define NSIG (SIGMAX + 1)     /* For djgpp */
101 # else
102 #  define NSIG 64               /* Use a reasonable default value */
103 # endif
104 #endif
105 
106 static void faulthandler_user(int signum);
107 #endif /* FAULTHANDLER_USER */
108 
109 
110 static fault_handler_t faulthandler_handlers[] = {
111 #ifdef SIGBUS
112     {SIGBUS, 0, "Bus error", },
113 #endif
114 #ifdef SIGILL
115     {SIGILL, 0, "Illegal instruction", },
116 #endif
117     {SIGFPE, 0, "Floating point exception", },
118     {SIGABRT, 0, "Aborted", },
119     /* define SIGSEGV at the end to make it the default choice if searching the
120        handler fails in faulthandler_fatal_error() */
121     {SIGSEGV, 0, "Segmentation fault", }
122 };
123 static const size_t faulthandler_nsignals = \
124     Py_ARRAY_LENGTH(faulthandler_handlers);
125 
126 #ifdef HAVE_SIGALTSTACK
127 static stack_t stack;
128 static stack_t old_stack;
129 #endif
130 
131 
132 /* Get the file descriptor of a file by calling its fileno() method and then
133    call its flush() method.
134 
135    If file is NULL or Py_None, use sys.stderr as the new file.
136    If file is an integer, it will be treated as file descriptor.
137 
138    On success, return the file descriptor and write the new file into *file_ptr.
139    On error, return -1. */
140 
141 static int
faulthandler_get_fileno(PyObject ** file_ptr)142 faulthandler_get_fileno(PyObject **file_ptr)
143 {
144     PyObject *result;
145     long fd_long;
146     int fd;
147     PyObject *file = *file_ptr;
148 
149     if (file == NULL || file == Py_None) {
150         file = _PySys_GetObjectId(&PyId_stderr);
151         if (file == NULL) {
152             PyErr_SetString(PyExc_RuntimeError, "unable to get sys.stderr");
153             return -1;
154         }
155         if (file == Py_None) {
156             PyErr_SetString(PyExc_RuntimeError, "sys.stderr is None");
157             return -1;
158         }
159     }
160     else if (PyLong_Check(file)) {
161         fd = _PyLong_AsInt(file);
162         if (fd == -1 && PyErr_Occurred())
163             return -1;
164         if (fd < 0) {
165             PyErr_SetString(PyExc_ValueError,
166                             "file is not a valid file descripter");
167             return -1;
168         }
169         *file_ptr = NULL;
170         return fd;
171     }
172 
173     result = _PyObject_CallMethodId(file, &PyId_fileno, NULL);
174     if (result == NULL)
175         return -1;
176 
177     fd = -1;
178     if (PyLong_Check(result)) {
179         fd_long = PyLong_AsLong(result);
180         if (0 <= fd_long && fd_long < INT_MAX)
181             fd = (int)fd_long;
182     }
183     Py_DECREF(result);
184 
185     if (fd == -1) {
186         PyErr_SetString(PyExc_RuntimeError,
187                         "file.fileno() is not a valid file descriptor");
188         return -1;
189     }
190 
191     result = _PyObject_CallMethodId(file, &PyId_flush, NULL);
192     if (result != NULL)
193         Py_DECREF(result);
194     else {
195         /* ignore flush() error */
196         PyErr_Clear();
197     }
198     *file_ptr = file;
199     return fd;
200 }
201 
202 /* Get the state of the current thread: only call this function if the current
203    thread holds the GIL. Raise an exception on error. */
204 static PyThreadState*
get_thread_state(void)205 get_thread_state(void)
206 {
207     PyThreadState *tstate = _PyThreadState_UncheckedGet();
208     if (tstate == NULL) {
209         /* just in case but very unlikely... */
210         PyErr_SetString(PyExc_RuntimeError,
211                         "unable to get the current thread state");
212         return NULL;
213     }
214     return tstate;
215 }
216 
217 static void
faulthandler_dump_traceback(int fd,int all_threads,PyInterpreterState * interp)218 faulthandler_dump_traceback(int fd, int all_threads,
219                             PyInterpreterState *interp)
220 {
221     static volatile int reentrant = 0;
222     PyThreadState *tstate;
223 
224     if (reentrant)
225         return;
226 
227     reentrant = 1;
228 
229     /* SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals and
230        are thus delivered to the thread that caused the fault. Get the Python
231        thread state of the current thread.
232 
233        PyThreadState_Get() doesn't give the state of the thread that caused the
234        fault if the thread released the GIL, and so this function cannot be
235        used. Read the thread specific storage (TSS) instead: call
236        PyGILState_GetThisThreadState(). */
237     tstate = PyGILState_GetThisThreadState();
238 
239     if (all_threads) {
240         (void)_Py_DumpTracebackThreads(fd, NULL, tstate);
241     }
242     else {
243         if (tstate != NULL)
244             _Py_DumpTraceback(fd, tstate);
245     }
246 
247     reentrant = 0;
248 }
249 
250 static PyObject*
faulthandler_dump_traceback_py(PyObject * self,PyObject * args,PyObject * kwargs)251 faulthandler_dump_traceback_py(PyObject *self,
252                                PyObject *args, PyObject *kwargs)
253 {
254     static char *kwlist[] = {"file", "all_threads", NULL};
255     PyObject *file = NULL;
256     int all_threads = 1;
257     PyThreadState *tstate;
258     const char *errmsg;
259     int fd;
260 
261     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
262         "|Oi:dump_traceback", kwlist,
263         &file, &all_threads))
264         return NULL;
265 
266     fd = faulthandler_get_fileno(&file);
267     if (fd < 0)
268         return NULL;
269 
270     tstate = get_thread_state();
271     if (tstate == NULL)
272         return NULL;
273 
274     if (all_threads) {
275         errmsg = _Py_DumpTracebackThreads(fd, NULL, tstate);
276         if (errmsg != NULL) {
277             PyErr_SetString(PyExc_RuntimeError, errmsg);
278             return NULL;
279         }
280     }
281     else {
282         _Py_DumpTraceback(fd, tstate);
283     }
284 
285     if (PyErr_CheckSignals())
286         return NULL;
287 
288     Py_RETURN_NONE;
289 }
290 
291 static void
faulthandler_disable_fatal_handler(fault_handler_t * handler)292 faulthandler_disable_fatal_handler(fault_handler_t *handler)
293 {
294     if (!handler->enabled)
295         return;
296     handler->enabled = 0;
297 #ifdef HAVE_SIGACTION
298     (void)sigaction(handler->signum, &handler->previous, NULL);
299 #else
300     (void)signal(handler->signum, handler->previous);
301 #endif
302 }
303 
304 
305 /* Handler for SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals.
306 
307    Display the current Python traceback, restore the previous handler and call
308    the previous handler.
309 
310    On Windows, don't explicitly call the previous handler, because the Windows
311    signal handler would not be called (for an unknown reason). The execution of
312    the program continues at faulthandler_fatal_error() exit, but the same
313    instruction will raise the same fault (signal), and so the previous handler
314    will be called.
315 
316    This function is signal-safe and should only call signal-safe functions. */
317 
318 static void
faulthandler_fatal_error(int signum)319 faulthandler_fatal_error(int signum)
320 {
321     const int fd = fatal_error.fd;
322     size_t i;
323     fault_handler_t *handler = NULL;
324     int save_errno = errno;
325 
326     if (!fatal_error.enabled)
327         return;
328 
329     for (i=0; i < faulthandler_nsignals; i++) {
330         handler = &faulthandler_handlers[i];
331         if (handler->signum == signum)
332             break;
333     }
334     if (handler == NULL) {
335         /* faulthandler_nsignals == 0 (unlikely) */
336         return;
337     }
338 
339     /* restore the previous handler */
340     faulthandler_disable_fatal_handler(handler);
341 
342     PUTS(fd, "Fatal Python error: ");
343     PUTS(fd, handler->name);
344     PUTS(fd, "\n\n");
345 
346     faulthandler_dump_traceback(fd, fatal_error.all_threads,
347                                 fatal_error.interp);
348 
349     errno = save_errno;
350 #ifdef MS_WINDOWS
351     if (signum == SIGSEGV) {
352         /* don't explicitly call the previous handler for SIGSEGV in this signal
353            handler, because the Windows signal handler would not be called */
354         return;
355     }
356 #endif
357     /* call the previous signal handler: it is called immediately if we use
358        sigaction() thanks to SA_NODEFER flag, otherwise it is deferred */
359     raise(signum);
360 }
361 
362 #ifdef MS_WINDOWS
363 static int
faulthandler_ignore_exception(DWORD code)364 faulthandler_ignore_exception(DWORD code)
365 {
366     /* bpo-30557: ignore exceptions which are not errors */
367     if (!(code & 0x80000000)) {
368         return 1;
369     }
370     /* bpo-31701: ignore MSC and COM exceptions
371        E0000000 + code */
372     if (code == 0xE06D7363 /* MSC exception ("Emsc") */
373         || code == 0xE0434352 /* COM Callable Runtime exception ("ECCR") */) {
374         return 1;
375     }
376     /* Interesting exception: log it with the Python traceback */
377     return 0;
378 }
379 
380 static LONG WINAPI
faulthandler_exc_handler(struct _EXCEPTION_POINTERS * exc_info)381 faulthandler_exc_handler(struct _EXCEPTION_POINTERS *exc_info)
382 {
383     const int fd = fatal_error.fd;
384     DWORD code = exc_info->ExceptionRecord->ExceptionCode;
385     DWORD flags = exc_info->ExceptionRecord->ExceptionFlags;
386 
387     if (faulthandler_ignore_exception(code)) {
388         /* ignore the exception: call the next exception handler */
389         return EXCEPTION_CONTINUE_SEARCH;
390     }
391 
392     PUTS(fd, "Windows fatal exception: ");
393     switch (code)
394     {
395     /* only format most common errors */
396     case EXCEPTION_ACCESS_VIOLATION: PUTS(fd, "access violation"); break;
397     case EXCEPTION_FLT_DIVIDE_BY_ZERO: PUTS(fd, "float divide by zero"); break;
398     case EXCEPTION_FLT_OVERFLOW: PUTS(fd, "float overflow"); break;
399     case EXCEPTION_INT_DIVIDE_BY_ZERO: PUTS(fd, "int divide by zero"); break;
400     case EXCEPTION_INT_OVERFLOW: PUTS(fd, "integer overflow"); break;
401     case EXCEPTION_IN_PAGE_ERROR: PUTS(fd, "page error"); break;
402     case EXCEPTION_STACK_OVERFLOW: PUTS(fd, "stack overflow"); break;
403     default:
404         PUTS(fd, "code 0x");
405         _Py_DumpHexadecimal(fd, code, 8);
406     }
407     PUTS(fd, "\n\n");
408 
409     if (code == EXCEPTION_ACCESS_VIOLATION) {
410         /* disable signal handler for SIGSEGV */
411         for (size_t i=0; i < faulthandler_nsignals; i++) {
412             fault_handler_t *handler = &faulthandler_handlers[i];
413             if (handler->signum == SIGSEGV) {
414                 faulthandler_disable_fatal_handler(handler);
415                 break;
416             }
417         }
418     }
419 
420     faulthandler_dump_traceback(fd, fatal_error.all_threads,
421                                 fatal_error.interp);
422 
423     /* call the next exception handler */
424     return EXCEPTION_CONTINUE_SEARCH;
425 }
426 #endif
427 
428 /* Install the handler for fatal signals, faulthandler_fatal_error(). */
429 
430 static int
faulthandler_enable(void)431 faulthandler_enable(void)
432 {
433     if (fatal_error.enabled) {
434         return 0;
435     }
436     fatal_error.enabled = 1;
437 
438     for (size_t i=0; i < faulthandler_nsignals; i++) {
439         fault_handler_t *handler;
440 #ifdef HAVE_SIGACTION
441         struct sigaction action;
442 #endif
443         int err;
444 
445         handler = &faulthandler_handlers[i];
446         assert(!handler->enabled);
447 #ifdef HAVE_SIGACTION
448         action.sa_handler = faulthandler_fatal_error;
449         sigemptyset(&action.sa_mask);
450         /* Do not prevent the signal from being received from within
451            its own signal handler */
452         action.sa_flags = SA_NODEFER;
453 #ifdef HAVE_SIGALTSTACK
454         if (stack.ss_sp != NULL) {
455             /* Call the signal handler on an alternate signal stack
456                provided by sigaltstack() */
457             action.sa_flags |= SA_ONSTACK;
458         }
459 #endif
460         err = sigaction(handler->signum, &action, &handler->previous);
461 #else
462         handler->previous = signal(handler->signum,
463                 faulthandler_fatal_error);
464         err = (handler->previous == SIG_ERR);
465 #endif
466         if (err) {
467             PyErr_SetFromErrno(PyExc_RuntimeError);
468             return -1;
469         }
470 
471         handler->enabled = 1;
472     }
473 
474 #ifdef MS_WINDOWS
475     assert(fatal_error.exc_handler == NULL);
476     fatal_error.exc_handler = AddVectoredExceptionHandler(1, faulthandler_exc_handler);
477 #endif
478     return 0;
479 }
480 
481 static PyObject*
faulthandler_py_enable(PyObject * self,PyObject * args,PyObject * kwargs)482 faulthandler_py_enable(PyObject *self, PyObject *args, PyObject *kwargs)
483 {
484     static char *kwlist[] = {"file", "all_threads", NULL};
485     PyObject *file = NULL;
486     int all_threads = 1;
487     int fd;
488     PyThreadState *tstate;
489 
490     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
491         "|Oi:enable", kwlist, &file, &all_threads))
492         return NULL;
493 
494     fd = faulthandler_get_fileno(&file);
495     if (fd < 0)
496         return NULL;
497 
498     tstate = get_thread_state();
499     if (tstate == NULL)
500         return NULL;
501 
502     Py_XINCREF(file);
503     Py_XSETREF(fatal_error.file, file);
504     fatal_error.fd = fd;
505     fatal_error.all_threads = all_threads;
506     fatal_error.interp = tstate->interp;
507 
508     if (faulthandler_enable() < 0) {
509         return NULL;
510     }
511 
512     Py_RETURN_NONE;
513 }
514 
515 static void
faulthandler_disable(void)516 faulthandler_disable(void)
517 {
518     if (fatal_error.enabled) {
519         fatal_error.enabled = 0;
520         for (size_t i=0; i < faulthandler_nsignals; i++) {
521             fault_handler_t *handler;
522             handler = &faulthandler_handlers[i];
523             faulthandler_disable_fatal_handler(handler);
524         }
525     }
526 #ifdef MS_WINDOWS
527     if (fatal_error.exc_handler != NULL) {
528         RemoveVectoredExceptionHandler(fatal_error.exc_handler);
529         fatal_error.exc_handler = NULL;
530     }
531 #endif
532     Py_CLEAR(fatal_error.file);
533 }
534 
535 static PyObject*
faulthandler_disable_py(PyObject * self)536 faulthandler_disable_py(PyObject *self)
537 {
538     if (!fatal_error.enabled) {
539         Py_RETURN_FALSE;
540     }
541     faulthandler_disable();
542     Py_RETURN_TRUE;
543 }
544 
545 static PyObject*
faulthandler_is_enabled(PyObject * self)546 faulthandler_is_enabled(PyObject *self)
547 {
548     return PyBool_FromLong(fatal_error.enabled);
549 }
550 
551 #ifdef FAULTHANDLER_LATER
552 
553 static void
faulthandler_thread(void * unused)554 faulthandler_thread(void *unused)
555 {
556     PyLockStatus st;
557     const char* errmsg;
558     int ok;
559 #if defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK)
560     sigset_t set;
561 
562     /* we don't want to receive any signal */
563     sigfillset(&set);
564     pthread_sigmask(SIG_SETMASK, &set, NULL);
565 #endif
566 
567     do {
568         st = PyThread_acquire_lock_timed(thread.cancel_event,
569                                          thread.timeout_us, 0);
570         if (st == PY_LOCK_ACQUIRED) {
571             PyThread_release_lock(thread.cancel_event);
572             break;
573         }
574         /* Timeout => dump traceback */
575         assert(st == PY_LOCK_FAILURE);
576 
577         _Py_write_noraise(thread.fd, thread.header, (int)thread.header_len);
578 
579         errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, NULL);
580         ok = (errmsg == NULL);
581 
582         if (thread.exit)
583             _exit(1);
584     } while (ok && thread.repeat);
585 
586     /* The only way out */
587     PyThread_release_lock(thread.running);
588 }
589 
590 static void
cancel_dump_traceback_later(void)591 cancel_dump_traceback_later(void)
592 {
593     /* Notify cancellation */
594     PyThread_release_lock(thread.cancel_event);
595 
596     /* Wait for thread to join */
597     PyThread_acquire_lock(thread.running, 1);
598     PyThread_release_lock(thread.running);
599 
600     /* The main thread should always hold the cancel_event lock */
601     PyThread_acquire_lock(thread.cancel_event, 1);
602 
603     Py_CLEAR(thread.file);
604     if (thread.header) {
605         PyMem_Free(thread.header);
606         thread.header = NULL;
607     }
608 }
609 
610 #define SEC_TO_US (1000 * 1000)
611 
612 static char*
format_timeout(_PyTime_t us)613 format_timeout(_PyTime_t us)
614 {
615     unsigned long sec, min, hour;
616     char buffer[100];
617 
618     /* the downcast is safe: the caller check that 0 < us <= LONG_MAX */
619     sec = (unsigned long)(us / SEC_TO_US);
620     us %= SEC_TO_US;
621 
622     min = sec / 60;
623     sec %= 60;
624     hour = min / 60;
625     min %= 60;
626 
627     if (us != 0) {
628         PyOS_snprintf(buffer, sizeof(buffer),
629                       "Timeout (%lu:%02lu:%02lu.%06u)!\n",
630                       hour, min, sec, (unsigned int)us);
631     }
632     else {
633         PyOS_snprintf(buffer, sizeof(buffer),
634                       "Timeout (%lu:%02lu:%02lu)!\n",
635                       hour, min, sec);
636     }
637     return _PyMem_Strdup(buffer);
638 }
639 
640 static PyObject*
faulthandler_dump_traceback_later(PyObject * self,PyObject * args,PyObject * kwargs)641 faulthandler_dump_traceback_later(PyObject *self,
642                                    PyObject *args, PyObject *kwargs)
643 {
644     static char *kwlist[] = {"timeout", "repeat", "file", "exit", NULL};
645     PyObject *timeout_obj;
646     _PyTime_t timeout, timeout_us;
647     int repeat = 0;
648     PyObject *file = NULL;
649     int fd;
650     int exit = 0;
651     PyThreadState *tstate;
652     char *header;
653     size_t header_len;
654 
655     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
656         "O|iOi:dump_traceback_later", kwlist,
657         &timeout_obj, &repeat, &file, &exit))
658         return NULL;
659 
660     if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
661                                   _PyTime_ROUND_TIMEOUT) < 0) {
662         return NULL;
663     }
664     timeout_us = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_TIMEOUT);
665     if (timeout_us <= 0) {
666         PyErr_SetString(PyExc_ValueError, "timeout must be greater than 0");
667         return NULL;
668     }
669     /* Limit to LONG_MAX seconds for format_timeout() */
670     if (timeout_us >= PY_TIMEOUT_MAX || timeout_us / SEC_TO_US >= LONG_MAX) {
671         PyErr_SetString(PyExc_OverflowError,
672                         "timeout value is too large");
673         return NULL;
674     }
675 
676     tstate = get_thread_state();
677     if (tstate == NULL)
678         return NULL;
679 
680     fd = faulthandler_get_fileno(&file);
681     if (fd < 0)
682         return NULL;
683 
684     /* format the timeout */
685     header = format_timeout(timeout_us);
686     if (header == NULL)
687         return PyErr_NoMemory();
688     header_len = strlen(header);
689 
690     /* Cancel previous thread, if running */
691     cancel_dump_traceback_later();
692 
693     Py_XINCREF(file);
694     Py_XSETREF(thread.file, file);
695     thread.fd = fd;
696     /* the downcast is safe: we check that 0 < timeout_us < PY_TIMEOUT_MAX */
697     thread.timeout_us = (PY_TIMEOUT_T)timeout_us;
698     thread.repeat = repeat;
699     thread.interp = tstate->interp;
700     thread.exit = exit;
701     thread.header = header;
702     thread.header_len = header_len;
703 
704     /* Arm these locks to serve as events when released */
705     PyThread_acquire_lock(thread.running, 1);
706 
707     if (PyThread_start_new_thread(faulthandler_thread, NULL) == PYTHREAD_INVALID_THREAD_ID) {
708         PyThread_release_lock(thread.running);
709         Py_CLEAR(thread.file);
710         PyMem_Free(header);
711         thread.header = NULL;
712         PyErr_SetString(PyExc_RuntimeError,
713                         "unable to start watchdog thread");
714         return NULL;
715     }
716 
717     Py_RETURN_NONE;
718 }
719 
720 static PyObject*
faulthandler_cancel_dump_traceback_later_py(PyObject * self)721 faulthandler_cancel_dump_traceback_later_py(PyObject *self)
722 {
723     cancel_dump_traceback_later();
724     Py_RETURN_NONE;
725 }
726 #endif  /* FAULTHANDLER_LATER */
727 
728 #ifdef FAULTHANDLER_USER
729 static int
faulthandler_register(int signum,int chain,_Py_sighandler_t * p_previous)730 faulthandler_register(int signum, int chain, _Py_sighandler_t *p_previous)
731 {
732 #ifdef HAVE_SIGACTION
733     struct sigaction action;
734     action.sa_handler = faulthandler_user;
735     sigemptyset(&action.sa_mask);
736     /* if the signal is received while the kernel is executing a system
737        call, try to restart the system call instead of interrupting it and
738        return EINTR. */
739     action.sa_flags = SA_RESTART;
740     if (chain) {
741         /* do not prevent the signal from being received from within its
742            own signal handler */
743         action.sa_flags = SA_NODEFER;
744     }
745 #ifdef HAVE_SIGALTSTACK
746     if (stack.ss_sp != NULL) {
747         /* Call the signal handler on an alternate signal stack
748            provided by sigaltstack() */
749         action.sa_flags |= SA_ONSTACK;
750     }
751 #endif
752     return sigaction(signum, &action, p_previous);
753 #else
754     _Py_sighandler_t previous;
755     previous = signal(signum, faulthandler_user);
756     if (p_previous != NULL)
757         *p_previous = previous;
758     return (previous == SIG_ERR);
759 #endif
760 }
761 
762 /* Handler of user signals (e.g. SIGUSR1).
763 
764    Dump the traceback of the current thread, or of all threads if
765    thread.all_threads is true.
766 
767    This function is signal safe and should only call signal safe functions. */
768 
769 static void
faulthandler_user(int signum)770 faulthandler_user(int signum)
771 {
772     user_signal_t *user;
773     int save_errno = errno;
774 
775     user = &user_signals[signum];
776     if (!user->enabled)
777         return;
778 
779     faulthandler_dump_traceback(user->fd, user->all_threads, user->interp);
780 
781 #ifdef HAVE_SIGACTION
782     if (user->chain) {
783         (void)sigaction(signum, &user->previous, NULL);
784         errno = save_errno;
785 
786         /* call the previous signal handler */
787         raise(signum);
788 
789         save_errno = errno;
790         (void)faulthandler_register(signum, user->chain, NULL);
791         errno = save_errno;
792     }
793 #else
794     if (user->chain) {
795         errno = save_errno;
796         /* call the previous signal handler */
797         user->previous(signum);
798     }
799 #endif
800 }
801 
802 static int
check_signum(int signum)803 check_signum(int signum)
804 {
805     for (size_t i=0; i < faulthandler_nsignals; i++) {
806         if (faulthandler_handlers[i].signum == signum) {
807             PyErr_Format(PyExc_RuntimeError,
808                          "signal %i cannot be registered, "
809                          "use enable() instead",
810                          signum);
811             return 0;
812         }
813     }
814     if (signum < 1 || NSIG <= signum) {
815         PyErr_SetString(PyExc_ValueError, "signal number out of range");
816         return 0;
817     }
818     return 1;
819 }
820 
821 static PyObject*
faulthandler_register_py(PyObject * self,PyObject * args,PyObject * kwargs)822 faulthandler_register_py(PyObject *self,
823                          PyObject *args, PyObject *kwargs)
824 {
825     static char *kwlist[] = {"signum", "file", "all_threads", "chain", NULL};
826     int signum;
827     PyObject *file = NULL;
828     int all_threads = 1;
829     int chain = 0;
830     int fd;
831     user_signal_t *user;
832     _Py_sighandler_t previous;
833     PyThreadState *tstate;
834     int err;
835 
836     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
837         "i|Oii:register", kwlist,
838         &signum, &file, &all_threads, &chain))
839         return NULL;
840 
841     if (!check_signum(signum))
842         return NULL;
843 
844     tstate = get_thread_state();
845     if (tstate == NULL)
846         return NULL;
847 
848     fd = faulthandler_get_fileno(&file);
849     if (fd < 0)
850         return NULL;
851 
852     if (user_signals == NULL) {
853         user_signals = PyMem_Malloc(NSIG * sizeof(user_signal_t));
854         if (user_signals == NULL)
855             return PyErr_NoMemory();
856         memset(user_signals, 0, NSIG * sizeof(user_signal_t));
857     }
858     user = &user_signals[signum];
859 
860     if (!user->enabled) {
861         err = faulthandler_register(signum, chain, &previous);
862         if (err) {
863             PyErr_SetFromErrno(PyExc_OSError);
864             return NULL;
865         }
866 
867         user->previous = previous;
868     }
869 
870     Py_XINCREF(file);
871     Py_XSETREF(user->file, file);
872     user->fd = fd;
873     user->all_threads = all_threads;
874     user->chain = chain;
875     user->interp = tstate->interp;
876     user->enabled = 1;
877 
878     Py_RETURN_NONE;
879 }
880 
881 static int
faulthandler_unregister(user_signal_t * user,int signum)882 faulthandler_unregister(user_signal_t *user, int signum)
883 {
884     if (!user->enabled)
885         return 0;
886     user->enabled = 0;
887 #ifdef HAVE_SIGACTION
888     (void)sigaction(signum, &user->previous, NULL);
889 #else
890     (void)signal(signum, user->previous);
891 #endif
892     Py_CLEAR(user->file);
893     user->fd = -1;
894     return 1;
895 }
896 
897 static PyObject*
faulthandler_unregister_py(PyObject * self,PyObject * args)898 faulthandler_unregister_py(PyObject *self, PyObject *args)
899 {
900     int signum;
901     user_signal_t *user;
902     int change;
903 
904     if (!PyArg_ParseTuple(args, "i:unregister", &signum))
905         return NULL;
906 
907     if (!check_signum(signum))
908         return NULL;
909 
910     if (user_signals == NULL)
911         Py_RETURN_FALSE;
912 
913     user = &user_signals[signum];
914     change = faulthandler_unregister(user, signum);
915     return PyBool_FromLong(change);
916 }
917 #endif   /* FAULTHANDLER_USER */
918 
919 
920 static void
faulthandler_suppress_crash_report(void)921 faulthandler_suppress_crash_report(void)
922 {
923 #ifdef MS_WINDOWS
924     UINT mode;
925 
926     /* Configure Windows to not display the Windows Error Reporting dialog */
927     mode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
928     SetErrorMode(mode | SEM_NOGPFAULTERRORBOX);
929 #endif
930 
931 #ifdef HAVE_SYS_RESOURCE_H
932     struct rlimit rl;
933 
934     /* Disable creation of core dump */
935     if (getrlimit(RLIMIT_CORE, &rl) == 0) {
936         rl.rlim_cur = 0;
937         setrlimit(RLIMIT_CORE, &rl);
938     }
939 #endif
940 
941 #ifdef _MSC_VER
942     /* Visual Studio: configure abort() to not display an error message nor
943        open a popup asking to report the fault. */
944     _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
945 #endif
946 }
947 
948 static PyObject *
faulthandler_read_null(PyObject * self,PyObject * args)949 faulthandler_read_null(PyObject *self, PyObject *args)
950 {
951     volatile int *x;
952     volatile int y;
953 
954     faulthandler_suppress_crash_report();
955     x = NULL;
956     y = *x;
957     return PyLong_FromLong(y);
958 
959 }
960 
961 static void
faulthandler_raise_sigsegv(void)962 faulthandler_raise_sigsegv(void)
963 {
964     faulthandler_suppress_crash_report();
965 #if defined(MS_WINDOWS)
966     /* For SIGSEGV, faulthandler_fatal_error() restores the previous signal
967        handler and then gives back the execution flow to the program (without
968        explicitly calling the previous error handler). In a normal case, the
969        SIGSEGV was raised by the kernel because of a fault, and so if the
970        program retries to execute the same instruction, the fault will be
971        raised again.
972 
973        Here the fault is simulated by a fake SIGSEGV signal raised by the
974        application. We have to raise SIGSEGV at lease twice: once for
975        faulthandler_fatal_error(), and one more time for the previous signal
976        handler. */
977     while(1)
978         raise(SIGSEGV);
979 #else
980     raise(SIGSEGV);
981 #endif
982 }
983 
984 static PyObject *
faulthandler_sigsegv(PyObject * self,PyObject * args)985 faulthandler_sigsegv(PyObject *self, PyObject *args)
986 {
987     int release_gil = 0;
988     if (!PyArg_ParseTuple(args, "|i:_sigsegv", &release_gil))
989         return NULL;
990 
991     if (release_gil) {
992         Py_BEGIN_ALLOW_THREADS
993         faulthandler_raise_sigsegv();
994         Py_END_ALLOW_THREADS
995     } else {
996         faulthandler_raise_sigsegv();
997     }
998     Py_RETURN_NONE;
999 }
1000 
1001 static void
faulthandler_fatal_error_thread(void * plock)1002 faulthandler_fatal_error_thread(void *plock)
1003 {
1004 #ifndef __clang__
1005     PyThread_type_lock *lock = (PyThread_type_lock *)plock;
1006 #endif
1007 
1008     Py_FatalError("in new thread");
1009 
1010 #ifndef __clang__
1011     /* Issue #28152: Py_FatalError() is declared with
1012        __attribute__((__noreturn__)).  GCC emits a warning without
1013        "PyThread_release_lock()" (compiler bug?), but Clang is smarter and
1014        emits a warning on the return. */
1015 
1016     /* notify the caller that we are done */
1017     PyThread_release_lock(lock);
1018 #endif
1019 }
1020 
1021 static PyObject *
faulthandler_fatal_error_c_thread(PyObject * self,PyObject * args)1022 faulthandler_fatal_error_c_thread(PyObject *self, PyObject *args)
1023 {
1024     long thread;
1025     PyThread_type_lock lock;
1026 
1027     faulthandler_suppress_crash_report();
1028 
1029     lock = PyThread_allocate_lock();
1030     if (lock == NULL)
1031         return PyErr_NoMemory();
1032 
1033     PyThread_acquire_lock(lock, WAIT_LOCK);
1034 
1035     thread = PyThread_start_new_thread(faulthandler_fatal_error_thread, lock);
1036     if (thread == -1) {
1037         PyThread_free_lock(lock);
1038         PyErr_SetString(PyExc_RuntimeError, "unable to start the thread");
1039         return NULL;
1040     }
1041 
1042     /* wait until the thread completes: it will never occur, since Py_FatalError()
1043        exits the process immediately. */
1044     PyThread_acquire_lock(lock, WAIT_LOCK);
1045     PyThread_release_lock(lock);
1046     PyThread_free_lock(lock);
1047 
1048     Py_RETURN_NONE;
1049 }
1050 
1051 static PyObject *
faulthandler_sigfpe(PyObject * self,PyObject * args)1052 faulthandler_sigfpe(PyObject *self, PyObject *args)
1053 {
1054     /* Do an integer division by zero: raise a SIGFPE on Intel CPU, but not on
1055        PowerPC. Use volatile to disable compile-time optimizations. */
1056     volatile int x = 1, y = 0, z;
1057     faulthandler_suppress_crash_report();
1058     z = x / y;
1059     /* If the division by zero didn't raise a SIGFPE (e.g. on PowerPC),
1060        raise it manually. */
1061     raise(SIGFPE);
1062     /* This line is never reached, but we pretend to make something with z
1063        to silence a compiler warning. */
1064     return PyLong_FromLong(z);
1065 }
1066 
1067 static PyObject *
faulthandler_sigabrt(PyObject * self,PyObject * args)1068 faulthandler_sigabrt(PyObject *self, PyObject *args)
1069 {
1070     faulthandler_suppress_crash_report();
1071     abort();
1072     Py_RETURN_NONE;
1073 }
1074 
1075 static PyObject *
faulthandler_fatal_error_py(PyObject * self,PyObject * args)1076 faulthandler_fatal_error_py(PyObject *self, PyObject *args)
1077 {
1078     char *message;
1079     int release_gil = 0;
1080     if (!PyArg_ParseTuple(args, "y|i:fatal_error", &message, &release_gil))
1081         return NULL;
1082     faulthandler_suppress_crash_report();
1083     if (release_gil) {
1084         Py_BEGIN_ALLOW_THREADS
1085         Py_FatalError(message);
1086         Py_END_ALLOW_THREADS
1087     }
1088     else {
1089         Py_FatalError(message);
1090     }
1091     Py_RETURN_NONE;
1092 }
1093 
1094 #if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)
1095 #define FAULTHANDLER_STACK_OVERFLOW
1096 
1097 #ifdef __INTEL_COMPILER
1098    /* Issue #23654: Turn off ICC's tail call optimization for the
1099     * stack_overflow generator. ICC turns the recursive tail call into
1100     * a loop. */
1101 #  pragma intel optimization_level 0
1102 #endif
1103 static
1104 uintptr_t
stack_overflow(uintptr_t min_sp,uintptr_t max_sp,size_t * depth)1105 stack_overflow(uintptr_t min_sp, uintptr_t max_sp, size_t *depth)
1106 {
1107     /* allocate 4096 bytes on the stack at each call */
1108     unsigned char buffer[4096];
1109     uintptr_t sp = (uintptr_t)&buffer;
1110     *depth += 1;
1111     if (sp < min_sp || max_sp < sp)
1112         return sp;
1113     buffer[0] = 1;
1114     buffer[4095] = 0;
1115     return stack_overflow(min_sp, max_sp, depth);
1116 }
1117 
1118 static PyObject *
faulthandler_stack_overflow(PyObject * self)1119 faulthandler_stack_overflow(PyObject *self)
1120 {
1121     size_t depth, size;
1122     uintptr_t sp = (uintptr_t)&depth;
1123     uintptr_t stop;
1124 
1125     faulthandler_suppress_crash_report();
1126     depth = 0;
1127     stop = stack_overflow(sp - STACK_OVERFLOW_MAX_SIZE,
1128                           sp + STACK_OVERFLOW_MAX_SIZE,
1129                           &depth);
1130     if (sp < stop)
1131         size = stop - sp;
1132     else
1133         size = sp - stop;
1134     PyErr_Format(PyExc_RuntimeError,
1135         "unable to raise a stack overflow (allocated %zu bytes "
1136         "on the stack, %zu recursive calls)",
1137         size, depth);
1138     return NULL;
1139 }
1140 #endif   /* defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) */
1141 
1142 
1143 static int
faulthandler_traverse(PyObject * module,visitproc visit,void * arg)1144 faulthandler_traverse(PyObject *module, visitproc visit, void *arg)
1145 {
1146 #ifdef FAULTHANDLER_LATER
1147     Py_VISIT(thread.file);
1148 #endif
1149 #ifdef FAULTHANDLER_USER
1150     if (user_signals != NULL) {
1151         for (size_t signum=0; signum < NSIG; signum++)
1152             Py_VISIT(user_signals[signum].file);
1153     }
1154 #endif
1155     Py_VISIT(fatal_error.file);
1156     return 0;
1157 }
1158 
1159 #ifdef MS_WINDOWS
1160 static PyObject *
faulthandler_raise_exception(PyObject * self,PyObject * args)1161 faulthandler_raise_exception(PyObject *self, PyObject *args)
1162 {
1163     unsigned int code, flags = 0;
1164     if (!PyArg_ParseTuple(args, "I|I:_raise_exception", &code, &flags))
1165         return NULL;
1166     faulthandler_suppress_crash_report();
1167     RaiseException(code, flags, 0, NULL);
1168     Py_RETURN_NONE;
1169 }
1170 #endif
1171 
1172 PyDoc_STRVAR(module_doc,
1173 "faulthandler module.");
1174 
1175 static PyMethodDef module_methods[] = {
1176     {"enable",
1177      (PyCFunction)faulthandler_py_enable, METH_VARARGS|METH_KEYWORDS,
1178      PyDoc_STR("enable(file=sys.stderr, all_threads=True): "
1179                "enable the fault handler")},
1180     {"disable", (PyCFunction)faulthandler_disable_py, METH_NOARGS,
1181      PyDoc_STR("disable(): disable the fault handler")},
1182     {"is_enabled", (PyCFunction)faulthandler_is_enabled, METH_NOARGS,
1183      PyDoc_STR("is_enabled()->bool: check if the handler is enabled")},
1184     {"dump_traceback",
1185      (PyCFunction)faulthandler_dump_traceback_py, METH_VARARGS|METH_KEYWORDS,
1186      PyDoc_STR("dump_traceback(file=sys.stderr, all_threads=True): "
1187                "dump the traceback of the current thread, or of all threads "
1188                "if all_threads is True, into file")},
1189 #ifdef FAULTHANDLER_LATER
1190     {"dump_traceback_later",
1191      (PyCFunction)faulthandler_dump_traceback_later, METH_VARARGS|METH_KEYWORDS,
1192      PyDoc_STR("dump_traceback_later(timeout, repeat=False, file=sys.stderrn, exit=False):\n"
1193                "dump the traceback of all threads in timeout seconds,\n"
1194                "or each timeout seconds if repeat is True. If exit is True, "
1195                "call _exit(1) which is not safe.")},
1196     {"cancel_dump_traceback_later",
1197      (PyCFunction)faulthandler_cancel_dump_traceback_later_py, METH_NOARGS,
1198      PyDoc_STR("cancel_dump_traceback_later():\ncancel the previous call "
1199                "to dump_traceback_later().")},
1200 #endif
1201 
1202 #ifdef FAULTHANDLER_USER
1203     {"register",
1204      (PyCFunction)faulthandler_register_py, METH_VARARGS|METH_KEYWORDS,
1205      PyDoc_STR("register(signum, file=sys.stderr, all_threads=True, chain=False): "
1206                "register a handler for the signal 'signum': dump the "
1207                "traceback of the current thread, or of all threads if "
1208                "all_threads is True, into file")},
1209     {"unregister",
1210      faulthandler_unregister_py, METH_VARARGS|METH_KEYWORDS,
1211      PyDoc_STR("unregister(signum): unregister the handler of the signal "
1212                 "'signum' registered by register()")},
1213 #endif
1214 
1215     {"_read_null", faulthandler_read_null, METH_NOARGS,
1216      PyDoc_STR("_read_null(): read from NULL, raise "
1217                "a SIGSEGV or SIGBUS signal depending on the platform")},
1218     {"_sigsegv", faulthandler_sigsegv, METH_VARARGS,
1219      PyDoc_STR("_sigsegv(release_gil=False): raise a SIGSEGV signal")},
1220     {"_fatal_error_c_thread", faulthandler_fatal_error_c_thread, METH_NOARGS,
1221      PyDoc_STR("fatal_error_c_thread(): "
1222                "call Py_FatalError() in a new C thread.")},
1223     {"_sigabrt", faulthandler_sigabrt, METH_NOARGS,
1224      PyDoc_STR("_sigabrt(): raise a SIGABRT signal")},
1225     {"_sigfpe", (PyCFunction)faulthandler_sigfpe, METH_NOARGS,
1226      PyDoc_STR("_sigfpe(): raise a SIGFPE signal")},
1227     {"_fatal_error", faulthandler_fatal_error_py, METH_VARARGS,
1228      PyDoc_STR("_fatal_error(message): call Py_FatalError(message)")},
1229 #ifdef FAULTHANDLER_STACK_OVERFLOW
1230     {"_stack_overflow", (PyCFunction)faulthandler_stack_overflow, METH_NOARGS,
1231      PyDoc_STR("_stack_overflow(): recursive call to raise a stack overflow")},
1232 #endif
1233 #ifdef MS_WINDOWS
1234     {"_raise_exception", faulthandler_raise_exception, METH_VARARGS,
1235      PyDoc_STR("raise_exception(code, flags=0): Call RaiseException(code, flags).")},
1236 #endif
1237     {NULL, NULL}  /* sentinel */
1238 };
1239 
1240 static struct PyModuleDef module_def = {
1241     PyModuleDef_HEAD_INIT,
1242     "faulthandler",
1243     module_doc,
1244     0, /* non-negative size to be able to unload the module */
1245     module_methods,
1246     NULL,
1247     faulthandler_traverse,
1248     NULL,
1249     NULL
1250 };
1251 
1252 PyMODINIT_FUNC
PyInit_faulthandler(void)1253 PyInit_faulthandler(void)
1254 {
1255     PyObject *m = PyModule_Create(&module_def);
1256     if (m == NULL)
1257         return NULL;
1258 
1259     /* Add constants for unit tests */
1260 #ifdef MS_WINDOWS
1261     /* RaiseException() codes (prefixed by an underscore) */
1262     if (PyModule_AddIntConstant(m, "_EXCEPTION_ACCESS_VIOLATION",
1263                                 EXCEPTION_ACCESS_VIOLATION))
1264         return NULL;
1265     if (PyModule_AddIntConstant(m, "_EXCEPTION_INT_DIVIDE_BY_ZERO",
1266                                 EXCEPTION_INT_DIVIDE_BY_ZERO))
1267         return NULL;
1268     if (PyModule_AddIntConstant(m, "_EXCEPTION_STACK_OVERFLOW",
1269                                 EXCEPTION_STACK_OVERFLOW))
1270         return NULL;
1271 
1272     /* RaiseException() flags (prefixed by an underscore) */
1273     if (PyModule_AddIntConstant(m, "_EXCEPTION_NONCONTINUABLE",
1274                                 EXCEPTION_NONCONTINUABLE))
1275         return NULL;
1276     if (PyModule_AddIntConstant(m, "_EXCEPTION_NONCONTINUABLE_EXCEPTION",
1277                                 EXCEPTION_NONCONTINUABLE_EXCEPTION))
1278         return NULL;
1279 #endif
1280 
1281     return m;
1282 }
1283 
1284 static int
faulthandler_init_enable(void)1285 faulthandler_init_enable(void)
1286 {
1287     PyObject *module = PyImport_ImportModule("faulthandler");
1288     if (module == NULL) {
1289         return -1;
1290     }
1291 
1292     PyObject *res = _PyObject_CallMethodId(module, &PyId_enable, NULL);
1293     Py_DECREF(module);
1294     if (res == NULL) {
1295         return -1;
1296     }
1297     Py_DECREF(res);
1298 
1299     return 0;
1300 }
1301 
1302 _PyInitError
_PyFaulthandler_Init(int enable)1303 _PyFaulthandler_Init(int enable)
1304 {
1305 #ifdef HAVE_SIGALTSTACK
1306     int err;
1307 
1308     /* Try to allocate an alternate stack for faulthandler() signal handler to
1309      * be able to allocate memory on the stack, even on a stack overflow. If it
1310      * fails, ignore the error. */
1311     stack.ss_flags = 0;
1312     stack.ss_size = SIGSTKSZ;
1313     stack.ss_sp = PyMem_Malloc(stack.ss_size);
1314     if (stack.ss_sp != NULL) {
1315         err = sigaltstack(&stack, &old_stack);
1316         if (err) {
1317             PyMem_Free(stack.ss_sp);
1318             stack.ss_sp = NULL;
1319         }
1320     }
1321 #endif
1322 #ifdef FAULTHANDLER_LATER
1323     thread.file = NULL;
1324     thread.cancel_event = PyThread_allocate_lock();
1325     thread.running = PyThread_allocate_lock();
1326     if (!thread.cancel_event || !thread.running) {
1327         return _Py_INIT_ERR("failed to allocate locks for faulthandler");
1328     }
1329     PyThread_acquire_lock(thread.cancel_event, 1);
1330 #endif
1331 
1332     if (enable) {
1333         if (faulthandler_init_enable() < 0) {
1334             return _Py_INIT_ERR("failed to enable faulthandler");
1335         }
1336     }
1337     return _Py_INIT_OK();
1338 }
1339 
_PyFaulthandler_Fini(void)1340 void _PyFaulthandler_Fini(void)
1341 {
1342 #ifdef FAULTHANDLER_LATER
1343     /* later */
1344     if (thread.cancel_event) {
1345         cancel_dump_traceback_later();
1346         PyThread_release_lock(thread.cancel_event);
1347         PyThread_free_lock(thread.cancel_event);
1348         thread.cancel_event = NULL;
1349     }
1350     if (thread.running) {
1351         PyThread_free_lock(thread.running);
1352         thread.running = NULL;
1353     }
1354 #endif
1355 
1356 #ifdef FAULTHANDLER_USER
1357     /* user */
1358     if (user_signals != NULL) {
1359         for (size_t signum=0; signum < NSIG; signum++) {
1360             faulthandler_unregister(&user_signals[signum], signum);
1361         }
1362         PyMem_Free(user_signals);
1363         user_signals = NULL;
1364     }
1365 #endif
1366 
1367     /* fatal */
1368     faulthandler_disable();
1369 #ifdef HAVE_SIGALTSTACK
1370     if (stack.ss_sp != NULL) {
1371         /* Fetch the current alt stack */
1372         stack_t current_stack = {};
1373         if (sigaltstack(NULL, &current_stack) == 0) {
1374             if (current_stack.ss_sp == stack.ss_sp) {
1375                 /* The current alt stack is the one that we installed.
1376                  It is safe to restore the old stack that we found when
1377                  we installed ours */
1378                 sigaltstack(&old_stack, NULL);
1379             } else {
1380                 /* Someone switched to a different alt stack and didn't
1381                    restore ours when they were done (if they're done).
1382                    There's not much we can do in this unlikely case */
1383             }
1384         }
1385         PyMem_Free(stack.ss_sp);
1386         stack.ss_sp = NULL;
1387     }
1388 #endif
1389 }
1390