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