From a912fc5f3b2ae907ce26f75638d4108df96f5334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 27 Dec 2025 13:18:17 +0100 Subject: [PATCH 1/2] gh-142664: fix UAF in `memoryview.__hash__` via re-entrant data's `__hash__` --- Lib/test/test_memoryview.py | 15 +++++++++++++++ ...2025-12-27-13-18-12.gh-issue-142664.peeEDV.rst | 3 +++ Objects/memoryobject.c | 13 ++++++++++--- 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-13-18-12.gh-issue-142664.peeEDV.rst diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 1bd58eb6408833..c1a164e67b3dc8 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -4,6 +4,7 @@ are in test_buffer. """ +import array import unittest import test.support import sys @@ -387,6 +388,20 @@ def test_hash_writable(self): m = self._view(b) self.assertRaises(ValueError, hash, m) + def test_hash_use_after_free(self): + # Prevent crash in memoryview(v).__hash__ with re-entrant v.__hash__. + # Regression test for https://github.com/python/cpython/issues/142664. + class E(array.array): + def __hash__(self): + mv.release() + self.clear() + return 123 + + v = E('B', b'A' * 4096) + mv = memoryview(v).toreadonly() # must be read-only for hash() + self.assertRaises(BufferError, hash, mv) + self.assertRaises(BufferError, mv.__hash__) + def test_weakref(self): # Check memoryviews are weakrefable for tp in self._types: diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-13-18-12.gh-issue-142664.peeEDV.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-13-18-12.gh-issue-142664.peeEDV.rst new file mode 100644 index 00000000000000..39c218395cc4d3 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-13-18-12.gh-issue-142664.peeEDV.rst @@ -0,0 +1,3 @@ +Fix a use-after-free crash in :meth:`memoryview.__hash__ ` +when the ``__hash__`` method of the referenced object mutates that object or +the view. Patch by Bénédikt Tran. diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index f1232f389210ea..5ef4dbb2a882a7 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -3225,9 +3225,16 @@ memory_hash(PyObject *_self) "memoryview: hashing is restricted to formats 'B', 'b' or 'c'"); return -1; } - if (view->obj != NULL && PyObject_Hash(view->obj) == -1) { - /* Keep the original error message */ - return -1; + if (view->obj != NULL) { + // Prevent 'self' from being freed when computing the item's hash. + // See https://github.com/python/cpython/issues/142664. + self->exports++; + int rc = PyObject_Hash(view->obj); + self->exports--; + if (rc == -1) { + /* Keep the original error message */ + return -1; + } } if (!MV_C_CONTIGUOUS(self->flags)) { From bac3b9a008edb0823608950f3a6351187724d5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 27 Dec 2025 13:25:34 +0100 Subject: [PATCH 2/2] remove unused import --- Lib/test/test_memoryview.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index c1a164e67b3dc8..a88413e4bb1d9e 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -4,7 +4,6 @@ are in test_buffer. """ -import array import unittest import test.support import sys