diff options
-rw-r--r-- | Doc/c-api/extension-modules.rst | 4 | ||||
-rw-r--r-- | Doc/library/dialog.rst | 2 | ||||
-rw-r--r-- | Doc/library/sys.rst | 12 | ||||
-rw-r--r-- | Lib/test/libregrtest/tsan.py | 2 | ||||
-rw-r--r-- | Lib/test/test_free_threading/test_heapq.py | 29 | ||||
-rw-r--r-- | Lib/test/test_hashlib.py | 64 | ||||
-rw-r--r-- | Lib/test/test_os.py | 4 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 10 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2025-06-17-23-13-56.gh-issue-135557.Bfcy4v.rst | 2 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2025-06-18-13-58-13.gh-issue-135645.109nff.rst | 2 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2025-06-20-16-28-47.gh-issue-135759.jne0Zi.rst | 4 | ||||
-rw-r--r-- | Modules/_hashopenssl.c | 23 | ||||
-rw-r--r-- | Modules/_heapqmodule.c | 37 | ||||
-rw-r--r-- | Modules/clinic/sha3module.c.h | 38 | ||||
-rw-r--r-- | Modules/sha3module.c | 108 | ||||
-rw-r--r-- | Python/sysmodule.c | 12 |
16 files changed, 269 insertions, 84 deletions
diff --git a/Doc/c-api/extension-modules.rst b/Doc/c-api/extension-modules.rst index 4c8212f2f5e..3d331e6ec12 100644 --- a/Doc/c-api/extension-modules.rst +++ b/Doc/c-api/extension-modules.rst @@ -242,6 +242,6 @@ in the following ways: * Single-phase modules support module lookup functions like :c:func:`PyState_FindModule`. -.. [#testsinglephase] ``_testsinglephase`` is an internal module used \ - in CPython's self-test suite; your installation may or may not \ +.. [#testsinglephase] ``_testsinglephase`` is an internal module used + in CPython's self-test suite; your installation may or may not include it. diff --git a/Doc/library/dialog.rst b/Doc/library/dialog.rst index 191e0da1210..e0693e8eb6e 100644 --- a/Doc/library/dialog.rst +++ b/Doc/library/dialog.rst @@ -220,7 +220,7 @@ is the base class for dialogs defined in other supporting modules. .. class:: Dialog(master=None, **options) - .. method:: show(color=None, **options) + .. method:: show(**options) Render the Dialog window. diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index f8f727f4a23..1626a89a073 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1185,6 +1185,15 @@ always available. Unless explicitly noted otherwise, all variables are read-only ``cache_tag`` is set to ``None``, it indicates that module caching should be disabled. + *supports_isolated_interpreters* is a boolean value, whether + this implementation supports multiple isolated interpreters. + It is ``True`` for CPython on most platforms. Platforms with + this support implement the low-level :mod:`!_interpreters` module. + + .. seealso:: + + :pep:`684`, :pep:`734`, and :mod:`concurrent.interpreters`. + :data:`sys.implementation` may contain additional attributes specific to the Python implementation. These non-standard attributes must start with 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 .. versionadded:: 3.3 + .. versionchanged:: 3.14 + Added ``supports_isolated_interpreters`` field. + .. note:: The addition of new required attributes must go through the normal PEP diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py index d984a735bdf..3545c5f999f 100644 --- a/Lib/test/libregrtest/tsan.py +++ b/Lib/test/libregrtest/tsan.py @@ -8,7 +8,7 @@ TSAN_TESTS = [ 'test_capi.test_pyatomic', 'test_code', 'test_ctypes', - # 'test_concurrent_futures', # gh-130605: too many data races + 'test_concurrent_futures', 'test_enum', 'test_functools', 'test_httpservers', diff --git a/Lib/test/test_free_threading/test_heapq.py b/Lib/test/test_free_threading/test_heapq.py index f75fb264c8a..ee7adfb2b78 100644 --- a/Lib/test/test_free_threading/test_heapq.py +++ b/Lib/test/test_free_threading/test_heapq.py @@ -3,7 +3,7 @@ import unittest import heapq from enum import Enum -from threading import Thread, Barrier +from threading import Thread, Barrier, Lock from random import shuffle, randint from test.support import threading_helper @@ -178,6 +178,33 @@ class TestHeapq(unittest.TestCase): self.assertEqual(len(max_heap), OBJECT_COUNT) self.test_heapq.check_max_invariant(max_heap) + def test_lock_free_list_read(self): + n, n_threads = 1_000, 10 + l = [] + barrier = Barrier(n_threads * 2) + + count = 0 + lock = Lock() + + def worker(): + with lock: + nonlocal count + x = count + count += 1 + + barrier.wait() + for i in range(n): + if x % 2: + heapq.heappush(l, 1) + heapq.heappop(l) + else: + try: + l[0] + except IndexError: + pass + + self.run_concurrently(worker, (), n_threads * 2) + @staticmethod def is_sorted_ascending(lst): """ diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index b83ae181718..e77343f8278 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -208,6 +208,11 @@ class HashLibTestCase(unittest.TestCase): return itertools.chain.from_iterable(constructors) @property + def shake_constructors(self): + for shake_name in self.shakes: + yield from self.constructors_to_test.get(shake_name, ()) + + @property def is_fips_mode(self): return get_fips_mode() @@ -376,21 +381,50 @@ class HashLibTestCase(unittest.TestCase): self.assertIsInstance(h.digest(), bytes) self.assertEqual(hexstr(h.digest()), h.hexdigest()) - def test_digest_length_overflow(self): - # See issue #34922 - large_sizes = (2**29, 2**32-10, 2**32+10, 2**61, 2**64-10, 2**64+10) - for cons in self.hash_constructors: - h = cons(usedforsecurity=False) - if h.name not in self.shakes: - continue - if HASH is not None and isinstance(h, HASH): - # _hashopenssl's take a size_t - continue - for digest in h.digest, h.hexdigest: - self.assertRaises(ValueError, digest, -10) - for length in large_sizes: - with self.assertRaises((ValueError, OverflowError)): - digest(length) + def test_shakes_zero_digest_length(self): + for constructor in self.shake_constructors: + with self.subTest(constructor=constructor): + h = constructor(b'abcdef', usedforsecurity=False) + self.assertEqual(h.digest(0), b'') + self.assertEqual(h.hexdigest(0), '') + + def test_shakes_invalid_digest_length(self): + # See https://github.com/python/cpython/issues/79103. + for constructor in self.shake_constructors: + with self.subTest(constructor=constructor): + h = constructor(usedforsecurity=False) + # Note: digest() and hexdigest() take a signed input and + # raise if it is negative; the rationale is that we use + # internally PyBytes_FromStringAndSize() and _Py_strhex() + # which both take a Py_ssize_t. + for negative_size in (-1, -10, -(1 << 31), -sys.maxsize): + self.assertRaises(ValueError, h.digest, negative_size) + self.assertRaises(ValueError, h.hexdigest, negative_size) + + def test_shakes_overflow_digest_length(self): + # See https://github.com/python/cpython/issues/135759. + + exc_types = (OverflowError, ValueError) + # HACL* accepts an 'uint32_t' while OpenSSL accepts a 'size_t'. + openssl_overflown_sizes = (sys.maxsize + 1, 2 * sys.maxsize) + # https://github.com/python/cpython/issues/79103 restricts + # the accepted built-in lengths to 2 ** 29, even if OpenSSL + # accepts such lengths. + builtin_overflown_sizes = openssl_overflown_sizes + ( + 2 ** 29, 2 ** 32 - 10, 2 ** 32, 2 ** 32 + 10, + 2 ** 61, 2 ** 64 - 10, 2 ** 64, 2 ** 64 + 10, + ) + + for constructor in self.shake_constructors: + with self.subTest(constructor=constructor): + h = constructor(usedforsecurity=False) + if HASH is not None and isinstance(h, HASH): + overflown_sizes = openssl_overflown_sizes + else: + overflown_sizes = builtin_overflown_sizes + for invalid_size in overflown_sizes: + self.assertRaises(exc_types, h.digest, invalid_size) + self.assertRaises(exc_types, h.hexdigest, invalid_size) def test_name_attribute(self): for cons in self.hash_constructors: diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 8f56b4559f4..5217037ae9d 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1918,6 +1918,10 @@ class MakedirTests(unittest.TestCase): support.is_wasi, "WASI's umask is a stub." ) + @unittest.skipIf( + support.is_emscripten, + "TODO: Fails in buildbot; see #135783" + ) def test_mode(self): with os_helper.temp_umask(0o002): base = os_helper.TESTFN diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 73a72024bba..27524d86355 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1074,6 +1074,7 @@ class SysModuleTest(unittest.TestCase): self.assertHasAttr(sys.implementation, 'version') self.assertHasAttr(sys.implementation, 'hexversion') self.assertHasAttr(sys.implementation, 'cache_tag') + self.assertHasAttr(sys.implementation, 'supports_isolated_interpreters') version = sys.implementation.version self.assertEqual(version[:2], (version.major, version.minor)) @@ -1087,6 +1088,15 @@ class SysModuleTest(unittest.TestCase): self.assertEqual(sys.implementation.name, sys.implementation.name.lower()) + # https://peps.python.org/pep-0734 + sii = sys.implementation.supports_isolated_interpreters + self.assertIsInstance(sii, bool) + if test.support.check_impl_detail(cpython=True): + if test.support.is_emscripten or test.support.is_wasi: + self.assertFalse(sii) + else: + self.assertTrue(sii) + @test.support.cpython_only def test_debugmallocstats(self): # Test sys._debugmallocstats() diff --git a/Misc/NEWS.d/next/Library/2025-06-17-23-13-56.gh-issue-135557.Bfcy4v.rst b/Misc/NEWS.d/next/Library/2025-06-17-23-13-56.gh-issue-135557.Bfcy4v.rst new file mode 100644 index 00000000000..eabf5ea4aaa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-17-23-13-56.gh-issue-135557.Bfcy4v.rst @@ -0,0 +1,2 @@ +Fix races on :mod:`heapq` updates and :class:`list` reads on the :term:`free threaded <free threading>` +build. diff --git a/Misc/NEWS.d/next/Library/2025-06-18-13-58-13.gh-issue-135645.109nff.rst b/Misc/NEWS.d/next/Library/2025-06-18-13-58-13.gh-issue-135645.109nff.rst new file mode 100644 index 00000000000..a7764a0105b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-18-13-58-13.gh-issue-135645.109nff.rst @@ -0,0 +1,2 @@ +Added ``supports_isolated_interpreters`` field to +:data:`sys.implementation`. diff --git a/Misc/NEWS.d/next/Library/2025-06-20-16-28-47.gh-issue-135759.jne0Zi.rst b/Misc/NEWS.d/next/Library/2025-06-20-16-28-47.gh-issue-135759.jne0Zi.rst new file mode 100644 index 00000000000..268d7eccdab --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-20-16-28-47.gh-issue-135759.jne0Zi.rst @@ -0,0 +1,4 @@ +:mod:`hashlib`: reject negative digest lengths in OpenSSL-based SHAKE objects +by raising a :exc:`ValueError`. Previously, negative lengths were implicitly +rejected by raising a :exc:`MemoryError` or a :exc:`SystemError`. +Patch by Bénédikt Tran. diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index ce9603d5db8..20eba9dd77b 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -938,8 +938,18 @@ _hashlib_HASHXOF_digest_impl(HASHobject *self, Py_ssize_t length) /*[clinic end generated code: output=dcb09335dd2fe908 input=3eb034ce03c55b21]*/ { EVP_MD_CTX *temp_ctx; - PyObject *retval = PyBytes_FromStringAndSize(NULL, length); + PyObject *retval; + + if (length < 0) { + PyErr_SetString(PyExc_ValueError, "negative digest length"); + return NULL; + } + + if (length == 0) { + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } + retval = PyBytes_FromStringAndSize(NULL, length); if (retval == NULL) { return NULL; } @@ -986,9 +996,18 @@ _hashlib_HASHXOF_hexdigest_impl(HASHobject *self, Py_ssize_t length) EVP_MD_CTX *temp_ctx; PyObject *retval; + if (length < 0) { + PyErr_SetString(PyExc_ValueError, "negative digest length"); + return NULL; + } + + if (length == 0) { + return Py_GetConstant(Py_CONSTANT_EMPTY_STR); + } + digest = (unsigned char*)PyMem_Malloc(length); if (digest == NULL) { - PyErr_NoMemory(); + (void)PyErr_NoMemory(); return NULL; } diff --git a/Modules/_heapqmodule.c b/Modules/_heapqmodule.c index 7784cdcd9ff..560fe431fca 100644 --- a/Modules/_heapqmodule.c +++ b/Modules/_heapqmodule.c @@ -12,6 +12,7 @@ annotated by François Pinard, and converted to C by Raymond Hettinger. #include "Python.h" #include "pycore_list.h" // _PyList_ITEMS(), _PyList_AppendTakeRef() +#include "pycore_pyatomic_ft_wrappers.h" #include "clinic/_heapqmodule.c.h" @@ -59,8 +60,8 @@ siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) arr = _PyList_ITEMS(heap); parent = arr[parentpos]; newitem = arr[pos]; - arr[parentpos] = newitem; - arr[pos] = parent; + FT_ATOMIC_STORE_PTR_RELAXED(arr[parentpos], newitem); + FT_ATOMIC_STORE_PTR_RELAXED(arr[pos], parent); pos = parentpos; } return 0; @@ -108,8 +109,8 @@ siftup(PyListObject *heap, Py_ssize_t pos) /* Move the smaller child up. */ tmp1 = arr[childpos]; tmp2 = arr[pos]; - arr[childpos] = tmp2; - arr[pos] = tmp1; + FT_ATOMIC_STORE_PTR_RELAXED(arr[childpos], tmp2); + FT_ATOMIC_STORE_PTR_RELAXED(arr[pos], tmp1); pos = childpos; } /* 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)) if (!n) return lastelt; returnitem = PyList_GET_ITEM(heap, 0); - PyList_SET_ITEM(heap, 0, lastelt); - if (siftup_func((PyListObject *)heap, 0)) { + PyListObject *list = _PyList_CAST(heap); + FT_ATOMIC_STORE_PTR_RELAXED(list->ob_item[0], lastelt); + if (siftup_func(list, 0)) { Py_DECREF(returnitem); return NULL; } @@ -208,8 +210,9 @@ heapreplace_internal(PyObject *heap, PyObject *item, int siftup_func(PyListObjec } returnitem = PyList_GET_ITEM(heap, 0); - PyList_SET_ITEM(heap, 0, Py_NewRef(item)); - if (siftup_func((PyListObject *)heap, 0)) { + PyListObject *list = _PyList_CAST(heap); + FT_ATOMIC_STORE_PTR_RELAXED(list->ob_item[0], Py_NewRef(item)); + if (siftup_func(list, 0)) { Py_DECREF(returnitem); return NULL; } @@ -284,8 +287,9 @@ _heapq_heappushpop_impl(PyObject *module, PyObject *heap, PyObject *item) } returnitem = PyList_GET_ITEM(heap, 0); - PyList_SET_ITEM(heap, 0, Py_NewRef(item)); - if (siftup((PyListObject *)heap, 0)) { + PyListObject *list = _PyList_CAST(heap); + FT_ATOMIC_STORE_PTR_RELAXED(list->ob_item[0], Py_NewRef(item)); + if (siftup(list, 0)) { Py_DECREF(returnitem); return NULL; } @@ -437,8 +441,8 @@ siftdown_max(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) arr = _PyList_ITEMS(heap); parent = arr[parentpos]; newitem = arr[pos]; - arr[parentpos] = newitem; - arr[pos] = parent; + FT_ATOMIC_STORE_PTR_RELAXED(arr[parentpos], newitem); + FT_ATOMIC_STORE_PTR_RELAXED(arr[pos], parent); pos = parentpos; } return 0; @@ -486,8 +490,8 @@ siftup_max(PyListObject *heap, Py_ssize_t pos) /* Move the smaller child up. */ tmp1 = arr[childpos]; tmp2 = arr[pos]; - arr[childpos] = tmp2; - arr[pos] = tmp1; + FT_ATOMIC_STORE_PTR_RELAXED(arr[childpos], tmp2); + FT_ATOMIC_STORE_PTR_RELAXED(arr[pos], tmp1); pos = childpos; } /* 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) } returnitem = PyList_GET_ITEM(heap, 0); - PyList_SET_ITEM(heap, 0, Py_NewRef(item)); - if (siftup_max((PyListObject *)heap, 0) < 0) { + PyListObject *list = _PyList_CAST(heap); + FT_ATOMIC_STORE_PTR_RELAXED(list->ob_item[0], Py_NewRef(item)); + if (siftup_max(list, 0) < 0) { Py_DECREF(returnitem); return NULL; } diff --git a/Modules/clinic/sha3module.c.h b/Modules/clinic/sha3module.c.h index 121be2c0758..d6d44bf4c51 100644 --- a/Modules/clinic/sha3module.c.h +++ b/Modules/clinic/sha3module.c.h @@ -6,7 +6,7 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif -#include "pycore_long.h" // _PyLong_UnsignedLong_Converter() +#include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(py_sha3_new__doc__, @@ -179,7 +179,7 @@ PyDoc_STRVAR(_sha3_shake_128_digest__doc__, {"digest", _PyCFunction_CAST(_sha3_shake_128_digest), METH_FASTCALL|METH_KEYWORDS, _sha3_shake_128_digest__doc__}, static PyObject * -_sha3_shake_128_digest_impl(SHA3object *self, unsigned long length); +_sha3_shake_128_digest_impl(SHA3object *self, Py_ssize_t length); static PyObject * _sha3_shake_128_digest(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -213,15 +213,24 @@ _sha3_shake_128_digest(PyObject *self, PyObject *const *args, Py_ssize_t nargs, }; #undef KWTUPLE PyObject *argsbuf[1]; - unsigned long length; + Py_ssize_t length; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } - if (!_PyLong_UnsignedLong_Converter(args[0], &length)) { - goto exit; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[0]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + length = ival; } return_value = _sha3_shake_128_digest_impl((SHA3object *)self, length); @@ -239,7 +248,7 @@ PyDoc_STRVAR(_sha3_shake_128_hexdigest__doc__, {"hexdigest", _PyCFunction_CAST(_sha3_shake_128_hexdigest), METH_FASTCALL|METH_KEYWORDS, _sha3_shake_128_hexdigest__doc__}, static PyObject * -_sha3_shake_128_hexdigest_impl(SHA3object *self, unsigned long length); +_sha3_shake_128_hexdigest_impl(SHA3object *self, Py_ssize_t length); static PyObject * _sha3_shake_128_hexdigest(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -273,19 +282,28 @@ _sha3_shake_128_hexdigest(PyObject *self, PyObject *const *args, Py_ssize_t narg }; #undef KWTUPLE PyObject *argsbuf[1]; - unsigned long length; + Py_ssize_t length; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } - if (!_PyLong_UnsignedLong_Converter(args[0], &length)) { - goto exit; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[0]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + length = ival; } return_value = _sha3_shake_128_hexdigest_impl((SHA3object *)self, length); exit: return return_value; } -/*[clinic end generated code: output=65e437799472b89f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c17e3ec670afe253 input=a9049054013a1b77]*/ diff --git a/Modules/sha3module.c b/Modules/sha3module.c index cfbf0cbcc04..9946024fe2e 100644 --- a/Modules/sha3module.c +++ b/Modules/sha3module.c @@ -24,6 +24,17 @@ #include "pycore_typeobject.h" // _PyType_GetModuleState() #include "hashlib.h" +/* + * Assert that 'LEN' can be safely casted to uint32_t. + * + * The 'LEN' parameter should be convertible to Py_ssize_t. + */ +#if !defined(NDEBUG) && (PY_SSIZE_T_MAX > UINT32_MAX) +#define CHECK_HACL_UINT32_T_LENGTH(LEN) assert((LEN) < (Py_ssize_t)UINT32_MAX) +#else +#define CHECK_HACL_UINT32_T_LENGTH(LEN) +#endif + #define SHA3_MAX_DIGESTSIZE 64 /* 64 Bytes (512 Bits) for 224 to 512 */ typedef struct { @@ -472,69 +483,94 @@ SHA3_TYPE_SPEC(sha3_384_spec, "sha3_384", sha3_384_slots); SHA3_TYPE_SLOTS(sha3_512_slots, sha3_512__doc__, SHA3_methods, SHA3_getseters); SHA3_TYPE_SPEC(sha3_512_spec, "sha3_512", sha3_512_slots); -static PyObject * -_SHAKE_digest(PyObject *op, unsigned long digestlen, int hex) +static int +sha3_shake_check_digest_length(Py_ssize_t length) { - unsigned char *digest = NULL; - PyObject *result = NULL; - SHA3object *self = _SHA3object_CAST(op); - - if (digestlen >= (1 << 29)) { - PyErr_SetString(PyExc_ValueError, "length is too large"); - return NULL; - } - digest = (unsigned char*)PyMem_Malloc(digestlen); - if (digest == NULL) { - return PyErr_NoMemory(); - } - - /* Get the raw (binary) digest value. The HACL functions errors out if: - * - the algorithm is not shake -- not the case here - * - the output length is zero -- we follow the existing behavior and return - * an empty digest, without raising an error */ - if (digestlen > 0) { - (void)Hacl_Hash_SHA3_squeeze(self->hash_state, digest, digestlen); - } - if (hex) { - result = _Py_strhex((const char *)digest, digestlen); + if (length < 0) { + PyErr_SetString(PyExc_ValueError, "negative digest length"); + return -1; } - else { - result = PyBytes_FromStringAndSize((const char *)digest, digestlen); + if ((size_t)length >= (1 << 29)) { + /* + * Raise OverflowError to match the semantics of OpenSSL SHAKE + * when the digest length exceeds the range of a 'Py_ssize_t'; + * the exception message will however be different in this case. + */ + PyErr_SetString(PyExc_OverflowError, "digest length is too large"); + return -1; } - PyMem_Free(digest); - return result; + return 0; } /*[clinic input] _sha3.shake_128.digest - length: unsigned_long + length: Py_ssize_t Return the digest value as a bytes object. [clinic start generated code]*/ static PyObject * -_sha3_shake_128_digest_impl(SHA3object *self, unsigned long length) -/*[clinic end generated code: output=2313605e2f87bb8f input=93d6d6ff32904f18]*/ +_sha3_shake_128_digest_impl(SHA3object *self, Py_ssize_t length) +/*[clinic end generated code: output=6c53fb71a6cff0a0 input=be03ade4b31dd54c]*/ { - return _SHAKE_digest((PyObject *)self, length, 0); + if (sha3_shake_check_digest_length(length) < 0) { + return NULL; + } + + /* + * Hacl_Hash_SHA3_squeeze() fails if the algorithm is not SHAKE, + * or if the length is 0. In the latter case, we follow OpenSSL's + * behavior and return an empty digest, without raising an error. + */ + if (length == 0) { + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } + + CHECK_HACL_UINT32_T_LENGTH(length); + PyObject *digest = PyBytes_FromStringAndSize(NULL, length); + uint8_t *buffer = (uint8_t *)PyBytes_AS_STRING(digest); + ENTER_HASHLIB(self); + (void)Hacl_Hash_SHA3_squeeze(self->hash_state, buffer, (uint32_t)length); + LEAVE_HASHLIB(self); + return digest; } /*[clinic input] _sha3.shake_128.hexdigest - length: unsigned_long + length: Py_ssize_t Return the digest value as a string of hexadecimal digits. [clinic start generated code]*/ static PyObject * -_sha3_shake_128_hexdigest_impl(SHA3object *self, unsigned long length) -/*[clinic end generated code: output=bf8e2f1e490944a8 input=562d74e7060b56ab]*/ +_sha3_shake_128_hexdigest_impl(SHA3object *self, Py_ssize_t length) +/*[clinic end generated code: output=a27412d404f64512 input=0d84d05d7a8ccd37]*/ { - return _SHAKE_digest((PyObject *)self, length, 1); + if (sha3_shake_check_digest_length(length) < 0) { + return NULL; + } + + /* See _sha3_shake_128_digest_impl() for the fast path rationale. */ + if (length == 0) { + return Py_GetConstant(Py_CONSTANT_EMPTY_STR); + } + + CHECK_HACL_UINT32_T_LENGTH(length); + uint8_t *buffer = PyMem_Malloc(length); + if (buffer == NULL) { + return PyErr_NoMemory(); + } + + ENTER_HASHLIB(self); + (void)Hacl_Hash_SHA3_squeeze(self->hash_state, buffer, (uint32_t)length); + LEAVE_HASHLIB(self); + PyObject *digest = _Py_strhex((const char *)buffer, length); + PyMem_Free(buffer); + return digest; } static PyObject * diff --git a/Python/sysmodule.c b/Python/sysmodule.c index b3a2512a99d..ae6cf306735 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -3607,6 +3607,18 @@ make_impl_info(PyObject *version_info) goto error; #endif + // PEP-734 +#if defined(__wasi__) || defined(__EMSCRIPTEN__) + // It is not enabled on WASM builds just yet + value = Py_False; +#else + value = Py_True; +#endif + res = PyDict_SetItemString(impl_info, "supports_isolated_interpreters", value); + if (res < 0) { + goto error; + } + /* dict ready */ ns = _PyNamespace_New(impl_info); |