summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatti Picus <matti.picus@gmail.com>2017-01-23 23:36:35 +0200
committerMatti Picus <matti.picus@gmail.com>2017-01-23 23:36:35 +0200
commit0d66e6fb53a0cb4dadf850df34a3ac6ecab89e2d (patch)
tree29bb6a476caff2fdeb61cd019eed2f14f0555ee2
parentbacbdbc9bf9861d6148f00a795d6afe86a1f53d8 (diff)
downloadcffi-warn-no-ref-kept.tar.gz
proof-of-concept issue a warning if assigning a dead object to a struct fieldwarn-no-ref-kept
-rw-r--r--c/_cffi_backend.c7
-rw-r--r--testing/cffi0/test_ffi_backend.py23
2 files changed, 30 insertions, 0 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
index 7156220..5a7d584 100644
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -2503,6 +2503,13 @@ cdata_setattro(CDataObject *cd, PyObject *attr, PyObject *value)
{
CFieldObject *cf;
CTypeDescrObject *ct = cd->c_type;
+ if (value && (CDataOwn_Check(value) || Py_TYPE(value) == &CDataGCP_Type) && (value->ob_refcnt == 1))
+ {
+ if (PyErr_WarnEx(PyExc_UserWarning,
+ "setting a struct field with a non-referenced value, expect trouble", 1))
+ return -1;
+ }
+
if (ct->ct_flags & CT_POINTER)
ct = ct->ct_itemdescr;
diff --git a/testing/cffi0/test_ffi_backend.py b/testing/cffi0/test_ffi_backend.py
index f9d66de..d048e0f 100644
--- a/testing/cffi0/test_ffi_backend.py
+++ b/testing/cffi0/test_ffi_backend.py
@@ -128,6 +128,27 @@ class TestFFI(backend_tests.BackendTests,
alloc5 = ffi.new_allocator(myalloc5)
py.test.raises(MemoryError, alloc5, "int[5]")
+ def test_no_ref_kept(self):
+ import gc, numpy as np
+ ffi = FFI(backend=self.Backend())
+ seen = []
+ def myalloc(size):
+ seen.append(size)
+ return ffi.new("char[]", b"X" * size)
+ def myfree(raw):
+ seen.append(raw)
+ ffi.cdef('typedef struct {char * buf;} vstruct;')
+ alloc = ffi.new_allocator(myalloc, myfree)
+ vstruct = ffi.new('vstruct[1]')
+ b = np.empty(10, dtype='uint8')
+ vstruct[0].buf = ffi.cast('char*', b.ctypes.data)
+ with pytest.warns(UserWarning):
+ vstruct[0].buf = alloc('char[]', chr(100) * 100)
+ for i in range(10):
+ gc.collect()
+ if len(seen) > 1:
+ break
+ assert len(seen) == 2
class TestBitfield:
def check(self, source, expected_ofs_y, expected_align, expected_size):
@@ -505,3 +526,5 @@ class TestBitfield:
py.test.raises(TypeError, cd)
py.test.raises(TypeError, cd, ffi.NULL)
py.test.raises(TypeError, cd, ffi.typeof("void *"))
+
+