[3.14] gh-143195: fix UAF in {bytearray,memoryview}.hex(sep) via re-entrant sep.__len__ (GH-143209) (#143219)

gh-143195: fix UAF in `{bytearray,memoryview}.hex(sep)` via re-entrant `sep.__len__` (GH-143209)
(cherry picked from commit 9976c2b634)

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
Miss Islington (bot) 2025-12-27 14:57:41 +01:00 committed by GitHub
parent cb8b96ed25
commit 93029e45b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 44 additions and 2 deletions

View file

@ -2001,6 +2001,19 @@ def make_case():
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

View file

@ -456,6 +456,20 @@ def test_issue22668(self):
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.

View file

@ -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.

View 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 *

View 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);