Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4ddee7f

Browse files
committed
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.
1 parent 62b6a0d commit 4ddee7f

1 file changed

Lines changed: 50 additions & 34 deletions

File tree

Python/pylifecycle.c

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,31 +1241,62 @@ initstdio(void)
12411241
}
12421242

12431243

1244+
static void
1245+
_Py_FatalError_DumpTracebacks(int fd)
1246+
{
1247+
PyThreadState *tstate;
1248+
1249+
#ifdef WITH_THREAD
1250+
/* PyGILState_GetThisThreadState() works even if the GIL was released */
1251+
tstate = PyGILState_GetThisThreadState();
1252+
#else
1253+
tstate = PyThreadState_GET();
1254+
#endif
1255+
if (tstate == NULL) {
1256+
/* _Py_DumpTracebackThreads() requires the thread state to display
1257+
* frames */
1258+
return;
1259+
}
1260+
1261+
fputc('\n', stderr);
1262+
fflush(stderr);
1263+
1264+
/* display the current Python stack */
1265+
_Py_DumpTracebackThreads(fd, tstate->interp, tstate);
1266+
}
1267+
12441268
/* Print the current exception (if an exception is set) with its traceback,
1245-
* or display the current Python stack.
1246-
*
1247-
* Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is
1248-
* called on catastrophic cases. */
1269+
or display the current Python stack.
12491270
1250-
static void
1251-
_Py_PrintFatalError(int fd)
1271+
Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is
1272+
called on catastrophic cases.
1273+
1274+
Return 1 if the traceback was displayed, 0 otherwise. */
1275+
1276+
static int
1277+
_Py_FatalError_PrintExc(int fd)
12521278
{
12531279
PyObject *ferr, *res;
12541280
PyObject *exception, *v, *tb;
12551281
int has_tb;
1256-
PyThreadState *tstate;
1282+
1283+
if (PyThreadState_GET() == NULL) {
1284+
/* The GIL is released: trying to acquire it is likely to deadlock,
1285+
just give up. */
1286+
return 0;
1287+
}
12571288

12581289
PyErr_Fetch(&exception, &v, &tb);
12591290
if (exception == NULL) {
12601291
/* No current exception */
1261-
goto display_stack;
1292+
return 0;
12621293
}
12631294

12641295
ferr = _PySys_GetObjectId(&PyId_stderr);
12651296
if (ferr == NULL || ferr == Py_None) {
12661297
/* sys.stderr is not set yet or set to None,
12671298
no need to try to display the exception */
1268-
goto display_stack;
1299+
return 0;
12691300
}
12701301

12711302
PyErr_NormalizeException(&exception, &v, &tb);
@@ -1276,7 +1307,7 @@ _Py_PrintFatalError(int fd)
12761307
PyException_SetTraceback(v, tb);
12771308
if (exception == NULL) {
12781309
/* PyErr_NormalizeException() failed */
1279-
goto display_stack;
1310+
return 0;
12801311
}
12811312

12821313
has_tb = (tb != Py_None);
@@ -1292,28 +1323,9 @@ _Py_PrintFatalError(int fd)
12921323
else
12931324
Py_DECREF(res);
12941325

1295-
if (has_tb)
1296-
return;
1297-
1298-
display_stack:
1299-
#ifdef WITH_THREAD
1300-
/* PyGILState_GetThisThreadState() works even if the GIL was released */
1301-
tstate = PyGILState_GetThisThreadState();
1302-
#else
1303-
tstate = PyThreadState_GET();
1304-
#endif
1305-
if (tstate == NULL) {
1306-
/* _Py_DumpTracebackThreads() requires the thread state to display
1307-
* frames */
1308-
return;
1309-
}
1310-
1311-
fputc('\n', stderr);
1312-
fflush(stderr);
1313-
1314-
/* display the current Python stack */
1315-
_Py_DumpTracebackThreads(fd, tstate->interp, tstate);
1326+
return has_tb;
13161327
}
1328+
13171329
/* Print fatal error message and abort */
13181330

13191331
void
@@ -1339,10 +1351,14 @@ Py_FatalError(const char *msg)
13391351

13401352
/* Print the exception (if an exception is set) with its traceback,
13411353
* or display the current Python stack. */
1342-
_Py_PrintFatalError(fd);
1354+
if (!_Py_FatalError_PrintExc(fd))
1355+
_Py_FatalError_DumpTracebacks(fd);
13431356

1344-
/* Flush sys.stdout and sys.stderr */
1345-
flush_std_files();
1357+
/* Check if the current Python thread hold the GIL */
1358+
if (PyThreadState_GET() != NULL) {
1359+
/* Flush sys.stdout and sys.stderr */
1360+
flush_std_files();
1361+
}
13461362

13471363
/* The main purpose of faulthandler is to display the traceback. We already
13481364
* did our best to display it. So faulthandler can now be disabled.

0 commit comments

Comments
 (0)