diff options
| author | Matti Picus <matti.picus@gmail.com> | 2017-01-23 23:36:35 +0200 |
|---|---|---|
| committer | Matti Picus <matti.picus@gmail.com> | 2017-01-23 23:36:35 +0200 |
| commit | 0d66e6fb53a0cb4dadf850df34a3ac6ecab89e2d (patch) | |
| tree | 29bb6a476caff2fdeb61cd019eed2f14f0555ee2 | |
| parent | bacbdbc9bf9861d6148f00a795d6afe86a1f53d8 (diff) | |
| download | cffi-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.c | 7 | ||||
| -rw-r--r-- | testing/cffi0/test_ffi_backend.py | 23 |
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 *")) + + |
