Skip to content

Commit 9976c2b

Browse files
authored
gh-143195: fix UAF in {bytearray,memoryview}.hex(sep) via re-entrant sep.__len__ (#143209)
1 parent f5e11fa commit 9976c2b

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
@@ -2092,6 +2092,19 @@ def make_case():
20922092
with self.assertRaises(BufferError):
20932093
ba.rsplit(evil)
20942094

2095+
def test_hex_use_after_free(self):
2096+
# Prevent UAF in bytearray.hex(sep) with re-entrant sep.__len__.
2097+
# Regression test for https://github.com/python/cpython/issues/143195.
2098+
ba = bytearray(b'\xAA')
2099+
2100+
class S(bytes):
2101+
def __len__(self):
2102+
ba.clear()
2103+
return 1
2104+
2105+
self.assertRaises(BufferError, ba.hex, S(b':'))
2106+
2107+
20952108
class AssortedBytesTest(unittest.TestCase):
20962109
#
20972110
# 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
@@ -442,6 +442,20 @@ def test_issue22668(self):
442442
self.assertEqual(c.format, "H")
443443
self.assertEqual(d.format, "H")
444444

445+
def test_hex_use_after_free(self):
446+
# Prevent UAF in memoryview.hex(sep) with re-entrant sep.__len__.
447+
# Regression test for https://github.com/python/cpython/issues/143195.
448+
ba = bytearray(b'A' * 1024)
449+
mv = memoryview(ba)
450+
451+
class S(bytes):
452+
def __len__(self):
453+
mv.release()
454+
ba.clear()
455+
return 1
456+
457+
self.assertRaises(BufferError, mv.hex, S(b':'))
458+
445459

446460
# Variations on source objects for the buffer: bytes-like objects, then arrays
447461
# 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
@@ -2664,7 +2664,13 @@ bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, int bytes_per_sep)
26642664
{
26652665
char* argbuf = PyByteArray_AS_STRING(self);
26662666
Py_ssize_t arglen = PyByteArray_GET_SIZE(self);
2667-
return _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
2667+
// Prevent 'self' from being freed if computing len(sep) mutates 'self'
2668+
// in _Py_strhex_with_sep().
2669+
// See: https://github.com/python/cpython/issues/143195.
2670+
self->ob_exports++;
2671+
PyObject *res = _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
2672+
self->ob_exports--;
2673+
return res;
26682674
}
26692675

26702676
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
PyBytesWriter *writer = PyBytesWriter_Create(src->len);

0 commit comments

Comments
 (0)