Skip to content

Commit 7c9e015

Browse files
committed
gh-156939: Detect buffer overflow in bytearray
Add bytearray_check_consistency() and bytearray_check_trailing_null_byte() functions in call them in most bytearray methods.
1 parent 121e27c commit 7c9e015

5 files changed

Lines changed: 217 additions & 12 deletions

File tree

‎Include/cpython/bytearrayobject.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ typedef struct {
77
PyObject_VAR_HEAD
88
/* How many bytes allocated in ob_bytes
99
10-
In the current implementation this is equivalent to Py_SIZE(ob_bytes_object).
10+
In the current implementation this is equivalent to
11+
PyBytes_GET_SIZE(ob_bytes_object).
1112
The value is always loaded and stored atomically for thread safety.
1213
There are API compatibilty concerns with removing so keeping for now. */
1314
Py_ssize_t ob_alloc;

‎Lib/test/test_capi/test_bytearray.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import sys
2+
import textwrap
23
import unittest
34
from test.support import import_helper
5+
from test.support.script_helper import assert_python_failure
46

57
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
68
from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX
@@ -172,6 +174,28 @@ def test_resize(self):
172174
# CRASHES resize(object(), 0)
173175
# CRASHES resize(NULL, 0)
174176

177+
def test_detect_overflow(self):
178+
# Test detection of buffer overflow
179+
for operation in (
180+
'repr(b)',
181+
'b.resize(5)',
182+
'del b[5:]',
183+
):
184+
with self.subTest(operation):
185+
code = textwrap.dedent(f'''
186+
from test.support import SuppressCrashReport
187+
import _testcapi
188+
189+
with SuppressCrashReport():
190+
# Trigger a buffer overflow in a new bytearray
191+
b = _testcapi.bytearray_overflow(123)
192+
{operation}
193+
b = None
194+
''')
195+
proc = assert_python_failure('-c', code)
196+
self.assertIn(b'Buffer overflow detected in bytearray', proc.err)
197+
self.assertIn(b'at position 123', proc.err)
198+
175199

176200
if __name__ == "__main__":
177201
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
When Python is built in debug mode, :class:`bytearray` now detects buffer
2+
overflow. Patch by Victor Stinner.

‎Modules/_testcapi/bytes.c‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,13 +502,29 @@ test_byteswriter_ptr(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
502502
}
503503

504504

505+
static PyObject *
506+
bytearray_overflow(PyObject *Py_UNUSED(module), PyObject *arg)
507+
{
508+
PyObject *bytearray = PyObject_CallOneArg((PyObject*)&PyByteArray_Type, arg);
509+
if (bytearray == NULL) {
510+
return NULL;
511+
}
512+
513+
char *data = PyByteArray_AS_STRING(bytearray);
514+
Py_ssize_t size = PyByteArray_GET_SIZE(bytearray);
515+
data[size] = '#'; // Buffer overflow!
516+
return bytearray;
517+
}
518+
519+
505520
static PyMethodDef test_methods[] = {
506521
{"bytes_resize", bytes_resize, METH_VARARGS},
507522
{"bytes_join", bytes_join, METH_VARARGS},
508523
{"byteswriter_abc", byteswriter_abc, METH_NOARGS},
509524
{"byteswriter_resize", byteswriter_resize, METH_NOARGS},
510525
{"byteswriter_highlevel", byteswriter_highlevel, METH_NOARGS},
511526
{"test_byteswriter_ptr", test_byteswriter_ptr, METH_NOARGS},
527+
{"bytearray_overflow", bytearray_overflow, METH_O},
512528
{NULL},
513529
};
514530

0 commit comments

Comments
 (0)