Skip to content

Commit 9b5b62a

Browse files
authored
Merge branch 'main' into fetch-restore-objects
2 parents 0fef293 + 207e1c5 commit 9b5b62a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+412
-304
lines changed

Doc/library/asyncio-eventloop.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -524,8 +524,8 @@ Opening network connections
524524
When a server's IPv4 path and protocol are working, but the server's
525525
IPv6 path and protocol are not working, a dual-stack client
526526
application experiences significant connection delay compared to an
527-
IPv4-only client. This is undesirable because it causes the dual-
528-
stack client to have a worse user experience. This document
527+
IPv4-only client. This is undesirable because it causes the
528+
dual-stack client to have a worse user experience. This document
529529
specifies requirements for algorithms that reduce this user-visible
530530
delay and provides an algorithm.
531531

Doc/library/decimal.rst

Lines changed: 84 additions & 84 deletions
Large diffs are not rendered by default.

Lib/ensurepip/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
__all__ = ["version", "bootstrap"]
1212
_PACKAGE_NAMES = ('setuptools', 'pip')
1313
_SETUPTOOLS_VERSION = "65.5.0"
14-
_PIP_VERSION = "23.0"
14+
_PIP_VERSION = "23.0.1"
1515
_PROJECTS = [
1616
("setuptools", _SETUPTOOLS_VERSION, "py3"),
1717
("pip", _PIP_VERSION, "py3"),

Lib/test/test_iter.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
from test.support import check_free_after_iterating, ALWAYS_EQ, NEVER_EQ
88
import pickle
99
import collections.abc
10+
import functools
11+
import contextlib
12+
import builtins
1013

1114
# Test result of triple loop (too big to inline)
1215
TRIPLETS = [(0, 0, 0), (0, 0, 1), (0, 0, 2),
@@ -91,6 +94,12 @@ def __call__(self):
9194
raise IndexError # Emergency stop
9295
return i
9396

97+
class EmptyIterClass:
98+
def __len__(self):
99+
return 0
100+
def __getitem__(self, i):
101+
raise StopIteration
102+
94103
# Main test suite
95104

96105
class TestCase(unittest.TestCase):
@@ -238,6 +247,78 @@ def test_mutating_seq_class_exhausted_iter(self):
238247
self.assertEqual(list(empit), [5, 6])
239248
self.assertEqual(list(a), [0, 1, 2, 3, 4, 5, 6])
240249

250+
def test_reduce_mutating_builtins_iter(self):
251+
# This is a reproducer of issue #101765
252+
# where iter `__reduce__` calls could lead to a segfault or SystemError
253+
# depending on the order of C argument evaluation, which is undefined
254+
255+
# Backup builtins
256+
builtins_dict = builtins.__dict__
257+
orig = {"iter": iter, "reversed": reversed}
258+
259+
def run(builtin_name, item, sentinel=None):
260+
it = iter(item) if sentinel is None else iter(item, sentinel)
261+
262+
class CustomStr:
263+
def __init__(self, name, iterator):
264+
self.name = name
265+
self.iterator = iterator
266+
def __hash__(self):
267+
return hash(self.name)
268+
def __eq__(self, other):
269+
# Here we exhaust our iterator, possibly changing
270+
# its `it_seq` pointer to NULL
271+
# The `__reduce__` call should correctly get
272+
# the pointers after this call
273+
list(self.iterator)
274+
return other == self.name
275+
276+
# del is required here
277+
# to not prematurely call __eq__ from
278+
# the hash collision with the old key
279+
del builtins_dict[builtin_name]
280+
builtins_dict[CustomStr(builtin_name, it)] = orig[builtin_name]
281+
282+
return it.__reduce__()
283+
284+
types = [
285+
(EmptyIterClass(),),
286+
(bytes(8),),
287+
(bytearray(8),),
288+
((1, 2, 3),),
289+
(lambda: 0, 0),
290+
(tuple[int],) # GenericAlias
291+
]
292+
293+
try:
294+
run_iter = functools.partial(run, "iter")
295+
# The returned value of `__reduce__` should not only be valid
296+
# but also *empty*, as `it` was exhausted during `__eq__`
297+
# i.e "xyz" returns (iter, ("",))
298+
self.assertEqual(run_iter("xyz"), (orig["iter"], ("",)))
299+
self.assertEqual(run_iter([1, 2, 3]), (orig["iter"], ([],)))
300+
301+
# _PyEval_GetBuiltin is also called for `reversed` in a branch of
302+
# listiter_reduce_general
303+
self.assertEqual(
304+
run("reversed", orig["reversed"](list(range(8)))),
305+
(iter, ([],))
306+
)
307+
308+
for case in types:
309+
self.assertEqual(run_iter(*case), (orig["iter"], ((),)))
310+
finally:
311+
# Restore original builtins
312+
for key, func in orig.items():
313+
# need to suppress KeyErrors in case
314+
# a failed test deletes the key without setting anything
315+
with contextlib.suppress(KeyError):
316+
# del is required here
317+
# to not invoke our custom __eq__ from
318+
# the hash collision with the old key
319+
del builtins_dict[key]
320+
builtins_dict[key] = func
321+
241322
# Test a new_style class with __iter__ but no next() method
242323
def test_new_style_iter_class(self):
243324
class IterClass(object):

Lib/test/test_tarfile.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,18 +225,19 @@ def test_add_dir_getmember(self):
225225
self.add_dir_and_getmember('bar')
226226
self.add_dir_and_getmember('a'*101)
227227

228-
@unittest.skipIf(
229-
(hasattr(os, 'getuid') and os.getuid() > 0o777_7777) or
230-
(hasattr(os, 'getgid') and os.getgid() > 0o777_7777),
231-
"uid or gid too high for USTAR format."
232-
)
228+
@unittest.skipUnless(hasattr(os, "getuid") and hasattr(os, "getgid"),
229+
"Missing getuid or getgid implementation")
233230
def add_dir_and_getmember(self, name):
231+
def filter(tarinfo):
232+
tarinfo.uid = tarinfo.gid = 100
233+
return tarinfo
234+
234235
with os_helper.temp_cwd():
235236
with tarfile.open(tmpname, 'w') as tar:
236237
tar.format = tarfile.USTAR_FORMAT
237238
try:
238239
os.mkdir(name)
239-
tar.add(name)
240+
tar.add(name, filter=filter)
240241
finally:
241242
os.rmdir(name)
242243
with tarfile.open(tmpname) as tar:

Lib/test/test_zipfile/test_path.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,8 @@ def test_joinpath_constant_time(self):
330330
# Check the file iterated all items
331331
assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES
332332

333-
@set_timeout(3)
333+
# timeout disabled due to #102209
334+
# @set_timeout(3)
334335
def test_implied_dirs_performance(self):
335336
data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)]
336337
zipfile.CompleteDirs._implied_dirs(data)

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,7 @@ Jon Oberheide
13081308
Milan Oberkirch
13091309
Pascal Oberndoerfer
13101310
Géry Ogam
1311+
Seonkyo Ok
13111312
Jeffrey Ollie
13121313
Adam Olsen
13131314
Bryan Olson
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix SystemError / segmentation fault in iter ``__reduce__`` when internal access of ``builtins.__dict__`` keys mutates the iter object.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix deadlock at shutdown when clearing thread states if any finalizer tries to acquire the runtime head lock. Patch by Kumar Aditya.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Upgrade pip wheel bundled with ensurepip (pip 23.0.1)

Modules/_abc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ abc_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
7979
return NULL;
8080
}
8181

82-
state = PyType_GetModuleState(type);
82+
state = _PyType_GetModuleState(type);
8383
if (state == NULL) {
8484
Py_DECREF(self);
8585
return NULL;

Modules/_asynciomodule.c

Lines changed: 23 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ get_asyncio_state(PyObject *mod)
7676
static inline asyncio_state *
7777
get_asyncio_state_by_cls(PyTypeObject *cls)
7878
{
79-
asyncio_state *state = (asyncio_state *)PyType_GetModuleState(cls);
79+
asyncio_state *state = (asyncio_state *)_PyType_GetModuleState(cls);
8080
assert(state != NULL);
8181
return state;
8282
}
@@ -1422,7 +1422,6 @@ _asyncio_Future__make_cancelled_error_impl(FutureObj *self)
14221422
static void
14231423
FutureObj_finalize(FutureObj *fut)
14241424
{
1425-
PyObject *error_type, *error_value, *error_traceback;
14261425
PyObject *context;
14271426
PyObject *message = NULL;
14281427
PyObject *func;
@@ -1434,7 +1433,7 @@ FutureObj_finalize(FutureObj *fut)
14341433
fut->fut_log_tb = 0;
14351434

14361435
/* Save the current exception, if any. */
1437-
PyErr_Fetch(&error_type, &error_value, &error_traceback);
1436+
PyObject *exc = PyErr_GetRaisedException();
14381437

14391438
context = PyDict_New();
14401439
if (context == NULL) {
@@ -1476,7 +1475,7 @@ FutureObj_finalize(FutureObj *fut)
14761475
Py_XDECREF(message);
14771476

14781477
/* Restore the saved exception. */
1479-
PyErr_Restore(error_type, error_value, error_traceback);
1478+
PyErr_SetRaisedException(exc);
14801479
}
14811480

14821481
static PyMethodDef FutureType_methods[] = {
@@ -2491,14 +2490,13 @@ TaskObj_finalize(TaskObj *task)
24912490
PyObject *context;
24922491
PyObject *message = NULL;
24932492
PyObject *func;
2494-
PyObject *error_type, *error_value, *error_traceback;
24952493

24962494
if (task->task_state != STATE_PENDING || !task->task_log_destroy_pending) {
24972495
goto done;
24982496
}
24992497

25002498
/* Save the current exception, if any. */
2501-
PyErr_Fetch(&error_type, &error_value, &error_traceback);
2499+
PyObject *exc = PyErr_GetRaisedException();
25022500

25032501
context = PyDict_New();
25042502
if (context == NULL) {
@@ -2541,7 +2539,7 @@ TaskObj_finalize(TaskObj *task)
25412539
Py_XDECREF(message);
25422540

25432541
/* Restore the saved exception. */
2544-
PyErr_Restore(error_type, error_value, error_traceback);
2542+
PyErr_SetRaisedException(exc);
25452543

25462544
done:
25472545
FutureObj_finalize((FutureObj*)task);
@@ -2766,8 +2764,6 @@ task_step_impl(asyncio_state *state, TaskObj *task, PyObject *exc)
27662764
}
27672765

27682766
if (gen_status == PYGEN_RETURN || gen_status == PYGEN_ERROR) {
2769-
PyObject *et, *ev, *tb;
2770-
27712767
if (result != NULL) {
27722768
/* The error is StopIteration and that means that
27732769
the underlying coroutine has resolved */
@@ -2794,52 +2790,39 @@ task_step_impl(asyncio_state *state, TaskObj *task, PyObject *exc)
27942790

27952791
if (PyErr_ExceptionMatches(state->asyncio_CancelledError)) {
27962792
/* CancelledError */
2797-
PyErr_Fetch(&et, &ev, &tb);
2798-
assert(et);
2799-
PyErr_NormalizeException(&et, &ev, &tb);
2800-
if (tb != NULL) {
2801-
PyException_SetTraceback(ev, tb);
2802-
Py_DECREF(tb);
2803-
}
2804-
Py_XDECREF(et);
2793+
2794+
PyObject *exc = PyErr_GetRaisedException();
2795+
assert(exc);
28052796

28062797
FutureObj *fut = (FutureObj*)task;
28072798
/* transfer ownership */
2808-
fut->fut_cancelled_exc = ev;
2799+
fut->fut_cancelled_exc = exc;
28092800

28102801
return future_cancel(state, fut, NULL);
28112802
}
28122803

28132804
/* Some other exception; pop it and call Task.set_exception() */
2814-
PyErr_Fetch(&et, &ev, &tb);
2815-
assert(et);
2816-
PyErr_NormalizeException(&et, &ev, &tb);
2817-
if (tb != NULL) {
2818-
PyException_SetTraceback(ev, tb);
2819-
}
2805+
PyObject *exc = PyErr_GetRaisedException();
2806+
assert(exc);
28202807

2821-
o = future_set_exception(state, (FutureObj*)task, ev);
2808+
o = future_set_exception(state, (FutureObj*)task, exc);
28222809
if (!o) {
28232810
/* An exception in Task.set_exception() */
2824-
Py_DECREF(et);
2825-
Py_XDECREF(tb);
2826-
Py_XDECREF(ev);
2811+
Py_DECREF(exc);
28272812
goto fail;
28282813
}
28292814
assert(o == Py_None);
28302815
Py_DECREF(o);
28312816

2832-
if (PyErr_GivenExceptionMatches(et, PyExc_KeyboardInterrupt) ||
2833-
PyErr_GivenExceptionMatches(et, PyExc_SystemExit))
2817+
if (PyErr_GivenExceptionMatches(exc, PyExc_KeyboardInterrupt) ||
2818+
PyErr_GivenExceptionMatches(exc, PyExc_SystemExit))
28342819
{
28352820
/* We've got a KeyboardInterrupt or a SystemError; re-raise it */
2836-
PyErr_Restore(et, ev, tb);
2821+
PyErr_SetRaisedException(exc);
28372822
goto fail;
28382823
}
28392824

2840-
Py_DECREF(et);
2841-
Py_XDECREF(tb);
2842-
Py_XDECREF(ev);
2825+
Py_DECREF(exc);
28432826

28442827
Py_RETURN_NONE;
28452828
}
@@ -3059,10 +3042,9 @@ task_step(asyncio_state *state, TaskObj *task, PyObject *exc)
30593042
res = task_step_impl(state, task, exc);
30603043

30613044
if (res == NULL) {
3062-
PyObject *et, *ev, *tb;
3063-
PyErr_Fetch(&et, &ev, &tb);
3045+
PyObject *exc = PyErr_GetRaisedException();
30643046
leave_task(state, task->task_loop, (PyObject*)task);
3065-
_PyErr_ChainExceptions(et, ev, tb); /* Normalizes (et, ev, tb) */
3047+
_PyErr_ChainExceptions1(exc);
30663048
return NULL;
30673049
}
30683050
else {
@@ -3079,7 +3061,6 @@ task_step(asyncio_state *state, TaskObj *task, PyObject *exc)
30793061
static PyObject *
30803062
task_wakeup(TaskObj *task, PyObject *o)
30813063
{
3082-
PyObject *et, *ev, *tb;
30833064
PyObject *result;
30843065
assert(o);
30853066

@@ -3111,18 +3092,12 @@ task_wakeup(TaskObj *task, PyObject *o)
31113092
/* exception raised */
31123093
}
31133094

3114-
PyErr_Fetch(&et, &ev, &tb);
3115-
assert(et);
3116-
PyErr_NormalizeException(&et, &ev, &tb);
3117-
if (tb != NULL) {
3118-
PyException_SetTraceback(ev, tb);
3119-
}
3095+
PyObject *exc = PyErr_GetRaisedException();
3096+
assert(exc);
31203097

3121-
result = task_step(state, task, ev);
3098+
result = task_step(state, task, exc);
31223099

3123-
Py_DECREF(et);
3124-
Py_XDECREF(tb);
3125-
Py_XDECREF(ev);
3100+
Py_DECREF(exc);
31263101

31273102
return result;
31283103
}

Modules/_io/_iomodule.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -437,10 +437,9 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode,
437437

438438
error:
439439
if (result != NULL) {
440-
PyObject *exc, *val, *tb, *close_result;
441-
PyErr_Fetch(&exc, &val, &tb);
442-
close_result = PyObject_CallMethodNoArgs(result, &_Py_ID(close));
443-
_PyErr_ChainExceptions(exc, val, tb);
440+
PyObject *exc = PyErr_GetRaisedException();
441+
PyObject *close_result = PyObject_CallMethodNoArgs(result, &_Py_ID(close));
442+
_PyErr_ChainExceptions1(exc);
444443
Py_XDECREF(close_result);
445444
Py_DECREF(result);
446445
}

0 commit comments

Comments
 (0)