Skip to content

Commit 93029e4

Browse files
[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 9976c2b) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
1 parent cb8b96e commit 93029e4

File tree

5 files changed

+44
-2
lines changed

5 files changed

+44
-2
lines changed

Lib/test/test_bytes.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,19 @@ def make_case():
20012001
with self.assertRaises(BufferError):
20022002
ba.rsplit(evil)
20032003

2004+
def test_hex_use_after_free(self):
2005+
# Prevent UAF in bytearray.hex(sep) with re-entrant sep.__len__.
2006+
# Regression test for https://github.com/python/cpython/issues/143195.
2007+
ba = bytearray(b'\xAA')
2008+
2009+
class S(bytes):
2010+
def __len__(self):
2011+
ba.clear()
2012+
return 1
2013+
2014+
self.assertRaises(BufferError, ba.hex, S(b':'))
2015+
2016+
20042017
class AssortedBytesTest(unittest.TestCase):
20052018
#
20062019
# Test various combinations of bytes and bytearray

Lib/test/test_memoryview.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,20 @@ def test_issue22668(self):
456456
self.assertEqual(c.format, "H")
457457
self.assertEqual(d.format, "H")
458458

459+
def test_hex_use_after_free(self):
460+
# Prevent UAF in memoryview.hex(sep) with re-entrant sep.__len__.
461+
# Regression test for https://github.com/python/cpython/issues/143195.
462+
ba = bytearray(b'A' * 1024)
463+
mv = memoryview(ba)
464+
465+
class S(bytes):
466+
def __len__(self):
467+
mv.release()
468+
ba.clear()
469+
return 1
470+
471+
self.assertRaises(BufferError, mv.hex, S(b':'))
472+
459473

460474
# Variations on source objects for the buffer: bytes-like objects, then arrays
461475
# with itemsize > 1.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix use-after-free crashes in :meth:`bytearray.hex` and :meth:`memoryview.hex`
2+
when the separator's :meth:`~object.__len__` mutates the original object.
3+
Patch by Bénédikt Tran.

Objects/bytearrayobject.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2616,7 +2616,13 @@ bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, int bytes_per_sep)
26162616
{
26172617
char* argbuf = PyByteArray_AS_STRING(self);
26182618
Py_ssize_t arglen = PyByteArray_GET_SIZE(self);
2619-
return _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
2619+
// Prevent 'self' from being freed if computing len(sep) mutates 'self'
2620+
// in _Py_strhex_with_sep().
2621+
// See: https://github.com/python/cpython/issues/143195.
2622+
self->ob_exports++;
2623+
PyObject *res = _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
2624+
self->ob_exports--;
2625+
return res;
26202626
}
26212627

26222628
static PyObject *

Objects/memoryobject.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2349,7 +2349,13 @@ memoryview_hex_impl(PyMemoryViewObject *self, PyObject *sep,
23492349
CHECK_RELEASED(self);
23502350

23512351
if (MV_C_CONTIGUOUS(self->flags)) {
2352-
return _Py_strhex_with_sep(src->buf, src->len, sep, bytes_per_sep);
2352+
// Prevent 'self' from being freed if computing len(sep) mutates 'self'
2353+
// in _Py_strhex_with_sep().
2354+
// See: https://github.com/python/cpython/issues/143195.
2355+
self->exports++;
2356+
PyObject *ret = _Py_strhex_with_sep(src->buf, src->len, sep, bytes_per_sep);
2357+
self->exports--;
2358+
return ret;
23532359
}
23542360

23552361
bytes = PyBytes_FromStringAndSize(NULL, src->len);

0 commit comments

Comments
 (0)