10000 Merge branch 'main' into zb/bolt-common · python/cpython@ee9a852 · GitHub
[go: up one dir, main page]

Skip to content

Commit ee9a852

Browse files
committed
Merge branch 'main' into zb/bolt-common
2 parents 88eef21 + b75ed95 commit ee9a852

File tree

9 files changed

+103
-53
lines changed

9 files changed

+103
-53
lines changed

Doc/library/faulthandler.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ Fault handler state
9191
The dump now mentions if a garbage collector collection is running
9292
if *all_threads* is true.
9393

94+
.. versionchanged:: next
95+
Only the current thread is dumped if the :term:`GIL` is disabled to
96+
prevent the risk of data races.
97+
9498
.. function:: disable()
9599

96100
Disable the fault handler: uninstall the signal handlers installed by

Lib/test/test_faulthandler.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@ def check_error(self, code, lineno, fatal_error, *,
100100
101101
Raise an error if the output doesn't match the expected format.
102102
"""
103-
if all_threads:
103+
all_threads_disabled = (
104+
(not py_fatal_error)
105+
and all_threads
106+
and (not sys._is_gil_enabled())
107+
)
108+
if all_threads and not all_threads_disabled:
104109
if know_current_thread:
105110
header = 'Current thread 0x[0-9a-f]+'
106111
else:
@@ -111,8 +116,10 @@ def check_error(self, code, lineno, fatal_error, *,
111116
if py_fatal_error:
112117
regex.append("Python runtime state: initialized")
113118
regex.append('')
119+
if all_threads_disabled:
120+
regex.append("<Cannot show all threads while the GIL is disabled>")
114121
regex.append(fr'{header} \(most recent call first\):')
115-
if garbage_collecting:
122+
if garbage_collecting and not all_threads_disabled:
116123
regex.append(' Garbage-collecting')
117124
regex.append(fr' File "<string>", line {lineno} in {function}')
118125
regex = '\n'.join(regex)

Lib/test/test_str.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"""
88
import _string
99
import codecs
10+
import datetime
1011
import itertools
1112
import operator
1213
import pickle
@@ -1908,6 +1909,12 @@ def test_utf8_decode_invalid_sequences(self):
19081909
self.assertRaises(UnicodeDecodeError,
19091910
(b'\xF4'+cb+b'\xBF\xBF').decode, 'utf-8')
19101911

1912+
def test_issue127903(self):
1913+
# gh-127903: ``_copy_characters`` crashes on DEBUG builds when
1914+
# there is nothing to copy.
1915+
d = datetime.datetime(2013, 11, 10, 14, 20, 59)
1916+
self.assertEqual(d.strftime('%z'), '')
1917+
19111918
def test_issue8271(self):
19121919
# Issue #8271: during the decoding of an invalid UTF-8 byte sequence,
19131920
# only the start byte and the continuation byte(s) are now considered
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
``Objects/unicodeobject.c``: fix a crash on DEBUG builds in ``_copy_characters``
2+
when there is nothing to copy.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Only show the current thread in :mod:`faulthandler` on the :term:`free
2+
threaded <free threading>` build to prevent races.

Modules/faulthandler.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "Python.h"
2+
#include "pycore_ceval.h" // _PyEval_IsGILEnabled
23
#include "pycore_initconfig.h" // _PyStatus_ERR
34
#include "pycore_pyerrors.h" // _Py_DumpExtensionModules
45
#include "pycore_pystate.h" // _PyThreadState_GET()
@@ -27,6 +28,8 @@
2728
# include <sys/auxv.h> // getauxval()
2829
#endif
2930

31+
/* Sentinel to ignore all_threads on free-threading */
32+
#define FT_IGNORE_ALL_THREADS 2
3033

3134
/* Allocate at maximum 100 MiB of the stack to raise the stack overflow */
3235
#define STACK_OVERFLOW_MAX_SIZE (100 * 1024 * 1024)
@@ -201,10 +204,13 @@ faulthandler_dump_traceback(int fd, int all_threads,
201204
PyGILState_GetThisThreadState(). */
202205
PyThreadState *tstate = PyGILState_GetThisThreadState();
203206

204-
if (all_threads) {
207+
if (all_threads == 1) {
205208
(void)_Py_DumpTracebackThreads(fd, NULL, tstate);
206209
}
207210
else {
211+
if (all_threads == FT_IGNORE_ALL_THREADS) {
212+
PUTS(fd, "<Cannot show all threads while the GIL is disabled>\n");
213+
}
208214
if (tstate != NULL)
209215
_Py_DumpTraceback(fd, tstate);
210216
}
@@ -271,6 +277,27 @@ faulthandler_disable_fatal_handler(fault_handler_t *handler)
271277
#endif
272278
}
273279

280+
static int
281+
deduce_all_threads(void)
282+
{
283+
#ifndef Py_GIL_DISABLED
284+
return fatal_error.all_threads;
285+
#else
286+
if (fatal_error.all_threads == 0) {
287+
return 0;
288+
}
289+
// We can't use _PyThreadState_GET, so use the stored GILstate one
290+
PyThreadState *tstate = PyGILState_GetThisThreadState();
291+
if (tstate == NULL) {
292+
return 0;
293+
}
294+
295+
/* In theory, it's safe to dump all threads if the GIL is enabled */
296+
return _PyEval_IsGILEnabled(tstate)
297+
? fatal_error.all_threads
298+
: FT_IGNORE_ALL_THREADS;
299+
#endif
300+
}
274301

275302
/* Handler for SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals.
276303
@@ -325,7 +352,7 @@ faulthandler_fatal_error(int signum)
325352
PUTS(fd, "\n\n");
326353
}
327354

328-
faulthandler_dump_traceback(fd, fatal_error.all_threads,
355+
faulthandler_dump_traceback(fd, deduce_all_threads(),
329356
fatal_error.interp);
330357

331358
_Py_DumpExtensionModules(fd, fatal_error.interp);
@@ -401,7 +428,7 @@ faulthandler_exc_handler(struct _EXCEPTION_POINTERS *exc_info)
401428
}
402429
}
403430

404-
faulthandler_dump_traceback(fd, fatal_error.all_threads,
431+
faulthandler_dump_traceback(fd, deduce_all_threads(),
405432
fatal_error.interp);
406433

407434
/* call the next exception handler */

Objects/unicodeobject.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,11 +1463,14 @@ _copy_characters(PyObject *to, Py_ssize_t to_start,
14631463
assert(PyUnicode_Check(from));
14641464
assert(from_start + how_many <= PyUnicode_GET_LENGTH(from));
14651465

1466-
assert(PyUnicode_Check(to));
1467-
assert(to_start + how_many <= PyUnicode_GET_LENGTH(to));
1466+
assert(to == NULL || PyUnicode_Check(to));
14681467

1469-
if (how_many == 0)
1468+
if (how_many == 0) {
14701469
return 0;
1470+
}
1471+
1472+
assert(to != NULL);
1473+
assert(to_start + how_many <= PyUnicode_GET_LENGTH(to));
14711474

14721475
from_kind = PyUnicode_KIND(from);
14731476
from_data = PyUnicode_DATA(from);

configure

Lines changed: 25 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
0