diff options
-rw-r--r-- | c/_cffi_backend.c | 24 | ||||
-rw-r--r-- | c/test_c.py | 27 |
2 files changed, 50 insertions, 1 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 4e055f8..29c7f80 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -2197,7 +2197,7 @@ static PyObject *_frombuf_repr(CDataObject *cd, const char *cd_type_name) } } -static PyObject *cdataowning_repr(CDataObject *cd) +static Py_ssize_t cdataowning_size_bytes(CDataObject *cd) { Py_ssize_t size = _cdata_var_byte_size(cd); if (size < 0) { @@ -2208,6 +2208,12 @@ static PyObject *cdataowning_repr(CDataObject *cd) else size = cd->c_type->ct_size; } + return size; +} + +static PyObject *cdataowning_repr(CDataObject *cd) +{ + Py_ssize_t size = cdataowning_size_bytes(cd); return PyText_FromFormat("<cdata '%s' owning %zd bytes>", cd->c_type->ct_name, size); } @@ -7016,12 +7022,14 @@ b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /* this is the constructor of the type implemented in minibuffer.h */ CDataObject *cd; Py_ssize_t size = -1; + int explicit_size; static char *keywords[] = {"cdata", "size", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:buffer", keywords, &CData_Type, &cd, &size)) return NULL; + explicit_size = size >= 0; if (size < 0) size = _cdata_var_byte_size(cd); @@ -7045,6 +7053,20 @@ b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) cd->c_type->ct_name); return NULL; } + + if (explicit_size && CDataOwn_Check(cd)) { + Py_ssize_t size_max = cdataowning_size_bytes(cd); + if (size > size_max) { + char msg[256]; + sprintf(msg, "ffi.buffer(cdata, bytes): creating a buffer of %llu " + "bytes over a cdata that owns only %llu bytes. This " + "will crash if you access the extra memory", + (unsigned PY_LONG_LONG)size, + (unsigned PY_LONG_LONG)size_max); + if (PyErr_WarnEx(PyExc_UserWarning, msg, 1)) + return NULL; + } + } /*WRITE(cd->c_data, size)*/ return minibuffer_new(cd->c_data, size, (PyObject *)cd); } diff --git a/c/test_c.py b/c/test_c.py index 906eb07..9dda28b 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -3498,6 +3498,18 @@ def test_bitfield_as_ppc_gcc(): _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_BIG_ENDIAN) +def buffer_warning(cdata): + import warnings + buf = buffer(cdata) + bytes = len(buf) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + buffer(cdata, bytes) + assert len(w) == 0 + buffer(cdata, bytes + 1) + assert len(w) <= 1 + return len(w) == 1 + def test_struct_array_no_length(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) @@ -3612,6 +3624,7 @@ def test_struct_array_no_length(): assert p.a[1] == 20 assert p.a[2] == 30 assert p.a[3] == 0 + assert buffer_warning(p) # # struct of struct of varsized array BStruct2 = new_struct_type("bar") @@ -3620,6 +3633,20 @@ def test_struct_array_no_length(): for i in range(2): # try to detect heap overwrites p = newp(new_pointer_type(BStruct2), [100, [200, list(range(50))]]) assert p.tail.y[49] == 49 + assert buffer_warning(p) + assert not buffer_warning(cast(new_pointer_type(BStruct2), p)) + assert not buffer_warning(cast(BIntP, p)) + +def test_more_buffer_warning(): + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BArray = new_array_type(BCharP, 10) # char[10] + p = newp(BArray) + assert buffer_warning(p) + assert not buffer_warning(cast(BCharP, p)) + p = newp(BCharP) + assert buffer_warning(p) + assert not buffer_warning(cast(BCharP, p)) def test_struct_array_no_length_explicit_position(): |