Skip to content

Commit 8aca2fd

Browse files
authored
[3.13] gh-143195: fix UAF in {bytearray,memoryview}.hex(sep) via re-entrant sep.__len__ (GH-143209) (#143220)
(cherry picked from commit 9976c2b)
1 parent 833fbe9 commit 8aca2fd

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
@@ -1929,6 +1929,19 @@ def make_case():
19291929
with self.assertRaises(BufferError):
19301930
ba.rsplit(evil)
19311931

1932+
def test_hex_use_after_free(self):
1933+
# Prevent UAF in bytearray.hex(sep) with re-entrant sep.__len__.
1934+
# Regression test for https://github.com/python/cpython/issues/143195.
1935+
ba = bytearray(b'\xAA')
1936+
1937+
class S(bytes):
1938+
def __len__(self):
1939+
ba.clear()
1940+
return 1
1941+
1942+
self.assertRaises(BufferError, ba.hex, S(b':'))
1943+
1944+
19321945
class AssortedBytesTest(unittest.TestCase):
19331946
#
19341947
# 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
@@ -399,6 +399,20 @@ def test_issue22668(self):
399399
self.assertEqual(c.format, "H")
400400
self.assertEqual(d.format, "H")
401401

402+
def test_hex_use_after_free(self):
403+
# Prevent UAF in memoryview.hex(sep) with re-entrant sep.__len__.
404+
# Regression test for https://github.com/python/cpython/issues/143195.
405+
ba = bytearray(b'A' * 1024)
406+
mv = memoryview(ba)
407+
408+
class S(bytes):
409+
def __len__(self):
410+
mv.release()
411+
ba.clear()
412+
return 1
413+
414+
self.assertRaises(BufferError, mv.hex, S(b':'))
415+
402416

403417
# Variations on source objects for the buffer: bytes-like objects, then arrays
404418
# 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
@@ -2224,7 +2224,13 @@ bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, int bytes_per_sep)
22242224
{
22252225
char* argbuf = PyByteArray_AS_STRING(self);
22262226
Py_ssize_t arglen = PyByteArray_GET_SIZE(self);
2227-
return _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
2227+
// Prevent 'self' from being freed if computing len(sep) mutates 'self'
2228+
// in _Py_strhex_with_sep().
2229+
// See: https://github.com/python/cpython/issues/143195.
2230+
self->ob_exports++;
2231+
PyObject *res = _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
2232+
self->ob_exports--;
2233+
return res;
22282234
}
22292235

22302236
static PyObject *

Objects/memoryobject.c

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

23322332
if (MV_C_CONTIGUOUS(self->flags)) {
2333-
return _Py_strhex_with_sep(src->buf, src->len, sep, bytes_per_sep);
2333+
// Prevent 'self' from being freed if computing len(sep) mutates 'self'
2334+
// in _Py_strhex_with_sep().
2335+
// See: https://github.com/python/cpython/issues/143195.
2336+
self->exports++;
2337+
PyObject *ret = _Py_strhex_with_sep(src->buf, src->len, sep, bytes_per_sep);
2338+
self->exports--;
2339+
return ret;
23342340
}
23352341

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

0 commit comments

Comments
 (0)