Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions Include/internal/pycore_traceback.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
76 changes: 40 additions & 36 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
}


Expand All @@ -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) {
Expand All @@ -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.
Expand All @@ -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)) {
Expand Down Expand Up @@ -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);
}


Expand All @@ -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
Expand All @@ -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);
}


Expand All @@ -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");
Expand Down
11 changes: 4 additions & 7 deletions Python/traceback.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down