Skip to content

Commit cb8b96e

Browse files
[3.14] gh-142664: fix UAF in memoryview.__hash__ via re-entrant data's __hash__ (GH-143217) (#143221)
gh-142664: fix UAF in `memoryview.__hash__` via re-entrant data's `__hash__` (GH-143217) (cherry picked from commit 00e24b8) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
1 parent bae4cd6 commit cb8b96e

File tree

3 files changed

+27
-3
lines changed

3 files changed

+27
-3
lines changed

Lib/test/test_memoryview.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,20 @@ def test_hash_writable(self):
387387
m = self._view(b)
388388
self.assertRaises(ValueError, hash, m)
389389

390+
def test_hash_use_after_free(self):
391+
# Prevent crash in memoryview(v).__hash__ with re-entrant v.__hash__.
392+
# Regression test for https://github.com/python/cpython/issues/142664.
393+
class E(array.array):
394+
def __hash__(self):
395+
mv.release()
396+
self.clear()
397+
return 123
398+
399+
v = E('B', b'A' * 4096)
400+
mv = memoryview(v).toreadonly() # must be read-only for hash()
401+
self.assertRaises(BufferError, hash, mv)
402+
self.assertRaises(BufferError, mv.__hash__)
403+
390404
def test_weakref(self):
391405
# Check memoryviews are weakrefable
392406
for tp in self._types:
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a use-after-free crash in :meth:`memoryview.__hash__ <object.__hash__>`
2+
when the ``__hash__`` method of the referenced object mutates that object or
3+
the view. Patch by Bénédikt Tran.

Objects/memoryobject.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3222,9 +3222,16 @@ memory_hash(PyObject *_self)
32223222
"memoryview: hashing is restricted to formats 'B', 'b' or 'c'");
32233223
return -1;
32243224
}
3225-
if (view->obj != NULL && PyObject_Hash(view->obj) == -1) {
3226-
/* Keep the original error message */
3227-
return -1;
3225+
if (view->obj != NULL) {
3226+
// Prevent 'self' from being freed when computing the item's hash.
3227+
// See https://github.com/python/cpython/issues/142664.
3228+
self->exports++;
3229+
int rc = PyObject_Hash(view->obj);
3230+
self->exports--;
3231+
if (rc == -1) {
3232+
/* Keep the original error message */
3233+
return -1;
3234+
}
32283235
}
32293236

32303237
if (!MV_C_CONTIGUOUS(self->flags)) {

0 commit comments

Comments
 (0)