Skip to content

Commit 314b878

Browse files
authored
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.
1 parent e232025 commit 314b878

File tree

3 files changed

+47
-50
lines changed

3 files changed

+47
-50
lines changed

Include/internal/pycore_traceback.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,11 @@ PyAPI_FUNC(void) _Py_DumpDecimal(
7676
int fd,
7777
unsigned long value);
7878

79-
/* Format an integer as hexadecimal into the file descriptor fd with at least
80-
width digits.
81-
82-
The maximum width is sizeof(unsigned long)*2 digits.
83-
84-
This function is signal safe. */
79+
/* Format an integer as hexadecimal with width digits into fd file descriptor.
80+
The function is signal safe. */
8581
PyAPI_FUNC(void) _Py_DumpHexadecimal(
8682
int fd,
87-
unsigned long value,
83+
uintptr_t value,
8884
Py_ssize_t width);
8985

9086
PyAPI_FUNC(PyObject*) _PyTraceBack_FromFrame(

Python/pylifecycle.c

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
#endif
3939

4040

41+
#define PUTS(fd, str) _Py_write_noraise(fd, str, (int)strlen(str))
42+
43+
4144
_Py_IDENTIFIER(flush);
4245
_Py_IDENTIFIER(name);
4346
_Py_IDENTIFIER(stdin);
@@ -2348,8 +2351,7 @@ static void
23482351
_Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp,
23492352
PyThreadState *tstate)
23502353
{
2351-
fputc('\n', stderr);
2352-
fflush(stderr);
2354+
PUTS(fd, "\n");
23532355

23542356
/* display the current Python stack */
23552357
_Py_DumpTracebackThreads(fd, interp, tstate);
@@ -2451,30 +2453,31 @@ fatal_output_debug(const char *msg)
24512453

24522454

24532455
static void
2454-
fatal_error_dump_runtime(FILE *stream, _PyRuntimeState *runtime)
2456+
fatal_error_dump_runtime(int fd, _PyRuntimeState *runtime)
24552457
{
2456-
fprintf(stream, "Python runtime state: ");
2458+
PUTS(fd, "Python runtime state: ");
24572459
PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
24582460
if (finalizing) {
2459-
fprintf(stream, "finalizing (tstate=%p)", finalizing);
2461+
PUTS(fd, "finalizing (tstate=0x");
2462+
_Py_DumpHexadecimal(fd, (uintptr_t)finalizing, sizeof(finalizing) * 2);
2463+
PUTS(fd, ")");
24602464
}
24612465
else if (runtime->initialized) {
2462-
fprintf(stream, "initialized");
2466+
PUTS(fd, "initialized");
24632467
}
24642468
else if (runtime->core_initialized) {
2465-
fprintf(stream, "core initialized");
2469+
PUTS(fd, "core initialized");
24662470
}
24672471
else if (runtime->preinitialized) {
2468-
fprintf(stream, "preinitialized");
2472+
PUTS(fd, "preinitialized");
24692473
}
24702474
else if (runtime->preinitializing) {
2471-
fprintf(stream, "preinitializing");
2475+
PUTS(fd, "preinitializing");
24722476
}
24732477
else {
2474-
fprintf(stream, "unknown");
2478+
PUTS(fd, "unknown");
24752479
}
2476-
fprintf(stream, "\n");
2477-
fflush(stream);
2480+
PUTS(fd, "\n");
24782481
}
24792482

24802483

@@ -2494,10 +2497,9 @@ fatal_error_exit(int status)
24942497

24952498

24962499
static void _Py_NO_RETURN
2497-
fatal_error(FILE *stream, int header, const char *prefix, const char *msg,
2500+
fatal_error(int fd, int header, const char *prefix, const char *msg,
24982501
int status)
24992502
{
2500-
const int fd = fileno(stream);
25012503
static int reentrant = 0;
25022504

25032505
if (reentrant) {
@@ -2508,29 +2510,22 @@ fatal_error(FILE *stream, int header, const char *prefix, const char *msg,
25082510
reentrant = 1;
25092511

25102512
if (header) {
2511-
fprintf(stream, "Fatal Python error: ");
2513+
PUTS(fd, "Fatal Python error: ");
25122514
if (prefix) {
2513-
fputs(prefix, stream);
2514-
fputs(": ", stream);
2515+
PUTS(fd, prefix);
2516+
PUTS(fd, ": ");
25152517
}
25162518
if (msg) {
2517-
fputs(msg, stream);
2519+
PUTS(fd, msg);
25182520
}
25192521
else {
2520-
fprintf(stream, "<message not set>");
2522+
PUTS(fd, "<message not set>");
25212523
}
2522-
fputs("\n", stream);
2523-
fflush(stream);
2524+
PUTS(fd, "\n");
25242525
}
25252526

25262527
_PyRuntimeState *runtime = &_PyRuntime;
2527-
fatal_error_dump_runtime(stream, runtime);
2528-
2529-
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
2530-
PyInterpreterState *interp = NULL;
2531-
if (tstate != NULL) {
2532-
interp = tstate->interp;
2533-
}
2528+
fatal_error_dump_runtime(fd, runtime);
25342529

25352530
/* Check if the current thread has a Python thread state
25362531
and holds the GIL.
@@ -2540,8 +2535,17 @@ fatal_error(FILE *stream, int header, const char *prefix, const char *msg,
25402535
25412536
tss_tstate != tstate if the current Python thread does not hold the GIL.
25422537
*/
2538+
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
2539+
PyInterpreterState *interp = NULL;
25432540
PyThreadState *tss_tstate = PyGILState_GetThisThreadState();
2541+
if (tstate != NULL) {
2542+
interp = tstate->interp;
2543+
}
2544+
else if (tss_tstate != NULL) {
2545+
interp = tss_tstate->interp;
2546+
}
25442547
int has_tstate_and_gil = (tss_tstate != NULL && tss_tstate == tstate);
2548+
25452549
if (has_tstate_and_gil) {
25462550
/* If an exception is set, print the exception with its traceback */
25472551
if (!_Py_FatalError_PrintExc(tss_tstate)) {
@@ -2578,14 +2582,14 @@ fatal_error(FILE *stream, int header, const char *prefix, const char *msg,
25782582
void _Py_NO_RETURN
25792583
Py_FatalError(const char *msg)
25802584
{
2581-
fatal_error(stderr, 1, NULL, msg, -1);
2585+
fatal_error(fileno(stderr), 1, NULL, msg, -1);
25822586
}
25832587

25842588

25852589
void _Py_NO_RETURN
25862590
_Py_FatalErrorFunc(const char *func, const char *msg)
25872591
{
2588-
fatal_error(stderr, 1, func, msg, -1);
2592+
fatal_error(fileno(stderr), 1, func, msg, -1);
25892593
}
25902594

25912595

@@ -2600,12 +2604,12 @@ _Py_FatalErrorFormat(const char *func, const char *format, ...)
26002604
reentrant = 1;
26012605

26022606
FILE *stream = stderr;
2603-
fprintf(stream, "Fatal Python error: ");
2607+
const int fd = fileno(stream);
2608+
PUTS(fd, "Fatal Python error: ");
26042609
if (func) {
2605-
fputs(func, stream);
2606-
fputs(": ", stream);
2610+
PUTS(fd, func);
2611+
PUTS(fd, ": ");
26072612
}
2608-
fflush(stream);
26092613

26102614
va_list vargs;
26112615
#ifdef HAVE_STDARG_PROTOTYPES
@@ -2619,7 +2623,7 @@ _Py_FatalErrorFormat(const char *func, const char *format, ...)
26192623
fputs("\n", stream);
26202624
fflush(stream);
26212625

2622-
fatal_error(stream, 0, NULL, NULL, -1);
2626+
fatal_error(fd, 0, NULL, NULL, -1);
26232627
}
26242628

26252629

@@ -2630,7 +2634,7 @@ Py_ExitStatusException(PyStatus status)
26302634
exit(status.exitcode);
26312635
}
26322636
else if (_PyStatus_IS_ERROR(status)) {
2633-
fatal_error(stderr, 1, status.func, status.err_msg, 1);
2637+
fatal_error(fileno(stderr), 1, status.func, status.err_msg, 1);
26342638
}
26352639
else {
26362640
Py_FatalError("Py_ExitStatusException() must not be called on success");

Python/traceback.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -649,15 +649,12 @@ _Py_DumpDecimal(int fd, unsigned long value)
649649
_Py_write_noraise(fd, ptr, end - ptr);
650650
}
651651

652-
/* Format an integer in range [0; 0xffffffff] to hexadecimal of 'width' digits,
653-
and write it into the file fd.
654-
655-
This function is signal safe. */
656-
652+
/* Format an integer as hexadecimal with width digits into fd file descriptor.
653+
The function is signal safe. */
657654
void
658-
_Py_DumpHexadecimal(int fd, unsigned long value, Py_ssize_t width)
655+
_Py_DumpHexadecimal(int fd, uintptr_t value, Py_ssize_t width)
659656
{
660-
char buffer[sizeof(unsigned long) * 2 + 1], *ptr, *end;
657+
char buffer[sizeof(uintptr_t) * 2 + 1], *ptr, *end;
661658
const Py_ssize_t size = Py_ARRAY_LENGTH(buffer) - 1;
662659

663660
if (width > size)

0 commit comments

Comments
 (0)