[pypy-commit] cffi default: Explicitly protect against variables that end up resolving at address NULL,

arigo noreply at buildbot.pypy.org
Mon Jul 6 19:12:40 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2214:57afc244cbc2
Date: 2015-07-06 19:09 +0200
http://bitbucket.org/cffi/cffi/changeset/57afc244cbc2/

Log:	Explicitly protect against variables that end up resolving at
	address NULL, which can occur in random cases like
	test_macro_var_callback

diff --git a/c/cglob.c b/c/cglob.c
--- a/c/cglob.c
+++ b/c/cglob.c
@@ -4,6 +4,7 @@
 typedef struct {
     PyObject_HEAD
 
+    PyObject         *gs_name;
     CTypeDescrObject *gs_type;
     char             *gs_data;
     gs_fetch_addr_fn  gs_fetch_addr;
@@ -12,6 +13,7 @@
 
 static void glob_support_dealloc(GlobSupportObject *gs)
 {
+    Py_DECREF(gs->gs_name);
     Py_DECREF(gs->gs_type);
     PyObject_Del(gs);
 }
@@ -41,14 +43,16 @@
 
 #define GlobSupport_Check(ob)  (Py_TYPE(ob) == &GlobSupport_Type)
 
-static PyObject *make_global_var(CTypeDescrObject *type, char *addr,
-                                 gs_fetch_addr_fn fetch_addr)
+static PyObject *make_global_var(PyObject *name, CTypeDescrObject *type,
+                                 char *addr, gs_fetch_addr_fn fetch_addr)
 {
     GlobSupportObject *gs = PyObject_New(GlobSupportObject, &GlobSupport_Type);
     if (gs == NULL)
         return NULL;
 
+    Py_INCREF(name);
     Py_INCREF(type);
+    gs->gs_name = name;
     gs->gs_type = type;
     gs->gs_data = addr;
     gs->gs_fetch_addr = fetch_addr;
@@ -68,18 +72,27 @@
         save_errno();
         Py_END_ALLOW_THREADS
     }
+    if (data == NULL) {
+        PyErr_Format(FFIError, "global variable '%s' is at address NULL",
+                     PyText_AS_UTF8(gs->gs_name));
+        return NULL;
+    }
     return data;
 }
 
 static PyObject *read_global_var(GlobSupportObject *gs)
 {
     void *data = fetch_global_var_addr(gs);
+    if (data == NULL)
+        return NULL;
     return convert_to_object(data, gs->gs_type);
 }
 
 static int write_global_var(GlobSupportObject *gs, PyObject *obj)
 {
     void *data = fetch_global_var_addr(gs);
+    if (data == NULL)
+        return -1;
     return convert_from_object(data, gs->gs_type, obj);
 }
 
@@ -91,7 +104,10 @@
         return NULL;
 
     data = fetch_global_var_addr(gs);
-    x = new_simple_cdata(data, (CTypeDescrObject *)ptrtype);
+    if (data != NULL)
+        x = new_simple_cdata(data, (CTypeDescrObject *)ptrtype);
+    else
+        x = NULL;
     Py_DECREF(ptrtype);
     return x;
 }
diff --git a/c/lib_obj.c b/c/lib_obj.c
--- a/c/lib_obj.c
+++ b/c/lib_obj.c
@@ -324,7 +324,7 @@
                 if (address == NULL)
                     return NULL;
             }
-            x = make_global_var(ct, address, NULL);
+            x = make_global_var(name, ct, address, NULL);
         }
         Py_DECREF(ct);
         break;
@@ -335,7 +335,7 @@
                             _CFFI_GETARG(g->type_op));
         if (ct == NULL)
             return NULL;
-        x = make_global_var(ct, NULL, (gs_fetch_addr_fn)g->address);
+        x = make_global_var(name, ct, NULL, (gs_fetch_addr_fn)g->address);
         Py_DECREF(ct);
         break;
 
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -1128,3 +1128,22 @@
     assert values[2] == 42
     assert p[-1] == 41
     assert p[+1] == 42
+    #
+    # if get_my_value raises or returns nonsense, the exception is printed
+    # to stderr like with any callback, but then the C expression 'my_value'
+    # expand to '*NULL'.  We assume here that '&my_value' will return NULL
+    # without segfaulting, and check for NULL when accessing the variable.
+    @ffi.callback("int *(*)(void)")
+    def get_my_value():
+        raise LookupError
+    lib.get_my_value = get_my_value
+    py.test.raises(ffi.error, getattr, lib, 'my_value')
+    py.test.raises(ffi.error, setattr, lib, 'my_value', 50)
+    py.test.raises(ffi.error, ffi.addressof, lib, 'my_value')
+    @ffi.callback("int *(*)(void)")
+    def get_my_value():
+        return "hello"
+    lib.get_my_value = get_my_value
+    py.test.raises(ffi.error, getattr, lib, 'my_value')
+    e = py.test.raises(ffi.error, setattr, lib, 'my_value', 50)
+    assert str(e.value) == "global variable 'my_value' is at address NULL"


More information about the pypy-commit mailing list