diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index b2f4363b23e748..d37b47a5432826 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -1902,6 +1902,17 @@ def test_hash(self): with self.assertRaisesRegex(TypeError, "unhashable type: 'list'"): hash(fd) + def test_hash_pipe_operator(self): + # gh-149676: frozendict created via | must have the same hash as one + # created directly with the same contents (ma_hash must be initialised + # to -1 so that the hash is computed on first call, not left as garbage) + a = frozendict({"a": 1}) + b = frozendict({"b": 2}) + c = frozendict({"a": 1, "b": 2}) + c_union = a | b + self.assertEqual(c, c_union) + self.assertEqual(hash(c), hash(c_union)) + def test_fromkeys(self): self.assertEqual(frozendict.fromkeys('abc'), frozendict(a=None, b=None, c=None)) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-14-48-56.gh-issue-149676.6aTrw1.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-14-48-56.gh-issue-149676.6aTrw1.rst new file mode 100644 index 00000000000000..bb5e83aa8a1b91 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-14-48-56.gh-issue-149676.6aTrw1.rst @@ -0,0 +1 @@ +Fix :class:`frozendict` instances being created with an uninitialised hash value, which could cause incorrect hash lookups or crashes. diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 42bc63acd9049c..fea65129dab55a 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -940,7 +940,13 @@ new_frozendict(PyDictKeysObject *keys, PyDictValues *values, Py_ssize_t used, int free_values_on_failure) { PyDictObject *mp = PyObject_GC_New(PyDictObject, &PyFrozenDict_Type); - return new_dict_impl(mp, keys, values, used, free_values_on_failure); + PyObject *result = new_dict_impl(mp, keys, values, used, free_values_on_failure); + if (result != NULL) { + /* ma_hash must be -1 (sentinel for "not computed") since PyObject_GC_New + does not zero-initialize memory and new_dict_impl does not touch ma_hash. */ + _PyFrozenDictObject_CAST(result)->ma_hash = -1; + } + return result; } static PyObject *