Skip to content

Commit dbc7fd6

Browse files
[3.14] gh-142560: prevent use-after-free in search-like methods by exporting buffer in bytearray (GH-142938) (#142983)
gh-142560: prevent use-after-free in search-like methods by exporting buffer in bytearray (GH-142938) (cherry picked from commit 220f0b1) Co-authored-by: wangxiaolei <[email protected]>
1 parent b46ce52 commit dbc7fd6

File tree

3 files changed

+103
-42
lines changed

3 files changed

+103
-42
lines changed

Lib/test/test_bytes.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,37 @@ def __index__(self):
19691969
self.assertEqual(instance.ba[0], ord("?"), "Assigned bytearray not altered")
19701970
self.assertEqual(instance.new_ba, bytearray(0x180), "Wrong object altered")
19711971

1972+
def test_search_methods_reentrancy_raises_buffererror(self):
1973+
# gh-142560: Raise BufferError if buffer mutates during search arg conversion.
1974+
class Evil:
1975+
def __init__(self, ba):
1976+
self.ba = ba
1977+
def __buffer__(self, flags):
1978+
self.ba.clear()
1979+
return memoryview(self.ba)
1980+
def __release_buffer__(self, view: memoryview) -> None:
1981+
view.release()
1982+
def __index__(self):
1983+
self.ba.clear()
1984+
return ord("A")
1985+
1986+
def make_case():
1987+
ba = bytearray(b"A")
1988+
return ba, Evil(ba)
1989+
1990+
for name in ("find", "count", "index", "rindex", "rfind"):
1991+
ba, evil = make_case()
1992+
with self.subTest(name):
1993+
with self.assertRaises(BufferError):
1994+
getattr(ba, name)(evil)
1995+
1996+
ba, evil = make_case()
1997+
with self.assertRaises(BufferError):
1998+
evil in ba
1999+
with self.assertRaises(BufferError):
2000+
ba.split(evil)
2001+
with self.assertRaises(BufferError):
2002+
ba.rsplit(evil)
19722003

19732004
class AssortedBytesTest(unittest.TestCase):
19742005
#
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix use-after-free in :class:`bytearray` search-like methods (:meth:`~bytearray.find`, :meth:`~bytearray.count`, :meth:`~bytearray.index`, :meth:`~bytearray.rindex`, and :meth:`~bytearray.rfind`) by marking the storage as exported which causes reallocation attempts to raise :exc:`BufferError`. For :func:`~operator.contains`, :meth:`~bytearray.split`, and :meth:`~bytearray.rsplit` the :ref:`buffer protocol <bufferobjects>` is used for this.

Objects/bytearrayobject.c

Lines changed: 71 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,25 @@ bytearray_releasebuffer(PyObject *self, Py_buffer *view)
8282
Py_END_CRITICAL_SECTION();
8383
}
8484

85+
typedef PyObject* (*_ba_bytes_op)(const char *buf, Py_ssize_t len,
86+
PyObject *sub, Py_ssize_t start,
87+
Py_ssize_t end);
88+
89+
static PyObject *
90+
_bytearray_with_buffer(PyByteArrayObject *self, _ba_bytes_op op, PyObject *sub,
91+
Py_ssize_t start, Py_ssize_t end)
92+
{
93+
PyObject *res;
94+
95+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
96+
97+
/* Increase exports to prevent bytearray storage from changing during op. */
98+
self->ob_exports++;
99+
res = op(PyByteArray_AS_STRING(self), Py_SIZE(self), sub, start, end);
100+
self->ob_exports--;
101+
return res;
102+
}
103+
85104
static int
86105
_canresize(PyByteArrayObject *self)
87106
{
@@ -1300,8 +1319,7 @@ bytearray_find_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start,
13001319
Py_ssize_t end)
13011320
/*[clinic end generated code: output=413e1cab2ae87da0 input=1de9f4558df68336]*/
13021321
{
1303-
return _Py_bytes_find(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1304-
sub, start, end);
1322+
return _bytearray_with_buffer(self, _Py_bytes_find, sub, start, end);
13051323
}
13061324

13071325
/*[clinic input]
@@ -1316,8 +1334,7 @@ bytearray_count_impl(PyByteArrayObject *self, PyObject *sub,
13161334
Py_ssize_t start, Py_ssize_t end)
13171335
/*[clinic end generated code: output=a21ee2692e4f1233 input=2608c30644614724]*/
13181336
{
1319-
return _Py_bytes_count(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1320-
sub, start, end);
1337+
return _bytearray_with_buffer(self, _Py_bytes_count, sub, start, end);
13211338
}
13221339

13231340
/*[clinic input]
@@ -1364,8 +1381,7 @@ bytearray_index_impl(PyByteArrayObject *self, PyObject *sub,
13641381
Py_ssize_t start, Py_ssize_t end)
13651382
/*[clinic end generated code: output=067a1e78efc672a7 input=0086ba0ab9bf44a5]*/
13661383
{
1367-
return _Py_bytes_index(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1368-
sub, start, end);
1384+
return _bytearray_with_buffer(self, _Py_bytes_index, sub, start, end);
13691385
}
13701386

13711387
/*[clinic input]
@@ -1382,8 +1398,7 @@ bytearray_rfind_impl(PyByteArrayObject *self, PyObject *sub,
13821398
Py_ssize_t start, Py_ssize_t end)
13831399
/*[clinic end generated code: output=51bf886f932b283c input=ac73593305d5c1d1]*/
13841400
{
1385-
return _Py_bytes_rfind(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1386-
sub, start, end);
1401+
return _bytearray_with_buffer(self, _Py_bytes_rfind, sub, start, end);
13871402
}
13881403

13891404
/*[clinic input]
@@ -1400,18 +1415,22 @@ bytearray_rindex_impl(PyByteArrayObject *self, PyObject *sub,
14001415
Py_ssize_t start, Py_ssize_t end)
14011416
/*[clinic end generated code: output=38e1cf66bafb08b9 input=0cf331bf5ebe0e91]*/
14021417
{
1403-
return _Py_bytes_rindex(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1404-
sub, start, end);
1418+
return _bytearray_with_buffer(self, _Py_bytes_rindex, sub, start, end);
14051419
}
14061420

14071421
static int
14081422
bytearray_contains(PyObject *self, PyObject *arg)
14091423
{
1410-
int ret;
1424+
int ret = -1;
14111425
Py_BEGIN_CRITICAL_SECTION(self);
1412-
ret = _Py_bytes_contains(PyByteArray_AS_STRING(self),
1426+
PyByteArrayObject *ba = _PyByteArray_CAST(self);
1427+
1428+
/* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
1429+
ba->ob_exports++;
1430+
ret = _Py_bytes_contains(PyByteArray_AS_STRING(ba),
14131431
PyByteArray_GET_SIZE(self),
14141432
arg);
1433+
ba->ob_exports--;
14151434
Py_END_CRITICAL_SECTION();
14161435
return ret;
14171436
}
@@ -1437,8 +1456,7 @@ bytearray_startswith_impl(PyByteArrayObject *self, PyObject *subobj,
14371456
Py_ssize_t start, Py_ssize_t end)
14381457
/*[clinic end generated code: output=a3d9b6d44d3662a6 input=ea8d036d09df34b2]*/
14391458
{
1440-
return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1441-
subobj, start, end);
1459+
return _bytearray_with_buffer(self, _Py_bytes_startswith, subobj, start, end);
14421460
}
14431461

14441462
/*[clinic input]
@@ -1462,8 +1480,7 @@ bytearray_endswith_impl(PyByteArrayObject *self, PyObject *subobj,
14621480
Py_ssize_t start, Py_ssize_t end)
14631481
/*[clinic end generated code: output=e75ea8c227954caa input=c61b90bb23a689ce]*/
14641482
{
1465-
return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1466-
subobj, start, end);
1483+
return _bytearray_with_buffer(self, _Py_bytes_endswith, subobj, start, end);
14671484
}
14681485

14691486
/*[clinic input]
@@ -1736,26 +1753,32 @@ bytearray_split_impl(PyByteArrayObject *self, PyObject *sep,
17361753
Py_ssize_t maxsplit)
17371754
/*[clinic end generated code: output=833e2cf385d9a04d input=1c367486b9938909]*/
17381755
{
1739-
Py_ssize_t len = PyByteArray_GET_SIZE(self), n;
1740-
const char *s = PyByteArray_AS_STRING(self), *sub;
1741-
PyObject *list;
1742-
Py_buffer vsub;
1756+
PyObject *list = NULL;
1757+
1758+
/* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
1759+
self->ob_exports++;
1760+
const char *sbuf = PyByteArray_AS_STRING(self);
1761+
Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self);
17431762

17441763
if (maxsplit < 0)
17451764
maxsplit = PY_SSIZE_T_MAX;
17461765

1747-
if (sep == Py_None)
1748-
return stringlib_split_whitespace((PyObject*) self, s, len, maxsplit);
1766+
if (sep == Py_None) {
1767+
list = stringlib_split_whitespace((PyObject*)self, sbuf, slen, maxsplit);
1768+
goto done;
1769+
}
17491770

1750-
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0)
1751-
return NULL;
1752-
sub = vsub.buf;
1753-
n = vsub.len;
1771+
Py_buffer vsub;
1772+
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) {
1773+
goto done;
1774+
}
17541775

1755-
list = stringlib_split(
1756-
(PyObject*) self, s, len, sub, n, maxsplit
1757-
);
1776+
list = stringlib_split((PyObject*)self, sbuf, slen,
1777+
(const char *)vsub.buf, vsub.len, maxsplit);
17581778
PyBuffer_Release(&vsub);
1779+
1780+
done:
1781+
self->ob_exports--;
17591782
return list;
17601783
}
17611784

@@ -1850,26 +1873,32 @@ bytearray_rsplit_impl(PyByteArrayObject *self, PyObject *sep,
18501873
Py_ssize_t maxsplit)
18511874
/*[clinic end generated code: output=a55e0b5a03cb6190 input=3cd513c2b94a53c1]*/
18521875
{
1853-
Py_ssize_t len = PyByteArray_GET_SIZE(self), n;
1854-
const char *s = PyByteArray_AS_STRING(self), *sub;
1855-
PyObject *list;
1856-
Py_buffer vsub;
1876+
PyObject *list = NULL;
1877+
1878+
/* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
1879+
self->ob_exports++;
1880+
const char *sbuf = PyByteArray_AS_STRING(self);
1881+
Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self);
18571882

18581883
if (maxsplit < 0)
18591884
maxsplit = PY_SSIZE_T_MAX;
18601885

1861-
if (sep == Py_None)
1862-
return stringlib_rsplit_whitespace((PyObject*) self, s, len, maxsplit);
1886+
if (sep == Py_None) {
1887+
list = stringlib_rsplit_whitespace((PyObject*)self, sbuf, slen, maxsplit);
1888+
goto done;
1889+
}
18631890

1864-
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0)
1865-
return NULL;
1866-
sub = vsub.buf;
1867-
n = vsub.len;
1891+
Py_buffer vsub;
1892+
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) {
1893+
goto done;
1894+
}
18681895

1869-
list = stringlib_rsplit(
1870-
(PyObject*) self, s, len, sub, n, maxsplit
1871-
);
1896+
list = stringlib_rsplit((PyObject*)self, sbuf, slen,
1897+
(const char *)vsub.buf, vsub.len, maxsplit);
18721898
PyBuffer_Release(&vsub);
1899+
1900+
done:
1901+
self->ob_exports--;
18731902
return list;
18741903
}
18751904

0 commit comments

Comments
 (0)