Skip to content

Commit 35f8b9e

Browse files
authored
Merge branch 'main' into support-IPv6-in-cookiejar
2 parents 7731e7f + 7c43615 commit 35f8b9e

File tree

12 files changed

+207
-57
lines changed

12 files changed

+207
-57
lines changed

Doc/library/sys.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,6 +1185,15 @@ always available. Unless explicitly noted otherwise, all variables are read-only
11851185
``cache_tag`` is set to ``None``, it indicates that module caching should
11861186
be disabled.
11871187

1188+
*supports_isolated_interpreters* is a boolean value, whether
1189+
this implementation supports multiple isolated interpreters.
1190+
It is ``True`` for CPython on most platforms. Platforms with
1191+
this support implement the low-level :mod:`!_interpreters` module.
1192+
1193+
.. seealso::
1194+
1195+
:pep:`684`, :pep:`734`, and :mod:`concurrent.interpreters`.
1196+
11881197
:data:`sys.implementation` may contain additional attributes specific to
11891198
the Python implementation. These non-standard attributes must start with
11901199
an underscore, and are not described here. Regardless of its contents,
@@ -1194,6 +1203,9 @@ always available. Unless explicitly noted otherwise, all variables are read-only
11941203

11951204
.. versionadded:: 3.3
11961205

1206+
.. versionchanged:: 3.14
1207+
Added ``supports_isolated_interpreters`` field.
1208+
11971209
.. note::
11981210

11991211
The addition of new required attributes must go through the normal PEP

Lib/test/test_free_threading/test_heapq.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import heapq
44

55
from enum import Enum
6-
from threading import Thread, Barrier
6+
from threading import Thread, Barrier, Lock
77
from random import shuffle, randint
88

99
from test.support import threading_helper
@@ -178,6 +178,33 @@ def heapreplace_max_func(max_heap, replace_items):
178178
self.assertEqual(len(max_heap), OBJECT_COUNT)
179179
self.test_heapq.check_max_invariant(max_heap)
180180

181+
def test_lock_free_list_read(self):
182+
n, n_threads = 1_000, 10
183+
l = []
184+
barrier = Barrier(n_threads * 2)
185+
186+
count = 0
187+
lock = Lock()
188+
189+
def worker():
190+
with lock:
191+
nonlocal count
192+
x = count
193+
count += 1
194+
195+
barrier.wait()
196+
for i in range(n):
197+
if x % 2:
198+
heapq.heappush(l, 1)
199+
heapq.heappop(l)
200+
else:
201+
try:
202+
l[0]
203+
except IndexError:
204+
pass
205+
206+
self.run_concurrently(worker, (), n_threads * 2)
207+
181208
@staticmethod
182209
def is_sorted_ascending(lst):
183210
"""

Lib/test/test_hashlib.py

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,11 @@ def hash_constructors(self):
207207
constructors = self.constructors_to_test.values()
208208
return itertools.chain.from_iterable(constructors)
209209

210+
@property
211+
def shake_constructors(self):
212+
for shake_name in self.shakes:
213+
yield from self.constructors_to_test.get(shake_name, ())
214+
210215
@property
211216
def is_fips_mode(self):
212217
return get_fips_mode()
@@ -376,21 +381,50 @@ def test_hexdigest(self):
376381
self.assertIsInstance(h.digest(), bytes)
377382
self.assertEqual(hexstr(h.digest()), h.hexdigest())
378383

379-
def test_digest_length_overflow(self):
380-
# See issue #34922
381-
large_sizes = (2**29, 2**32-10, 2**32+10, 2**61, 2**64-10, 2**64+10)
382-
for cons in self.hash_constructors:
383-
h = cons(usedforsecurity=False)
384-
if h.name not in self.shakes:
385-
continue
386-
if HASH is not None and isinstance(h, HASH):
387-
# _hashopenssl's take a size_t
388-
continue
389-
for digest in h.digest, h.hexdigest:
390-
self.assertRaises(ValueError, digest, -10)
391-
for length in large_sizes:
392-
with self.assertRaises((ValueError, OverflowError)):
393-
digest(length)
384+
def test_shakes_zero_digest_length(self):
385+
for constructor in self.shake_constructors:
386+
with self.subTest(constructor=constructor):
387+
h = constructor(b'abcdef', usedforsecurity=False)
388+
self.assertEqual(h.digest(0), b'')
389+
self.assertEqual(h.hexdigest(0), '')
390+
391+
def test_shakes_invalid_digest_length(self):
392+
# See https://github.com/python/cpython/issues/79103.
393+
for constructor in self.shake_constructors:
394+
with self.subTest(constructor=constructor):
395+
h = constructor(usedforsecurity=False)
396+
# Note: digest() and hexdigest() take a signed input and
397+
# raise if it is negative; the rationale is that we use
398+
# internally PyBytes_FromStringAndSize() and _Py_strhex()
399+
# which both take a Py_ssize_t.
400+
for negative_size in (-1, -10, -(1 << 31), -sys.maxsize):
401+
self.assertRaises(ValueError, h.digest, negative_size)
402+
self.assertRaises(ValueError, h.hexdigest, negative_size)
403+
404+
def test_shakes_overflow_digest_length(self):
405+
# See https://github.com/python/cpython/issues/135759.
406+
407+
exc_types = (OverflowError, ValueError)
408+
# HACL* accepts an 'uint32_t' while OpenSSL accepts a 'size_t'.
409+
openssl_overflown_sizes = (sys.maxsize + 1, 2 * sys.maxsize)
410+
# https://github.com/python/cpython/issues/79103 restricts
411+
# the accepted built-in lengths to 2 ** 29, even if OpenSSL
412+
# accepts such lengths.
413+
builtin_overflown_sizes = openssl_overflown_sizes + (
414+
2 ** 29, 2 ** 32 - 10, 2 ** 32, 2 ** 32 + 10,
415+
2 ** 61, 2 ** 64 - 10, 2 ** 64, 2 ** 64 + 10,
416+
)
417+
418+
for constructor in self.shake_constructors:
419+
with self.subTest(constructor=constructor):
420+
h = constructor(usedforsecurity=False)
421+
if HASH is not None and isinstance(h, HASH):
422+
overflown_sizes = openssl_overflown_sizes
423+
else:
424+
overflown_sizes = builtin_overflown_sizes
425+
for invalid_size in overflown_sizes:
426+
self.assertRaises(exc_types, h.digest, invalid_size)
427+
self.assertRaises(exc_types, h.hexdigest, invalid_size)
394428

395429
def test_name_attribute(self):
396430
for cons in self.hash_constructors:

Lib/test/test_sys.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,7 @@ def test_implementation(self):
10741074
self.assertHasAttr(sys.implementation, 'version')
10751075
self.assertHasAttr(sys.implementation, 'hexversion')
10761076
self.assertHasAttr(sys.implementation, 'cache_tag')
1077+
self.assertHasAttr(sys.implementation, 'supports_isolated_interpreters')
10771078

10781079
version = sys.implementation.version
10791080
self.assertEqual(version[:2], (version.major, version.minor))
@@ -1087,6 +1088,15 @@ def test_implementation(self):
10871088
self.assertEqual(sys.implementation.name,
10881089
sys.implementation.name.lower())
10891090

1091+
# https://peps.python.org/pep-0734
1092+
sii = sys.implementation.supports_isolated_interpreters
1093+
self.assertIsInstance(sii, bool)
1094+
if test.support.check_impl_detail(cpython=True):
1095+
if test.support.is_emscripten or test.support.is_wasi:
1096+
self.assertFalse(sii)
1097+
else:
1098+
self.assertTrue(sii)
1099+
10901100
@test.support.cpython_only
10911101
def test_debugmallocstats(self):
10921102
# Test sys._debugmallocstats()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix races on :mod:`heapq` updates and :class:`list` reads on the :term:`free threaded <free threading>`
2+
build.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Added ``supports_isolated_interpreters`` field to
2+
:data:`sys.implementation`.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:mod:`hashlib`: reject negative digest lengths in OpenSSL-based SHAKE objects
2+
by raising a :exc:`ValueError`. Previously, negative lengths were implicitly
3+
rejected by raising a :exc:`MemoryError` or a :exc:`SystemError`.
4+
Patch by Bénédikt Tran.

Modules/_hashopenssl.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -938,8 +938,14 @@ _hashlib_HASHXOF_digest_impl(HASHobject *self, Py_ssize_t length)
938938
/*[clinic end generated code: output=dcb09335dd2fe908 input=3eb034ce03c55b21]*/
939939
{
940940
EVP_MD_CTX *temp_ctx;
941-
PyObject *retval = PyBytes_FromStringAndSize(NULL, length);
941+
PyObject *retval;
942+
943+
if (length < 0) {
944+
PyErr_SetString(PyExc_ValueError, "negative digest length");
945+
return NULL;
946+
}
942947

948+
retval = PyBytes_FromStringAndSize(NULL, length);
943949
if (retval == NULL) {
944950
return NULL;
945951
}
@@ -986,9 +992,14 @@ _hashlib_HASHXOF_hexdigest_impl(HASHobject *self, Py_ssize_t length)
986992
EVP_MD_CTX *temp_ctx;
987993
PyObject *retval;
988994

995+
if (length < 0) {
996+
PyErr_SetString(PyExc_ValueError, "negative digest length");
997+
return NULL;
998+
}
999+
9891000
digest = (unsigned char*)PyMem_Malloc(length);
9901001
if (digest == NULL) {
991-
PyErr_NoMemory();
1002+
(void)PyErr_NoMemory();
9921003
return NULL;
9931004
}
9941005

Modules/_heapqmodule.c

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ annotated by François Pinard, and converted to C by Raymond Hettinger.
1212

1313
#include "Python.h"
1414
#include "pycore_list.h" // _PyList_ITEMS(), _PyList_AppendTakeRef()
15+
#include "pycore_pyatomic_ft_wrappers.h"
1516

1617
#include "clinic/_heapqmodule.c.h"
1718

@@ -59,8 +60,8 @@ siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos)
5960
arr = _PyList_ITEMS(heap);
6061
parent = arr[parentpos];
6162
newitem = arr[pos];
62-
arr[parentpos] = newitem;
63-
arr[pos] = parent;
63+
FT_ATOMIC_STORE_PTR_RELAXED(arr[parentpos], newitem);
64+
FT_ATOMIC_STORE_PTR_RELAXED(arr[pos], parent);
6465
pos = parentpos;
6566
}
6667
return 0;
@@ -108,8 +109,8 @@ siftup(PyListObject *heap, Py_ssize_t pos)
108109
/* Move the smaller child up. */
109110
tmp1 = arr[childpos];
110111
tmp2 = arr[pos];
111-
arr[childpos] = tmp2;
112-
arr[pos] = tmp1;
112+
FT_ATOMIC_STORE_PTR_RELAXED(arr[childpos], tmp2);
113+
FT_ATOMIC_STORE_PTR_RELAXED(arr[pos], tmp1);
113114
pos = childpos;
114115
}
115116
/* Bubble it up to its final resting place (by sifting its parents down). */
@@ -172,8 +173,9 @@ heappop_internal(PyObject *heap, int siftup_func(PyListObject *, Py_ssize_t))
172173
if (!n)
173174
return lastelt;
174175
returnitem = PyList_GET_ITEM(heap, 0);
175-
PyList_SET_ITEM(heap, 0, lastelt);
176-
if (siftup_func((PyListObject *)heap, 0)) {
176+
PyListObject *list = _PyList_CAST(heap);
177+
FT_ATOMIC_STORE_PTR_RELAXED(list->ob_item[0], lastelt);
178+
if (siftup_func(list, 0)) {
177179
Py_DECREF(returnitem);
178180
return NULL;
179181
}
@@ -208,8 +210,9 @@ heapreplace_internal(PyObject *heap, PyObject *item, int siftup_func(PyListObjec
208210
}
209211

210212
returnitem = PyList_GET_ITEM(heap, 0);
211-
PyList_SET_ITEM(heap, 0, Py_NewRef(item));
212-
if (siftup_func((PyListObject *)heap, 0)) {
213+
PyListObject *list = _PyList_CAST(heap);
214+
FT_ATOMIC_STORE_PTR_RELAXED(list->ob_item[0], Py_NewRef(item));
215+
if (siftup_func(list, 0)) {
213216
Py_DECREF(returnitem);
214217
return NULL;
215218
}
@@ -284,8 +287,9 @@ _heapq_heappushpop_impl(PyObject *module, PyObject *heap, PyObject *item)
284287
}
285288

286289
returnitem = PyList_GET_ITEM(heap, 0);
287-
PyList_SET_ITEM(heap, 0, Py_NewRef(item));
288-
if (siftup((PyListObject *)heap, 0)) {
290+
PyListObject *list = _PyList_CAST(heap);
291+
FT_ATOMIC_STORE_PTR_RELAXED(list->ob_item[0], Py_NewRef(item));
292+
if (siftup(list, 0)) {
289293
Py_DECREF(returnitem);
290294
return NULL;
291295
}
@@ -437,8 +441,8 @@ siftdown_max(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos)
437441
arr = _PyList_ITEMS(heap);
438442
parent = arr[parentpos];
439443
newitem = arr[pos];
440-
arr[parentpos] = newitem;
441-
arr[pos] = parent;
444+
FT_ATOMIC_STORE_PTR_RELAXED(arr[parentpos], newitem);
445+
FT_ATOMIC_STORE_PTR_RELAXED(arr[pos], parent);
442446
pos = parentpos;
443447
}
444448
return 0;
@@ -486,8 +490,8 @@ siftup_max(PyListObject *heap, Py_ssize_t pos)
486490
/* Move the smaller child up. */
487491
tmp1 = arr[childpos];
488492
tmp2 = arr[pos];
489-
arr[childpos] = tmp2;
490-
arr[pos] = tmp1;
493+
FT_ATOMIC_STORE_PTR_RELAXED(arr[childpos], tmp2);
494+
FT_ATOMIC_STORE_PTR_RELAXED(arr[pos], tmp1);
491495
pos = childpos;
492496
}
493497
/* Bubble it up to its final resting place (by sifting its parents down). */
@@ -621,8 +625,9 @@ _heapq_heappushpop_max_impl(PyObject *module, PyObject *heap, PyObject *item)
621625
}
622626

623627
returnitem = PyList_GET_ITEM(heap, 0);
624-
PyList_SET_ITEM(heap, 0, Py_NewRef(item));
625-
if (siftup_max((PyListObject *)heap, 0) < 0) {
628+
PyListObject *list = _PyList_CAST(heap);
629+
FT_ATOMIC_STORE_PTR_RELAXED(list->ob_item[0], Py_NewRef(item));
630+
if (siftup_max(list, 0) < 0) {
626631
Py_DECREF(returnitem);
627632
return NULL;
628633
}

0 commit comments

Comments
 (0)