mirror of
https://github.com/python/cpython.git
synced 2025-12-31 04:23:37 +00:00
[3.13] gh-142560: prevent use-after-free in search-like methods by exporting buffer in bytearray (GH-142938) (GH-142986)
(cherry picked from commit 220f0b1077)
Co-authored-by: wangxiaolei <fatelei@gmail.com>
This commit is contained in:
parent
88025560aa
commit
a9e068f0be
3 changed files with 105 additions and 41 deletions
|
|
@ -1897,6 +1897,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):
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -64,6 +64,24 @@ bytearray_releasebuffer(PyByteArrayObject *obj, Py_buffer *view)
|
|||
assert(obj->ob_exports >= 0);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
|
|
@ -1146,8 +1164,7 @@ bytearray_find_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start,
|
|||
Py_ssize_t end)
|
||||
/*[clinic end generated code: output=413e1cab2ae87da0 input=793dfad803e2952f]*/
|
||||
{
|
||||
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]
|
||||
|
|
@ -1161,8 +1178,7 @@ bytearray_count_impl(PyByteArrayObject *self, PyObject *sub,
|
|||
Py_ssize_t start, Py_ssize_t end)
|
||||
/*[clinic end generated code: output=a21ee2692e4f1233 input=4deb529db38deda8]*/
|
||||
{
|
||||
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]
|
||||
|
|
@ -1207,8 +1223,7 @@ bytearray_index_impl(PyByteArrayObject *self, PyObject *sub,
|
|||
Py_ssize_t start, Py_ssize_t end)
|
||||
/*[clinic end generated code: output=067a1e78efc672a7 input=8cbaf6836dbd2a9a]*/
|
||||
{
|
||||
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]
|
||||
|
|
@ -1224,8 +1239,7 @@ bytearray_rfind_impl(PyByteArrayObject *self, PyObject *sub,
|
|||
Py_ssize_t start, Py_ssize_t end)
|
||||
/*[clinic end generated code: output=51bf886f932b283c input=eaa107468a158423]*/
|
||||
{
|
||||
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]
|
||||
|
|
@ -1241,14 +1255,22 @@ bytearray_rindex_impl(PyByteArrayObject *self, PyObject *sub,
|
|||
Py_ssize_t start, Py_ssize_t end)
|
||||
/*[clinic end generated code: output=38e1cf66bafb08b9 input=81cf49d0af4d5bd0]*/
|
||||
{
|
||||
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)
|
||||
{
|
||||
return _Py_bytes_contains(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), arg);
|
||||
int ret = -1;
|
||||
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--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
|
@ -1271,8 +1293,7 @@ bytearray_startswith_impl(PyByteArrayObject *self, PyObject *subobj,
|
|||
Py_ssize_t start, Py_ssize_t end)
|
||||
/*[clinic end generated code: output=a3d9b6d44d3662a6 input=76385e0b376b45c1]*/
|
||||
{
|
||||
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]
|
||||
|
|
@ -1295,8 +1316,7 @@ bytearray_endswith_impl(PyByteArrayObject *self, PyObject *subobj,
|
|||
Py_ssize_t start, Py_ssize_t end)
|
||||
/*[clinic end generated code: output=e75ea8c227954caa input=9b8baa879aa3d74b]*/
|
||||
{
|
||||
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]
|
||||
|
|
@ -1539,26 +1559,32 @@ bytearray_split_impl(PyByteArrayObject *self, PyObject *sep,
|
|||
Py_ssize_t maxsplit)
|
||||
/*[clinic end generated code: output=833e2cf385d9a04d input=24f82669f41bf523]*/
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1650,26 +1676,32 @@ bytearray_rsplit_impl(PyByteArrayObject *self, PyObject *sep,
|
|||
Py_ssize_t maxsplit)
|
||||
/*[clinic end generated code: output=a55e0b5a03cb6190 input=a68286e4dd692ffe]*/
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue