From: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:58:42 +0000 (+0000) Subject: [3.13] gh-143195: fix UAF in `{bytearray,memoryview}.hex(sep)` via re-entrant `sep... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8aca2fd87cda966935b712424a30e4afe54220b9;p=thirdparty%2FPython%2Fcpython.git [3.13] gh-143195: fix UAF in `{bytearray,memoryview}.hex(sep)` via re-entrant `sep.__len__` (GH-143209) (#143220) (cherry picked from commit 9976c2b6349a079ae39931d960b8c147e21c6c3f) --- diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index cc0a46bb56e4..3e948f357797 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -1929,6 +1929,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 diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 69be0e5c8293..91ae688978b0 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -399,6 +399,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 index 000000000000..66dc5e22f0ab --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2025-12-27-10-14-26.gh-issue-143195.MNldfr.rst @@ -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. diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index f940e8c12546..f4e4e2885d80 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2224,7 +2224,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 * diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index b1a58d6b40ed..9773bc2cc1c9 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -2330,7 +2330,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);