Coverage Report

Created: 2026-06-21 06:15

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