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