diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index 253a8f455853f58..ddfacdd14e0a533 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -18,6 +18,8 @@ import tempfile import threading import unittest +from functools import partial +from operator import attrgetter from test import support from test.support import _4G, bigmemtest from test.support import hashlib_helper @@ -56,14 +58,12 @@ def get_fips_mode(): import _blake2 except ImportError: _blake2 = None - requires_blake2 = unittest.skipUnless(_blake2, 'requires _blake2') try: import _sha3 except ImportError: _sha3 = None - requires_sha3 = unittest.skipUnless(_sha3, 'requires _sha3') @@ -1418,5 +1418,42 @@ def scrypt(password=b"password", /, **kwargs): self.assertRaises(numeric_exc_types, scrypt, dklen=MAX_DKLEN + 1) +@threading_helper.requires_working_threading() +class TestTSAN(unittest.TestCase): + + @threading_helper.reap_threads + def check_attribute(self, write, read, expected, nthreads=8): + ready = threading.Event() + barrier = threading.Barrier(nthreads) + + def writer(): + barrier.wait() + while not ready.is_set(): + write() + + def reader(): + barrier.wait() + while not ready.is_set(): + self.assertEqual(read(), expected) + + targets = [writer if i % 2 else reader for i in range(nthreads)] + workers = [threading.Thread(target=target) for target in targets] + with threading_helper.start_threads(workers, unlock=ready.set): + pass + + def check_HACL_attribute(self, module, version, attrname): + blob = b"A" * 65536 + obj = getattr(module, version)() + update = partial(obj.update, blob) + read = attrgetter(attrname) + self.check_attribute(update, partial(read, obj), read(obj)) + + @requires_blake2 + @support.subTests("version", ["blake2s", "blake2b"]) + @support.subTests("attrname", ["block_size", "digest_size"]) + def test_HACL_blake2_attributes(self, version, attrname): + self.check_HACL_attribute(_blake2, version, attrname) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2026-08-15-13-18-01.gh-issue-155835.mlyLWp.rst b/Misc/NEWS.d/next/Library/2026-08-15-13-18-01.gh-issue-155835.mlyLWp.rst new file mode 100644 index 000000000000000..9ef7d0694e66c31 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-15-13-18-01.gh-issue-155835.mlyLWp.rst @@ -0,0 +1,2 @@ +:mod:`hashlib`: Fix a data race when accessing :attr:`~hashlib.hash.digest_size` +on BLAKE-2 objects. Patch by Bénédikt Tran. diff --git a/Modules/blake2module.c b/Modules/blake2module.c index ac7265bb9d6836c..c69c4259b12666f 100644 --- a/Modules/blake2module.c +++ b/Modules/blake2module.c @@ -950,7 +950,9 @@ static PyObject * py_blake2b_get_digest_size(PyObject *op, void *Py_UNUSED(closure)) { Blake2Object *self = _Blake2Object_CAST(op); + HASHLIB_ACQUIRE_LOCK(self); Hacl_Hash_Blake2b_index info = hacl_get_blake2_info(self); + HASHLIB_RELEASE_LOCK(self); return PyLong_FromLong(info.digest_length); }