Coverage Report

Created: 2025-08-26 06:26

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