[Python-checkins] bpo-42923: Py_FatalError() avoids fprintf() (GH-24242)

vstinner webhook-mailer at python.org
Mon Jan 18 12:35:05 EST 2021


https://github.com/python/cpython/commit/314b8787e0c50985ba708034b84ff5b37a1d47de
commit: 314b8787e0c50985ba708034b84ff5b37a1d47de
branch: master
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2021-01-18T18:34:56+01:00
summary:

bpo-42923: Py_FatalError() avoids fprintf() (GH-24242)

* Replace buffered fprintf() with unbuffered _Py_write_noraise()
  in Py_FatalError().
* _Py_DumpHexadecimal() now accepts uintptr_t.

files:
M Include/internal/pycore_traceback.h
M Python/pylifecycle.c
M Python/traceback.c

diff --git a/Include/internal/pycore_traceback.h b/Include/internal/pycore_traceback.h
index 1f092411a72ba..169e99b86ef72 100644
--- a/Include/internal/pycore_traceback.h
+++ b/Include/internal/pycore_traceback.h
@@ -76,15 +76,11 @@ PyAPI_FUNC(void) _Py_DumpDecimal(
     int fd,
     unsigned long value);
 
-/* Format an integer as hexadecimal into the file descriptor fd with at least
-   width digits.
-
-   The maximum width is sizeof(unsigned long)*2 digits.
-
-   This function is signal safe. */
+/* Format an integer as hexadecimal with width digits into fd file descriptor.
+   The function is signal safe. */
 PyAPI_FUNC(void) _Py_DumpHexadecimal(
     int fd,
-    unsigned long value,
+    uintptr_t value,
     Py_ssize_t width);
 
 PyAPI_FUNC(PyObject*) _PyTraceBack_FromFrame(
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 9828dffad5c63..c02071780b863 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -38,6 +38,9 @@
 #endif
 
 
+#define PUTS(fd, str) _Py_write_noraise(fd, str, (int)strlen(str))
+
+
 _Py_IDENTIFIER(flush);
 _Py_IDENTIFIER(name);
 _Py_IDENTIFIER(stdin);
@@ -2348,8 +2351,7 @@ static void
 _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp,
                               PyThreadState *tstate)
 {
-    fputc('\n', stderr);
-    fflush(stderr);
+    PUTS(fd, "\n");
 
     /* display the current Python stack */
     _Py_DumpTracebackThreads(fd, interp, tstate);
@@ -2451,30 +2453,31 @@ fatal_output_debug(const char *msg)
 
 
 static void
-fatal_error_dump_runtime(FILE *stream, _PyRuntimeState *runtime)
+fatal_error_dump_runtime(int fd, _PyRuntimeState *runtime)
 {
-    fprintf(stream, "Python runtime state: ");
+    PUTS(fd, "Python runtime state: ");
     PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
     if (finalizing) {
-        fprintf(stream, "finalizing (tstate=%p)", finalizing);
+        PUTS(fd, "finalizing (tstate=0x");
+        _Py_DumpHexadecimal(fd, (uintptr_t)finalizing, sizeof(finalizing) * 2);
+        PUTS(fd, ")");
     }
     else if (runtime->initialized) {
-        fprintf(stream, "initialized");
+        PUTS(fd, "initialized");
     }
     else if (runtime->core_initialized) {
-        fprintf(stream, "core initialized");
+        PUTS(fd, "core initialized");
     }
     else if (runtime->preinitialized) {
-        fprintf(stream, "preinitialized");
+        PUTS(fd, "preinitialized");
     }
     else if (runtime->preinitializing) {
-        fprintf(stream, "preinitializing");
+        PUTS(fd, "preinitializing");
     }
     else {
-        fprintf(stream, "unknown");
+        PUTS(fd, "unknown");
     }
-    fprintf(stream, "\n");
-    fflush(stream);
+    PUTS(fd, "\n");
 }
 
 
@@ -2494,10 +2497,9 @@ fatal_error_exit(int status)
 
 
 static void _Py_NO_RETURN
-fatal_error(FILE *stream, int header, const char *prefix, const char *msg,
+fatal_error(int fd, int header, const char *prefix, const char *msg,
             int status)
 {
-    const int fd = fileno(stream);
     static int reentrant = 0;
 
     if (reentrant) {
@@ -2508,29 +2510,22 @@ fatal_error(FILE *stream, int header, const char *prefix, const char *msg,
     reentrant = 1;
 
     if (header) {
-        fprintf(stream, "Fatal Python error: ");
+        PUTS(fd, "Fatal Python error: ");
         if (prefix) {
-            fputs(prefix, stream);
-            fputs(": ", stream);
+            PUTS(fd, prefix);
+            PUTS(fd, ": ");
         }
         if (msg) {
-            fputs(msg, stream);
+            PUTS(fd, msg);
         }
         else {
-            fprintf(stream, "<message not set>");
+            PUTS(fd, "<message not set>");
         }
-        fputs("\n", stream);
-        fflush(stream);
+        PUTS(fd, "\n");
     }
 
     _PyRuntimeState *runtime = &_PyRuntime;
-    fatal_error_dump_runtime(stream, runtime);
-
-    PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
-    PyInterpreterState *interp = NULL;
-    if (tstate != NULL) {
-        interp = tstate->interp;
-    }
+    fatal_error_dump_runtime(fd, runtime);
 
     /* Check if the current thread has a Python thread state
        and holds the GIL.
@@ -2540,8 +2535,17 @@ fatal_error(FILE *stream, int header, const char *prefix, const char *msg,
 
        tss_tstate != tstate if the current Python thread does not hold the GIL.
        */
+    PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
+    PyInterpreterState *interp = NULL;
     PyThreadState *tss_tstate = PyGILState_GetThisThreadState();
+    if (tstate != NULL) {
+        interp = tstate->interp;
+    }
+    else if (tss_tstate != NULL) {
+        interp = tss_tstate->interp;
+    }
     int has_tstate_and_gil = (tss_tstate != NULL && tss_tstate == tstate);
+
     if (has_tstate_and_gil) {
         /* If an exception is set, print the exception with its traceback */
         if (!_Py_FatalError_PrintExc(tss_tstate)) {
@@ -2578,14 +2582,14 @@ fatal_error(FILE *stream, int header, const char *prefix, const char *msg,
 void _Py_NO_RETURN
 Py_FatalError(const char *msg)
 {
-    fatal_error(stderr, 1, NULL, msg, -1);
+    fatal_error(fileno(stderr), 1, NULL, msg, -1);
 }
 
 
 void _Py_NO_RETURN
 _Py_FatalErrorFunc(const char *func, const char *msg)
 {
-    fatal_error(stderr, 1, func, msg, -1);
+    fatal_error(fileno(stderr), 1, func, msg, -1);
 }
 
 
@@ -2600,12 +2604,12 @@ _Py_FatalErrorFormat(const char *func, const char *format, ...)
     reentrant = 1;
 
     FILE *stream = stderr;
-    fprintf(stream, "Fatal Python error: ");
+    const int fd = fileno(stream);
+    PUTS(fd, "Fatal Python error: ");
     if (func) {
-        fputs(func, stream);
-        fputs(": ", stream);
+        PUTS(fd, func);
+        PUTS(fd, ": ");
     }
-    fflush(stream);
 
     va_list vargs;
 #ifdef HAVE_STDARG_PROTOTYPES
@@ -2619,7 +2623,7 @@ _Py_FatalErrorFormat(const char *func, const char *format, ...)
     fputs("\n", stream);
     fflush(stream);
 
-    fatal_error(stream, 0, NULL, NULL, -1);
+    fatal_error(fd, 0, NULL, NULL, -1);
 }
 
 
@@ -2630,7 +2634,7 @@ Py_ExitStatusException(PyStatus status)
         exit(status.exitcode);
     }
     else if (_PyStatus_IS_ERROR(status)) {
-        fatal_error(stderr, 1, status.func, status.err_msg, 1);
+        fatal_error(fileno(stderr), 1, status.func, status.err_msg, 1);
     }
     else {
         Py_FatalError("Py_ExitStatusException() must not be called on success");
diff --git a/Python/traceback.c b/Python/traceback.c
index b82cfd3665ce1..bd5fd352152e0 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -649,15 +649,12 @@ _Py_DumpDecimal(int fd, unsigned long value)
     _Py_write_noraise(fd, ptr, end - ptr);
 }
 
-/* Format an integer in range [0; 0xffffffff] to hexadecimal of 'width' digits,
-   and write it into the file fd.
-
-   This function is signal safe. */
-
+/* Format an integer as hexadecimal with width digits into fd file descriptor.
+   The function is signal safe. */
 void
-_Py_DumpHexadecimal(int fd, unsigned long value, Py_ssize_t width)
+_Py_DumpHexadecimal(int fd, uintptr_t value, Py_ssize_t width)
 {
-    char buffer[sizeof(unsigned long) * 2 + 1], *ptr, *end;
+    char buffer[sizeof(uintptr_t) * 2 + 1], *ptr, *end;
     const Py_ssize_t size = Py_ARRAY_LENGTH(buffer) - 1;
 
     if (width > size)



More information about the Python-checkins mailing list