]> granicus.if.org Git - python/commitdiff
Fix Py_FatalError() if called without the GIL
authorVictor Stinner <victor.stinner@gmail.com>
Mon, 14 Mar 2016 15:53:12 +0000 (16:53 +0100)
committerVictor Stinner <victor.stinner@gmail.com>
Mon, 14 Mar 2016 15:53:12 +0000 (16:53 +0100)
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.

Python/pylifecycle.c

index 715a547d330e05e4b214a4d675851e76bb5dd6f1..aaf58119e501cff3f225d43c9542dd2a08bda62d 100644 (file)
@@ -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.