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