Commit 67e69ada authored by Armin Rigo's avatar Armin Rigo
Browse files

Explicitly protect against variables that end up resolving at address NULL,

which can occur in random cases like test_macro_var_callback
parent 307f969f209f
......@@ -4,6 +4,7 @@ typedef void *(*gs_fetch_addr_fn)(void);
typedef struct {
PyObject_HEAD
PyObject *gs_name;
CTypeDescrObject *gs_type;
char *gs_data;
gs_fetch_addr_fn gs_fetch_addr;
......@@ -12,6 +13,7 @@ typedef struct {
static void glob_support_dealloc(GlobSupportObject *gs)
{
Py_DECREF(gs->gs_name);
Py_DECREF(gs->gs_type);
PyObject_Del(gs);
}
......@@ -41,14 +43,16 @@ static PyTypeObject GlobSupport_Type = {
#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 @@ static void *fetch_global_var_addr(GlobSupportObject *gs)
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 @@ static PyObject *cg_addressof_global_var(GlobSupportObject *gs)
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;
}
......@@ -324,7 +324,7 @@ static PyObject *lib_build_and_cache_attr(LibObject *lib, PyObject *name,
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 @@ static PyObject *lib_build_and_cache_attr(LibObject *lib, PyObject *name,
_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;
......
......@@ -1128,3 +1128,22 @@ def test_macro_var_callback():
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"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment