[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 220f0b1077)

Co-authored-by: wangxiaolei <fatelei@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-12-19 17:58:24 +01:00 committed by GitHub
parent b46ce52a53
commit dbc7fd61d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 103 additions and 42 deletions

View file

@ -1969,6 +1969,37 @@ def __index__(self):
self.assertEqual(instance.ba[0], ord("?"), "Assigned bytearray not altered")
self.assertEqual(instance.new_ba, bytearray(0x180), "Wrong object altered")
def test_search_methods_reentrancy_raises_buffererror(self):
# gh-142560: Raise BufferError if buffer mutates during search arg conversion.
class Evil:
def __init__(self, ba):
self.ba = ba
def __buffer__(self, flags):
self.ba.clear()
return memoryview(self.ba)
def __release_buffer__(self, view: memoryview) -> None:
view.release()
def __index__(self):
self.ba.clear()
return ord("A")
def make_case():
ba = bytearray(b"A")
return ba, Evil(ba)
for name in ("find", "count", "index", "rindex", "rfind"):
ba, evil = make_case()
with self.subTest(name):
with self.assertRaises(BufferError):
getattr(ba, name)(evil)
ba, evil = make_case()
with self.assertRaises(BufferError):
evil in ba
with self.assertRaises(BufferError):
ba.split(evil)
with self.assertRaises(BufferError):
ba.rsplit(evil)
class AssortedBytesTest(unittest.TestCase):
#

View file

@ -0,0 +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.

View file

@ -82,6 +82,25 @@ bytearray_releasebuffer(PyObject *self, Py_buffer *view)
Py_END_CRITICAL_SECTION();
}
typedef PyObject* (*_ba_bytes_op)(const char *buf, Py_ssize_t len,
PyObject *sub, Py_ssize_t start,
Py_ssize_t end);
static PyObject *
_bytearray_with_buffer(PyByteArrayObject *self, _ba_bytes_op op, PyObject *sub,
Py_ssize_t start, Py_ssize_t end)
{
PyObject *res;
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
/* Increase exports to prevent bytearray storage from changing during op. */
self->ob_exports++;
res = op(PyByteArray_AS_STRING(self), Py_SIZE(self), sub, start, end);
self->ob_exports--;
return res;
}
static int
_canresize(PyByteArrayObject *self)
{
@ -1300,8 +1319,7 @@ bytearray_find_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start,
Py_ssize_t end)
/*[clinic end generated code: output=413e1cab2ae87da0 input=1de9f4558df68336]*/
{
return _Py_bytes_find(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
sub, start, end);
return _bytearray_with_buffer(self, _Py_bytes_find, sub, start, end);
}
/*[clinic input]
@ -1316,8 +1334,7 @@ bytearray_count_impl(PyByteArrayObject *self, PyObject *sub,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=a21ee2692e4f1233 input=2608c30644614724]*/
{
return _Py_bytes_count(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
sub, start, end);
return _bytearray_with_buffer(self, _Py_bytes_count, sub, start, end);
}
/*[clinic input]
@ -1364,8 +1381,7 @@ bytearray_index_impl(PyByteArrayObject *self, PyObject *sub,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=067a1e78efc672a7 input=0086ba0ab9bf44a5]*/
{
return _Py_bytes_index(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
sub, start, end);
return _bytearray_with_buffer(self, _Py_bytes_index, sub, start, end);
}
/*[clinic input]
@ -1382,8 +1398,7 @@ bytearray_rfind_impl(PyByteArrayObject *self, PyObject *sub,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=51bf886f932b283c input=ac73593305d5c1d1]*/
{
return _Py_bytes_rfind(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
sub, start, end);
return _bytearray_with_buffer(self, _Py_bytes_rfind, sub, start, end);
}
/*[clinic input]
@ -1400,18 +1415,22 @@ bytearray_rindex_impl(PyByteArrayObject *self, PyObject *sub,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=38e1cf66bafb08b9 input=0cf331bf5ebe0e91]*/
{
return _Py_bytes_rindex(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
sub, start, end);
return _bytearray_with_buffer(self, _Py_bytes_rindex, sub, start, end);
}
static int
bytearray_contains(PyObject *self, PyObject *arg)
{
int ret;
int ret = -1;
Py_BEGIN_CRITICAL_SECTION(self);
ret = _Py_bytes_contains(PyByteArray_AS_STRING(self),
PyByteArrayObject *ba = _PyByteArray_CAST(self);
/* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
ba->ob_exports++;
ret = _Py_bytes_contains(PyByteArray_AS_STRING(ba),
PyByteArray_GET_SIZE(self),
arg);
ba->ob_exports--;
Py_END_CRITICAL_SECTION();
return ret;
}
@ -1437,8 +1456,7 @@ bytearray_startswith_impl(PyByteArrayObject *self, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=a3d9b6d44d3662a6 input=ea8d036d09df34b2]*/
{
return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
subobj, start, end);
return _bytearray_with_buffer(self, _Py_bytes_startswith, subobj, start, end);
}
/*[clinic input]
@ -1462,8 +1480,7 @@ bytearray_endswith_impl(PyByteArrayObject *self, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=e75ea8c227954caa input=c61b90bb23a689ce]*/
{
return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
subobj, start, end);
return _bytearray_with_buffer(self, _Py_bytes_endswith, subobj, start, end);
}
/*[clinic input]
@ -1736,26 +1753,32 @@ bytearray_split_impl(PyByteArrayObject *self, PyObject *sep,
Py_ssize_t maxsplit)
/*[clinic end generated code: output=833e2cf385d9a04d input=1c367486b9938909]*/
{
Py_ssize_t len = PyByteArray_GET_SIZE(self), n;
const char *s = PyByteArray_AS_STRING(self), *sub;
PyObject *list;
Py_buffer vsub;
PyObject *list = NULL;
/* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
self->ob_exports++;
const char *sbuf = PyByteArray_AS_STRING(self);
Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self);
if (maxsplit < 0)
maxsplit = PY_SSIZE_T_MAX;
if (sep == Py_None)
return stringlib_split_whitespace((PyObject*) self, s, len, maxsplit);
if (sep == Py_None) {
list = stringlib_split_whitespace((PyObject*)self, sbuf, slen, maxsplit);
goto done;
}
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0)
return NULL;
sub = vsub.buf;
n = vsub.len;
Py_buffer vsub;
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) {
goto done;
}
list = stringlib_split(
(PyObject*) self, s, len, sub, n, maxsplit
);
list = stringlib_split((PyObject*)self, sbuf, slen,
(const char *)vsub.buf, vsub.len, maxsplit);
PyBuffer_Release(&vsub);
done:
self->ob_exports--;
return list;
}
@ -1850,26 +1873,32 @@ bytearray_rsplit_impl(PyByteArrayObject *self, PyObject *sep,
Py_ssize_t maxsplit)
/*[clinic end generated code: output=a55e0b5a03cb6190 input=3cd513c2b94a53c1]*/
{
Py_ssize_t len = PyByteArray_GET_SIZE(self), n;
const char *s = PyByteArray_AS_STRING(self), *sub;
PyObject *list;
Py_buffer vsub;
PyObject *list = NULL;
/* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
self->ob_exports++;
const char *sbuf = PyByteArray_AS_STRING(self);
Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self);
if (maxsplit < 0)
maxsplit = PY_SSIZE_T_MAX;
if (sep == Py_None)
return stringlib_rsplit_whitespace((PyObject*) self, s, len, maxsplit);
if (sep == Py_None) {
list = stringlib_rsplit_whitespace((PyObject*)self, sbuf, slen, maxsplit);
goto done;
}
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0)
return NULL;
sub = vsub.buf;
n = vsub.len;
Py_buffer vsub;
if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) {
goto done;
}
list = stringlib_rsplit(
(PyObject*) self, s, len, sub, n, maxsplit
);
list = stringlib_rsplit((PyObject*)self, sbuf, slen,
(const char *)vsub.buf, vsub.len, maxsplit);
PyBuffer_Release(&vsub);
done:
self->ob_exports--;
return list;
}