diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2016-03-14 15:53:12 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2016-03-14 15:53:12 (GMT) |
commit | 791da1cc264574f8f3e44570d0fce293f755fdf3 (patch) | |
tree | 6a9cc7d7e834039e74571cd77b33a4e30cfc784e /Python/pylifecycle.c | |
parent | 34be807ca4dfecc5b0a9e577a48535e738dce32b (diff) | |
download | cpython-791da1cc264574f8f3e44570d0fce293f755fdf3.zip cpython-791da1cc264574f8f3e44570d0fce293f755fdf3.tar.gz cpython-791da1cc264574f8f3e44570d0fce293f755fdf3.tar.bz2 |
Fix Py_FatalError() if called without the GIL
Issue #26558: If Py_FatalError() is called without the GIL, don't try to print
the current exception, nor try to flush stdout and stderr: only dump the
traceback of Python threads.
Diffstat (limited to 'Python/pylifecycle.c')
-rw-r--r-- | Python/pylifecycle.c | 84 |
1 files changed, 50 insertions, 34 deletions
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 715a547..aaf5811 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1267,31 +1267,62 @@ initstdio(void) } +static void +_Py_FatalError_DumpTracebacks(int fd) +{ + PyThreadState *tstate; + +#ifdef WITH_THREAD + /* PyGILState_GetThisThreadState() works even if the GIL was released */ + tstate = PyGILState_GetThisThreadState(); +#else + tstate = PyThreadState_GET(); +#endif + if (tstate == NULL) { + /* _Py_DumpTracebackThreads() requires the thread state to display + * frames */ + return; + } + + fputc('\n', stderr); + fflush(stderr); + + /* display the current Python stack */ + _Py_DumpTracebackThreads(fd, tstate->interp, tstate); +} + /* Print the current exception (if an exception is set) with its traceback, - * or display the current Python stack. - * - * Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is - * called on catastrophic cases. */ + or display the current Python stack. -static void -_Py_PrintFatalError(int fd) + Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is + called on catastrophic cases. + + Return 1 if the traceback was displayed, 0 otherwise. */ + +static int +_Py_FatalError_PrintExc(int fd) { PyObject *ferr, *res; PyObject *exception, *v, *tb; int has_tb; - PyThreadState *tstate; + + if (PyThreadState_GET() == NULL) { + /* The GIL is released: trying to acquire it is likely to deadlock, + just give up. */ + return 0; + } PyErr_Fetch(&exception, &v, &tb); if (exception == NULL) { /* No current exception */ - goto display_stack; + return 0; } ferr = _PySys_GetObjectId(&PyId_stderr); if (ferr == NULL || ferr == Py_None) { /* sys.stderr is not set yet or set to None, no need to try to display the exception */ - goto display_stack; + return 0; } PyErr_NormalizeException(&exception, &v, &tb); @@ -1302,7 +1333,7 @@ _Py_PrintFatalError(int fd) PyException_SetTraceback(v, tb); if (exception == NULL) { /* PyErr_NormalizeException() failed */ - goto display_stack; + return 0; } has_tb = (tb != Py_None); @@ -1318,28 +1349,9 @@ _Py_PrintFatalError(int fd) else Py_DECREF(res); - if (has_tb) - return; - -display_stack: -#ifdef WITH_THREAD - /* PyGILState_GetThisThreadState() works even if the GIL was released */ - tstate = PyGILState_GetThisThreadState(); -#else - tstate = PyThreadState_GET(); -#endif - if (tstate == NULL) { - /* _Py_DumpTracebackThreads() requires the thread state to display - * frames */ - return; - } - - fputc('\n', stderr); - fflush(stderr); - - /* display the current Python stack */ - _Py_DumpTracebackThreads(fd, tstate->interp, tstate); + return has_tb; } + /* Print fatal error message and abort */ void @@ -1365,10 +1377,14 @@ Py_FatalError(const char *msg) /* Print the exception (if an exception is set) with its traceback, * or display the current Python stack. */ - _Py_PrintFatalError(fd); + if (!_Py_FatalError_PrintExc(fd)) + _Py_FatalError_DumpTracebacks(fd); - /* Flush sys.stdout and sys.stderr */ - flush_std_files(); + /* Check if the current Python thread hold the GIL */ + if (PyThreadState_GET() != NULL) { + /* Flush sys.stdout and sys.stderr */ + flush_std_files(); + } /* The main purpose of faulthandler is to display the traceback. We already * did our best to display it. So faulthandler can now be disabled. |