From 8da834ce3dee31e77e7529234a3fb1fdd31d0520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20La=C3=BCgt?= Date: Fri, 8 Mar 2024 16:15:40 +0100 Subject: [PATCH 1/3] Fix memory access violation on fatal error with Windows --- Python/pylifecycle.c | 66 ++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 724fda635112829..b505113ffac7e74 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -3079,6 +3079,8 @@ _Py_FatalError_PrintExc(PyThreadState *tstate) static void fatal_output_debug(const char *msg) { + assert(msg != NULL); + /* buffer of 256 bytes allocated on the stack */ WCHAR buffer[256 / sizeof(WCHAR)]; size_t buflen = Py_ARRAY_LENGTH(buffer) - 1; @@ -3285,7 +3287,7 @@ _Py_DumpExtensionModules(int fd, PyInterpreterState *interp) static void _Py_NO_RETURN -fatal_error(int fd, int header, const char *prefix, const char *msg, +fatal_error(int fd, const char *prefix, const char *msg, int status) { static int reentrant = 0; @@ -3297,20 +3299,18 @@ fatal_error(int fd, int header, const char *prefix, const char *msg, } reentrant = 1; - if (header) { - PUTS(fd, "Fatal Python error: "); - if (prefix) { - PUTS(fd, prefix); - PUTS(fd, ": "); - } - if (msg) { - PUTS(fd, msg); - } - else { - PUTS(fd, ""); - } - PUTS(fd, "\n"); + PUTS(fd, "Fatal Python error: "); + if (prefix) { + PUTS(fd, prefix); + PUTS(fd, ": "); + } + if (msg) { + PUTS(fd, msg); + } + else { + PUTS(fd, ""); } + PUTS(fd, "\n"); _PyRuntimeState *runtime = &_PyRuntime; fatal_error_dump_runtime(fd, runtime); @@ -3360,7 +3360,12 @@ fatal_error(int fd, int header, const char *prefix, const char *msg, } #ifdef MS_WINDOWS - fatal_output_debug(msg); + if (msg) { + fatal_output_debug(msg); + } + else { + fatal_output_debug(""); + } #endif /* MS_WINDOWS */ fatal_error_exit(status); @@ -3372,44 +3377,33 @@ fatal_error(int fd, int header, const char *prefix, const char *msg, void _Py_NO_RETURN Py_FatalError(const char *msg) { - fatal_error(fileno(stderr), 1, NULL, msg, -1); + fatal_error(fileno(stderr), NULL, msg, -1); } void _Py_NO_RETURN _Py_FatalErrorFunc(const char *func, const char *msg) { - fatal_error(fileno(stderr), 1, func, msg, -1); + fatal_error(fileno(stderr), func, msg, -1); } void _Py_NO_RETURN _Py_FatalErrorFormat(const char *func, const char *format, ...) { - static int reentrant = 0; - if (reentrant) { - /* _Py_FatalErrorFormat() caused a second fatal error */ - fatal_error_exit(-1); - } - reentrant = 1; - - FILE *stream = stderr; - const int fd = fileno(stream); - PUTS(fd, "Fatal Python error: "); - if (func) { - PUTS(fd, func); - PUTS(fd, ": "); - } - va_list vargs; va_start(vargs, format); - vfprintf(stream, format, vargs); + int length = vsnprintf(NULL, 0, format, vargs); va_end(vargs); - fputs("\n", stream); - fflush(stream); + char* msg = malloc(length + 1); + + va_start(vargs, format); + vsnprintf(text, length + 1, format, vargs); + va_end(vargs); - fatal_error(fd, 0, NULL, NULL, -1); + fatal_error(fileno(stderr), func, msg, -1); + free(msg); } From f892938791659ae5c0ec1c1df1e8ba391c108802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20La=C3=BCgt?= Date: Sat, 21 Jun 2025 13:07:43 +0200 Subject: [PATCH 2/3] Fix compilation --- Python/pylifecycle.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index b505113ffac7e74..3400d0a484a5fdf 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -3399,7 +3399,7 @@ _Py_FatalErrorFormat(const char *func, const char *format, ...) char* msg = malloc(length + 1); va_start(vargs, format); - vsnprintf(text, length + 1, format, vargs); + vsnprintf(msg, length + 1, format, vargs); va_end(vargs); fatal_error(fileno(stderr), func, msg, -1); @@ -3424,7 +3424,7 @@ Py_ExitStatusException(PyStatus status) exit(status.exitcode); } else if (_PyStatus_IS_ERROR(status)) { - fatal_error(fileno(stderr), 1, status.func, status.err_msg, 1); + fatal_error(fileno(stderr), status.func, status.err_msg, 1); } else { Py_FatalError("Py_ExitStatusException() must not be called on success"); From e93fbdac7f603b652492e1bf3b1950a3846e6b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20La=C3=BCgt?= Date: Mon, 23 Jun 2025 21:11:35 +0200 Subject: [PATCH 3/3] Solution without the malloc. On Windows, if the message is longer than 1024 characters, the message is cut off with an ellipsis at the debugger output. --- Python/pylifecycle.c | 77 ++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 3400d0a484a5fdf..54576ec6b1db8ed 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -3287,7 +3287,7 @@ _Py_DumpExtensionModules(int fd, PyInterpreterState *interp) static void _Py_NO_RETURN -fatal_error(int fd, const char *prefix, const char *msg, +fatal_error(int fd, int header, const char *prefix, const char *msg, int status) { static int reentrant = 0; @@ -3299,18 +3299,20 @@ fatal_error(int fd, const char *prefix, const char *msg, } reentrant = 1; - PUTS(fd, "Fatal Python error: "); - if (prefix) { - PUTS(fd, prefix); - PUTS(fd, ": "); - } - if (msg) { - PUTS(fd, msg); - } - else { - PUTS(fd, ""); + if (header) { + PUTS(fd, "Fatal Python error: "); + if (prefix) { + PUTS(fd, prefix); + PUTS(fd, ": "); + } + if (msg) { + PUTS(fd, msg); + } + else { + PUTS(fd, ""); + } + PUTS(fd, "\n"); } - PUTS(fd, "\n"); _PyRuntimeState *runtime = &_PyRuntime; fatal_error_dump_runtime(fd, runtime); @@ -3377,33 +3379,68 @@ fatal_error(int fd, const char *prefix, const char *msg, void _Py_NO_RETURN Py_FatalError(const char *msg) { - fatal_error(fileno(stderr), 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(fileno(stderr), func, msg, -1); + fatal_error(fileno(stderr), 1, func, msg, -1); } +#define FATAL_MSG_SIZE 1024 +#define FATAL_MSG_SIZE_WITHOUT_ELLIPSIS FATAL_MSG_SIZE - 3 void _Py_NO_RETURN _Py_FatalErrorFormat(const char *func, const char *format, ...) { + static int reentrant = 0; + if (reentrant) { + /* _Py_FatalErrorFormat() caused a second fatal error */ + fatal_error_exit(-1); + } + reentrant = 1; + va_list vargs; +#ifdef MS_WINDOWS va_start(vargs, format); - int length = vsnprintf(NULL, 0, format, vargs); + int length = vsnprintf(NULL, 0, format, vargs); va_end(vargs); - char* msg = malloc(length + 1); + char msg[FATAL_MSG_SIZE]; + if (length < FATAL_MSG_SIZE) { + va_start(vargs, format); + vsnprintf(msg, length + 1, format, vargs); + va_end(vargs); + + fatal_error(fileno(stderr), 1, func, msg, -1); + } va_start(vargs, format); - vsnprintf(msg, length + 1, format, vargs); + vsnprintf(msg, FATAL_MSG_SIZE_WITHOUT_ELLIPSIS, format, vargs); va_end(vargs); + strcpy (msg + FATAL_MSG_SIZE_WITHOUT_ELLIPSIS - 1, "..."); +#else + char *msg = NULL; +#endif + + FILE *stream = stderr; + const int fd = fileno(stream); + PUTS(fd, "Fatal Python error: "); + if (func) { + PUTS(fd, func); + PUTS(fd, ": "); + } + + va_start(vargs, format); + vfprintf(stream, format, vargs); + va_end(vargs); + + fputs("\n", stream); + fflush(stream); - fatal_error(fileno(stderr), func, msg, -1); - free(msg); + fatal_error(fd, 0, func, msg, -1); } @@ -3424,7 +3461,7 @@ Py_ExitStatusException(PyStatus status) exit(status.exitcode); } else if (_PyStatus_IS_ERROR(status)) { - fatal_error(fileno(stderr), 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");