Skip to content

Commit 751a787

Browse files
fateleicmaloney
authored andcommitted
[3.13] 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 <fatelei@gmail.com>
1 parent ae3834c commit 751a787

File tree

3 files changed

+110
-41
lines changed

3 files changed

+110
-41
lines changed

Lib/test/test_bytes.py

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

1900+
def test_search_methods_reentrancy_raises_buffererror(self):
1901+
# gh-142560: Raise BufferError if buffer mutates during search arg conversion.
1902+
class Evil:
1903+
def __init__(self, ba):
1904+
self.ba = ba
1905+
def __buffer__(self, flags):
1906+
self.ba.clear()
1907+
return memoryview(self.ba)
1908+
def __release_buffer__(self, view: memoryview) -> None:
1909+
view.release()
1910+
def __index__(self):
1911+
self.ba.clear()
1912+
return ord("A")
1913+
1914+
def make_case():
1915+
ba = bytearray(b"A")
1916+
return ba, Evil(ba)
1917+
1918+
for name in ("find", "count", "index", "rindex", "rfind"):
1919+
ba, evil = make_case()
1920+
with self.subTest(name):
1921+
with self.assertRaises(BufferError):
1922+
getattr(ba, name)(evil)
1923+
1924+
ba, evil = make_case()
1925+
with self.assertRaises(BufferError):
1926+
evil in ba
1927+
with self.assertRaises(BufferError):
1928+
ba.split(evil)
1929+
with self.assertRaises(BufferError):
1930+
ba.rsplit(evil)
19001931

19011932
class AssortedBytesTest(unittest.TestCase):
19021933
#
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: 78 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,27 @@ bytearray_releasebuffer(PyByteArrayObject *obj, Py_buffer *view)
6464
assert(obj->ob_exports >= 0);
6565
}
6666

67+
typedef PyObject* (*_ba_bytes_op)(const char *buf, Py_ssize_t len,
68+
PyObject *sub, Py_ssize_t start,
69+
Py_ssize_t end);
70+
71+
static PyObject *
72+
_bytearray_with_buffer(PyByteArrayObject *self, _ba_bytes_op op, PyObject *sub,
73+
Py_ssize_t start, Py_ssize_t end)
74+
{
75+
PyObject *res;
76+
77+
Py_BEGIN_CRITICAL_SECTION(self);
78+
79+
/* Increase exports to prevent bytearray storage from changing during op. */
80+
self->ob_exports++;
81+
res = op(PyByteArray_AS_STRING(self), Py_SIZE(self), sub, start, end);
82+
self->ob_exports--;
83+
Py_END_CRITICAL_SECTION(self);
84+
85+
return res;
86+
}
87+
6788
static int
6889
_canresize(PyByteArrayObject *self)
6990
{
@@ -1146,8 +1167,7 @@ bytearray_find_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start,
11461167
Py_ssize_t end)
11471168
/*[clinic end generated code: output=413e1cab2ae87da0 input=793dfad803e2952f]*/
11481169
{
1149-
return _Py_bytes_find(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1150-
sub, start, end);
1170+
return _bytearray_with_buffer(self, _Py_bytes_find, sub, start, end);
11511171
}
11521172

11531173
/*[clinic input]
@@ -1161,8 +1181,7 @@ bytearray_count_impl(PyByteArrayObject *self, PyObject *sub,
11611181
Py_ssize_t start, Py_ssize_t end)
11621182
/*[clinic end generated code: output=a21ee2692e4f1233 input=4deb529db38deda8]*/
11631183
{
1164-
return _Py_bytes_count(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1165-
sub, start, end);
1184+
return _bytearray_with_buffer(self, _Py_bytes_count, sub, start, end);
11661185
}
11671186

11681187
/*[clinic input]
@@ -1207,8 +1226,7 @@ bytearray_index_impl(PyByteArrayObject *self, PyObject *sub,
12071226
Py_ssize_t start, Py_ssize_t end)
12081227
/*[clinic end generated code: output=067a1e78efc672a7 input=8cbaf6836dbd2a9a]*/
12091228
{
1210-
return _Py_bytes_index(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1211-
sub, start, end);
1229+
return _bytearray_with_buffer(self, _Py_bytes_index, sub, start, end);
12121230
}
12131231

12141232
/*[clinic input]
@@ -1224,8 +1242,7 @@ bytearray_rfind_impl(PyByteArrayObject *self, PyObject *sub,
12241242
Py_ssize_t start, Py_ssize_t end)
12251243
/*[clinic end generated code: output=51bf886f932b283c input=eaa107468a158423]*/
12261244
{
1227-
return _Py_bytes_rfind(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1228-
sub, start, end);
1245+
return _bytearray_with_buffer(self, _Py_bytes_rfind, sub, start, end);
12291246
}
12301247

12311248
/*[clinic input]
@@ -1241,14 +1258,24 @@ bytearray_rindex_impl(PyByteArrayObject *self, PyObject *sub,
12411258
Py_ssize_t start, Py_ssize_t end)
12421259
/*[clinic end generated code: output=38e1cf66bafb08b9 input=81cf49d0af4d5bd0]*/
12431260
{
1244-
return _Py_bytes_rindex(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1245-
sub, start, end);
1261+
return _bytearray_with_buffer(self, _Py_bytes_rindex, sub, start, end);
12461262
}
12471263

12481264
static int
12491265
bytearray_contains(PyObject *self, PyObject *arg)
12501266
{
1251-
return _Py_bytes_contains(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), arg);
1267+
int ret = -1;
1268+
Py_BEGIN_CRITICAL_SECTION(self);
1269+
PyByteArrayObject *ba = _PyByteArray_CAST(self);
1270+
1271+
/* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
1272+
ba->ob_exports++;
1273+
ret = _Py_bytes_contains(PyByteArray_AS_STRING(ba),
1274+
PyByteArray_GET_SIZE(self),
1275+
arg);
1276+
ba->ob_exports--;
1277+
Py_END_CRITICAL_SECTION();
1278+
return ret;
12521279
}
12531280

12541281
/*[clinic input]
@@ -1271,8 +1298,7 @@ bytearray_startswith_impl(PyByteArrayObject *self, PyObject *subobj,
12711298
Py_ssize_t start, Py_ssize_t end)
12721299
/*[clinic end generated code: output=a3d9b6d44d3662a6 input=76385e0b376b45c1]*/
12731300
{
1274-
return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1275-
subobj, start, end);
1301+
return _bytearray_with_buffer(self, _Py_bytes_startswith, subobj, start, end);
12761302
}
12771303

12781304
/*[clinic input]
@@ -1295,8 +1321,7 @@ bytearray_endswith_impl(PyByteArrayObject *self, PyObject *subobj,
12951321
Py_ssize_t start, Py_ssize_t end)
12961322
/*[clinic end generated code: output=e75ea8c227954caa input=9b8baa879aa3d74b]*/
12971323
{
1298-
return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
1299-
subobj, start, end);
1324+
return _bytearray_with_buffer(self, _Py_bytes_endswith, subobj, start, end);
13001325
}
13011326

13021327
/*[clinic input]
@@ -1539,26 +1564,32 @@ bytearray_split_impl(PyByteArrayObject *self, PyObject *sep,
15391564
Py_ssize_t maxsplit)
15401565
/*[clinic end generated code: output=833e2cf385d9a04d input=24f82669f41bf523]*/
15411566
{
1542-
Py_ssize_t len = PyByteArray_GET_SIZE(self), n;
1543-
const char *s = PyByteArray_AS_STRING(self), *sub;
1544-
PyObject *list;
1545-
Py_buffer vsub;
1567+
PyObject *list = NULL;
1568+
1569+
/* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
1570+
self->ob_exports++;
1571+
const char *sbuf = PyByteArray_AS_STRING(self);
1572+
Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self);
15461573

15471574
if (maxsplit < 0)
15481575
maxsplit = PY_SSIZE_T_MAX;
15491576

1550-
if (sep == Py_None)
1551-
return stringlib_split_whitespace((PyObject*) self, s, len, maxsplit);
1577+
if (sep == Py_None) {
1578+
list = stringlib_split_whitespace((PyObject*)self, sbuf, slen, maxsplit);
1579+
goto done;
1580+
}
15521581

1553-
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0)
1554-
return NULL;
1555-
sub = vsub.buf;
1556-
n = vsub.len;
1582+
Py_buffer vsub;
1583+
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) {
1584+
goto done;
1585+
}
15571586

1558-
list = stringlib_split(
1559-
(PyObject*) self, s, len, sub, n, maxsplit
1560-
);
1587+
list = stringlib_split((PyObject*)self, sbuf, slen,
1588+
(const char *)vsub.buf, vsub.len, maxsplit);
15611589
PyBuffer_Release(&vsub);
1590+
1591+
done:
1592+
self->ob_exports--;
15621593
return list;
15631594
}
15641595

@@ -1650,26 +1681,32 @@ bytearray_rsplit_impl(PyByteArrayObject *self, PyObject *sep,
16501681
Py_ssize_t maxsplit)
16511682
/*[clinic end generated code: output=a55e0b5a03cb6190 input=a68286e4dd692ffe]*/
16521683
{
1653-
Py_ssize_t len = PyByteArray_GET_SIZE(self), n;
1654-
const char *s = PyByteArray_AS_STRING(self), *sub;
1655-
PyObject *list;
1656-
Py_buffer vsub;
1684+
PyObject *list = NULL;
1685+
1686+
/* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
1687+
self->ob_exports++;
1688+
const char *sbuf = PyByteArray_AS_STRING(self);
1689+
Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self);
16571690

16581691
if (maxsplit < 0)
16591692
maxsplit = PY_SSIZE_T_MAX;
16601693

1661-
if (sep == Py_None)
1662-
return stringlib_rsplit_whitespace((PyObject*) self, s, len, maxsplit);
1694+
if (sep == Py_None) {
1695+
list = stringlib_rsplit_whitespace((PyObject*)self, sbuf, slen, maxsplit);
1696+
goto done;
1697+
}
16631698

1664-
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0)
1665-
return NULL;
1666-
sub = vsub.buf;
1667-
n = vsub.len;
1699+
Py_buffer vsub;
1700+
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) {
1701+
goto done;
1702+
}
16681703

1669-
list = stringlib_rsplit(
1670-
(PyObject*) self, s, len, sub, n, maxsplit
1671-
);
1704+
list = stringlib_rsplit((PyObject*)self, sbuf, slen,
1705+
(const char *)vsub.buf, vsub.len, maxsplit);
16721706
PyBuffer_Release(&vsub);
1707+
1708+
done:
1709+
self->ob_exports--;
16731710
return list;
16741711
}
16751712

0 commit comments

Comments
 (0)