[pypy-commit] cffi cffi-1.0: Check sizeof(global variables) when possible

arigo noreply at buildbot.pypy.org
Mon Apr 27 18:27:40 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-1.0
Changeset: r1856:7d84833187ae
Date: 2015-04-27 17:26 +0200
http://bitbucket.org/cffi/cffi/changeset/7d84833187ae/

Log:	Check sizeof(global variables) when possible

diff --git a/_cffi1/lib_obj.c b/_cffi1/lib_obj.c
--- a/_cffi1/lib_obj.c
+++ b/_cffi1/lib_obj.c
@@ -141,8 +141,7 @@
         PyErr_Format(PyExc_AttributeError,
                      "lib '%.200s' has no function,"
                      " global variable or constant named '%.200s'",
-                     PyText_AS_UTF8(lib->l_libname),
-                     PyText_Check(name) ? PyText_AS_UTF8(name) : "?");
+                     PyText_AS_UTF8(lib->l_libname), s);
         return NULL;
     }
 
@@ -198,12 +197,23 @@
                             _CFFI_GETARG(g->type_op));
         if (ct == NULL)
             return NULL;
-        x = make_global_var(ct, g->address);
+        if (g->size != ct->ct_size &&
+                g->size != (size_t)-1 && ct->ct_size != -1) {
+            PyErr_Format(FFIError,
+                         "global variable '%.200s' should be %zd bytes "
+                         "according to the cdef, but is actually %zd",
+                         s, ct->ct_size, g->size);
+            x = NULL;
+        }
+        else {
+            x = make_global_var(ct, g->address);
+        }
         Py_DECREF(ct);
         break;
 
     default:
-        PyErr_SetString(PyExc_NotImplementedError, "in lib_build_attr");
+        PyErr_Format(PyExc_NotImplementedError, "in lib_build_attr: op=%d",
+                     (int)_CFFI_GETOP(g->type_op));
         return NULL;
     }
 
diff --git a/_cffi1/parse_c_type.h b/_cffi1/parse_c_type.h
--- a/_cffi1/parse_c_type.h
+++ b/_cffi1/parse_c_type.h
@@ -63,6 +63,7 @@
 struct _cffi_global_s {
     const char *name;
     void *address;
+    size_t size;             // -1 if unknown
     _cffi_opcode_t type_op;
 };
 
diff --git a/_cffi1/recompiler.py b/_cffi1/recompiler.py
--- a/_cffi1/recompiler.py
+++ b/_cffi1/recompiler.py
@@ -418,7 +418,8 @@
         else:
             meth_kind = 'V'   # 'METH_VARARGS'
         self._lsts["global"].append(
-            '  { "%s", _cffi_f_%s, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_%s, %d) },'
+            '  { "%s", _cffi_f_%s, (size_t)-1, '
+            '_CFFI_OP(_CFFI_OP_CPYTHON_BLTN_%s, %d) },'
             % (name, name, meth_kind, type_index))
 
     # ----------
@@ -631,7 +632,8 @@
             type_index = self._typesdict[tp]
             type_op = '_CFFI_OP(_CFFI_OP_CONSTANT, %d)' % type_index
         self._lsts["global"].append(
-            '  { "%s", _cffi_const_%s, %s },' % (name, name, type_op))
+            '  { "%s", _cffi_const_%s, (size_t)-1, %s },' % 
+            (name, name, type_op))
 
     # ----------
     # enums
@@ -648,8 +650,8 @@
         type_op = '_CFFI_OP(_CFFI_OP_ENUM, -1)'
         for enumerator in tp.enumerators:
             self._lsts["global"].append(
-                '  { "%s", _cffi_const_%s, %s },' % (enumerator, enumerator,
-                                                     type_op))
+                '  { "%s", _cffi_const_%s, (size_t)-1, %s },' %
+                (enumerator, enumerator, type_op))
         #
         if cname is not None and '$' not in cname:
             size = "sizeof(%s)" % cname
@@ -679,7 +681,8 @@
 
     def _generate_cpy_macro_ctx(self, tp, name):
         self._lsts["global"].append(
-            '  { "%s", _cffi_const_%s, _CFFI_OP(_CFFI_OP_CONSTANT_INT, 0) },' %
+            '  { "%s", _cffi_const_%s, (size_t)-1,'
+            ' _CFFI_OP(_CFFI_OP_CONSTANT_INT, 0) },' %
             (name, name))
 
     # ----------
@@ -700,9 +703,13 @@
     def _generate_cpy_variable_ctx(self, tp, name):
         tp = self._global_type(tp, name)
         type_index = self._typesdict[tp]
+        if tp.sizeof_enabled():
+            size = "sizeof(%s)" % (name,)
+        else:
+            size = "(size_t)-1"
         self._lsts["global"].append(
-            '  { "%s", &%s, _CFFI_OP(_CFFI_OP_GLOBAL_VAR, %d)},'
-            % (name, name, type_index))
+            '  { "%s", &%s, %s, _CFFI_OP(_CFFI_OP_GLOBAL_VAR, %d)},'
+            % (name, name, size, type_index))
 
     # ----------
     # emitting the opcodes for individual types
diff --git a/_cffi1/test_recompiler.py b/_cffi1/test_recompiler.py
--- a/_cffi1/test_recompiler.py
+++ b/_cffi1/test_recompiler.py
@@ -420,3 +420,23 @@
         assert test_module_name_in_package.mymod.lib.foo(10) == 42
     finally:
         sys.path[:] = old_sys_path
+
+def test_bad_size_of_global_1():
+    ffi = FFI()
+    ffi.cdef("short glob;")
+    lib = verify(ffi, "test_bad_size_of_global_1", "long glob;")
+    py.test.raises(ffi.error, "lib.glob")
+
+def test_bad_size_of_global_2():
+    ffi = FFI()
+    ffi.cdef("int glob[10];")
+    lib = verify(ffi, "test_bad_size_of_global_2", "int glob[9];")
+    e = py.test.raises(ffi.error, "lib.glob")
+    assert str(e.value) == ("global variable 'glob' should be 40 bytes "
+                            "according to the cdef, but is actually 36")
+
+def test_unspecified_size_of_global():
+    ffi = FFI()
+    ffi.cdef("int glob[];")
+    lib = verify(ffi, "test_unspecified_size_of_global", "int glob[10];")
+    lib.glob    # does not crash
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -31,6 +31,9 @@
 
     def has_c_name(self):
         return '$' not in self._get_c_name()
+    
+    def sizeof_enabled(self):
+        return False
 
     def get_cached_btype(self, ffi, finishlist, can_delay=False):
         try:
@@ -121,6 +124,9 @@
     def is_float_type(self):
         return self.ALL_PRIMITIVE_TYPES[self.name] == 'f'
 
+    def sizeof_enabled(self):
+        return True
+
     def build_backend_type(self, ffi, finishlist):
         return global_cache(self, ffi, 'new_primitive_type', self.name)
 
@@ -161,6 +167,9 @@
 class FunctionPtrType(BaseFunctionType):
     _base_pattern = '(*&)(%s)'
 
+    def sizeof_enabled(self):
+        return True
+
     def build_backend_type(self, ffi, finishlist):
         result = self.result.get_cached_btype(ffi, finishlist)
         args = []
@@ -186,6 +195,9 @@
             extra = self._base_pattern
         self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra)
 
+    def sizeof_enabled(self):
+        return True
+
     def build_backend_type(self, ffi, finishlist):
         BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True)
         return global_cache(self, ffi, 'new_pointer_type', BItem)
@@ -226,6 +238,9 @@
         self.c_name_with_marker = (
             self.item.c_name_with_marker.replace('&', brackets))
 
+    def sizeof_enabled(self):
+        return self.item.sizeof_enabled() and self.length is not None
+
     def resolve_length(self, newlength):
         return ArrayType(self.item, newlength)
 
@@ -379,6 +394,9 @@
             from . import ffiplatform
             raise ffiplatform.VerificationMissing(self._get_c_name())
 
+    def sizeof_enabled(self):
+        return self.fldtypes is not None
+
     def build_backend_type(self, ffi, finishlist):
         self.check_not_partial()
         finishlist.append(self)
@@ -407,6 +425,9 @@
         self.baseinttype = baseinttype
         self.build_c_name_with_marker()
 
+    def sizeof_enabled(self):
+        return True     # not strictly true, but external enums are obscure
+
     def force_the_name(self, forcename):
         StructOrUnionOrEnum.force_the_name(self, forcename)
         if self.forcename is None:


More information about the pypy-commit mailing list