]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-143195: fix UAF in `{bytearray,memoryview}.hex(sep)` via re-entrant `sep...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Sat, 27 Dec 2025 13:57:41 +0000 (14:57 +0100)
committerGitHub <noreply@github.com>
Sat, 27 Dec 2025 13:57:41 +0000 (13:57 +0000)
gh-143195: fix UAF in `{bytearray,memoryview}.hex(sep)` via re-entrant `sep.__len__` (GH-143209)
(cherry picked from commit 9976c2b6349a079ae39931d960b8c147e21c6c3f)

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Lib/test/test_bytes.py
Lib/test/test_memoryview.py
Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-10-14-26.gh-issue-143195.MNldfr.rst [new file with mode: 0644]
Objects/bytearrayobject.c
Objects/memoryobject.c

index 0ab330fdb0b107d3c420f11655217c33282a9c0d..54604376863d35d41e8b50800f294ef47d4e0b10 100644 (file)
@@ -2001,6 +2001,19 @@ class ByteArrayTest(BaseBytesTest, unittest.TestCase):
         with self.assertRaises(BufferError):
             ba.rsplit(evil)
 
+    def test_hex_use_after_free(self):
+        # Prevent UAF in bytearray.hex(sep) with re-entrant sep.__len__.
+        # Regression test for https://github.com/python/cpython/issues/143195.
+        ba = bytearray(b'\xAA')
+
+        class S(bytes):
+            def __len__(self):
+                ba.clear()
+                return 1
+
+        self.assertRaises(BufferError, ba.hex, S(b':'))
+
+
 class AssortedBytesTest(unittest.TestCase):
     #
     # Test various combinations of bytes and bytearray
index a88413e4bb1d9e35619932dcb4adad47ab69d8a7..656318668e6d6e77fa799e35fde76e0460518aaa 100644 (file)
@@ -456,6 +456,20 @@ class AbstractMemoryTests:
         self.assertEqual(c.format, "H")
         self.assertEqual(d.format, "H")
 
+    def test_hex_use_after_free(self):
+        # Prevent UAF in memoryview.hex(sep) with re-entrant sep.__len__.
+        # Regression test for https://github.com/python/cpython/issues/143195.
+        ba = bytearray(b'A' * 1024)
+        mv = memoryview(ba)
+
+        class S(bytes):
+            def __len__(self):
+                mv.release()
+                ba.clear()
+                return 1
+
+        self.assertRaises(BufferError, mv.hex, S(b':'))
+
 
 # Variations on source objects for the buffer: bytes-like objects, then arrays
 # with itemsize > 1.
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-10-14-26.gh-issue-143195.MNldfr.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-10-14-26.gh-issue-143195.MNldfr.rst
new file mode 100644 (file)
index 0000000..66dc5e2
--- /dev/null
@@ -0,0 +1,3 @@
+Fix use-after-free crashes in :meth:`bytearray.hex` and :meth:`memoryview.hex`
+when the separator's :meth:`~object.__len__` mutates the original object.
+Patch by Bénédikt Tran.
index 7ad71d93f0c1fa3212461657f266fbce593d1e7f..3778a37daac818013af11a277f674943c9ac4ac7 100644 (file)
@@ -2616,7 +2616,13 @@ bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, int bytes_per_sep)
 {
     char* argbuf = PyByteArray_AS_STRING(self);
     Py_ssize_t arglen = PyByteArray_GET_SIZE(self);
-    return _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
+    // Prevent 'self' from being freed if computing len(sep) mutates 'self'
+    // in _Py_strhex_with_sep().
+    // See: https://github.com/python/cpython/issues/143195.
+    self->ob_exports++;
+    PyObject *res = _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
+    self->ob_exports--;
+    return res;
 }
 
 static PyObject *
index cf570d091102f322926e79149fc685cd7a32a5e1..e78f5d110a457f485bf2aeb82d778ff1843ad7ec 100644 (file)
@@ -2349,7 +2349,13 @@ memoryview_hex_impl(PyMemoryViewObject *self, PyObject *sep,
     CHECK_RELEASED(self);
 
     if (MV_C_CONTIGUOUS(self->flags)) {
-        return _Py_strhex_with_sep(src->buf, src->len, sep, bytes_per_sep);
+        // Prevent 'self' from being freed if computing len(sep) mutates 'self'
+        // in _Py_strhex_with_sep().
+        // See: https://github.com/python/cpython/issues/143195.
+        self->exports++;
+        PyObject *ret = _Py_strhex_with_sep(src->buf, src->len, sep, bytes_per_sep);
+        self->exports--;
+        return ret;
     }
 
     bytes = PyBytes_FromStringAndSize(NULL, src->len);