LCOV - code coverage report
Current view: | top level - Modules - faulthandler.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 236 | 479 | 49.3 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 20 | 39 | 51.3 %
|  |  | Branches: | 96 | 236 | 40.7 %

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

Generated by: LCOV version 1.14