Skip to content

Commit c873b43

Browse files
committed
issue #535
Give a warning when we ask a ffi.buffer() that can be proven to overflow. Might help with the confusion on that issue #535.
1 parent 366ac46 commit c873b43

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

c/_cffi_backend.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2197,7 +2197,7 @@ static PyObject *_frombuf_repr(CDataObject *cd, const char *cd_type_name)
21972197
}
21982198
}
21992199

2200-
static PyObject *cdataowning_repr(CDataObject *cd)
2200+
static Py_ssize_t cdataowning_size_bytes(CDataObject *cd)
22012201
{
22022202
Py_ssize_t size = _cdata_var_byte_size(cd);
22032203
if (size < 0) {
@@ -2208,6 +2208,12 @@ static PyObject *cdataowning_repr(CDataObject *cd)
22082208
else
22092209
size = cd->c_type->ct_size;
22102210
}
2211+
return size;
2212+
}
2213+
2214+
static PyObject *cdataowning_repr(CDataObject *cd)
2215+
{
2216+
Py_ssize_t size = cdataowning_size_bytes(cd);
22112217
return PyText_FromFormat("<cdata '%s' owning %zd bytes>",
22122218
cd->c_type->ct_name, size);
22132219
}
@@ -7016,12 +7022,14 @@ b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
70167022
/* this is the constructor of the type implemented in minibuffer.h */
70177023
CDataObject *cd;
70187024
Py_ssize_t size = -1;
7025+
int explicit_size;
70197026
static char *keywords[] = {"cdata", "size", NULL};
70207027

70217028
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:buffer", keywords,
70227029
&CData_Type, &cd, &size))
70237030
return NULL;
70247031

7032+
explicit_size = size >= 0;
70257033
if (size < 0)
70267034
size = _cdata_var_byte_size(cd);
70277035

@@ -7045,6 +7053,20 @@ b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
70457053
cd->c_type->ct_name);
70467054
return NULL;
70477055
}
7056+
7057+
if (explicit_size && CDataOwn_Check(cd)) {
7058+
Py_ssize_t size_max = cdataowning_size_bytes(cd);
7059+
if (size > size_max) {
7060+
char msg[256];
7061+
sprintf(msg, "ffi.buffer(cdata, bytes): creating a buffer of %llu "
7062+
"bytes over a cdata that owns only %llu bytes. This "
7063+
"will crash if you access the extra memory",
7064+
(unsigned PY_LONG_LONG)size,
7065+
(unsigned PY_LONG_LONG)size_max);
7066+
if (PyErr_WarnEx(PyExc_UserWarning, msg, 1))
7067+
return NULL;
7068+
}
7069+
}
70487070
/*WRITE(cd->c_data, size)*/
70497071
return minibuffer_new(cd->c_data, size, (PyObject *)cd);
70507072
}

c/test_c.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3498,6 +3498,18 @@ def test_bitfield_as_ppc_gcc():
34983498
_test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_BIG_ENDIAN)
34993499

35003500

3501+
def buffer_warning(cdata):
3502+
import warnings
3503+
buf = buffer(cdata)
3504+
bytes = len(buf)
3505+
with warnings.catch_warnings(record=True) as w:
3506+
warnings.simplefilter("always")
3507+
buffer(cdata, bytes)
3508+
assert len(w) == 0
3509+
buffer(cdata, bytes + 1)
3510+
assert len(w) <= 1
3511+
return len(w) == 1
3512+
35013513
def test_struct_array_no_length():
35023514
BInt = new_primitive_type("int")
35033515
BIntP = new_pointer_type(BInt)
@@ -3612,6 +3624,7 @@ def test_struct_array_no_length():
36123624
assert p.a[1] == 20
36133625
assert p.a[2] == 30
36143626
assert p.a[3] == 0
3627+
assert buffer_warning(p)
36153628
#
36163629
# struct of struct of varsized array
36173630
BStruct2 = new_struct_type("bar")
@@ -3620,6 +3633,20 @@ def test_struct_array_no_length():
36203633
for i in range(2): # try to detect heap overwrites
36213634
p = newp(new_pointer_type(BStruct2), [100, [200, list(range(50))]])
36223635
assert p.tail.y[49] == 49
3636+
assert buffer_warning(p)
3637+
assert not buffer_warning(cast(new_pointer_type(BStruct2), p))
3638+
assert not buffer_warning(cast(BIntP, p))
3639+
3640+
def test_more_buffer_warning():
3641+
BChar = new_primitive_type("char")
3642+
BCharP = new_pointer_type(BChar)
3643+
BArray = new_array_type(BCharP, 10) # char[10]
3644+
p = newp(BArray)
3645+
assert buffer_warning(p)
3646+
assert not buffer_warning(cast(BCharP, p))
3647+
p = newp(BCharP)
3648+
assert buffer_warning(p)
3649+
assert not buffer_warning(cast(BCharP, p))
36233650

36243651

36253652
def test_struct_array_no_length_explicit_position():

0 commit comments

Comments
 (0)