Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/source/whatsnew.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@ v2.2.0.dev0
(`#265`_).
* The C source generated by ``cffi-gen-src`` and ``ffibuilder.emit_c_code()``
no longer depends on the interpreter running the generator.
* ``cdef()`` now raises a clean ``CDefError`` instead of crashing the whole
process with a fatal error when given an explicit definition of
``struct _IO_FILE``, the name cffi uses internally for the opaque
``FILE`` type. This could happen with an already-preprocessed system
header, which spells out the real struct. (`#281`_).

.. _`#265`: https://github.com/python-cffi/cffi/pull/265
.. _`#281`: https://github.com/python-cffi/cffi/pull/281

v2.1.0
======
Expand Down
17 changes: 17 additions & 0 deletions src/cffi/cparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,23 @@ def _get_struct_union_enum_type(self, kind, type, name=None, nested=False):
if type.decls is None:
return tp
#
if (kind == 'struct' and name is not None and
name == COMMON_TYPES['FILE'].name):
# cffi internally special-cases the C name used here (e.g.
# '_IO_FILE' on Linux) to mean the opaque FILE type: giving an
# explicit body for it collides with that special-casing and
# used to crash the process with a fatal error at runtime
# instead of failing cleanly here (issue #149). This commonly
# happens when cdef() is fed an already-preprocessed system
# header, which spells out the real definition of this struct.
raise CDefError(
"'%s %s' is the struct name that cffi uses internally for "
"the opaque 'FILE' type; giving an explicit definition for "
"it in cdef() is not supported. If this comes from a "
"preprocessed header, remove that struct definition (its "
"fields are not going to be used by cffi anyway)."
% (kind, name))
#
if tp.fldnames is not None:
raise CDefError("duplicate declaration of struct %s" % name)
fldnames = []
Expand Down
28 changes: 28 additions & 0 deletions testing/cffi0/test_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,34 @@ def test_redefine_common_type():
ffi = FFI()
ffi.cdef("typedef bool (*fn_t)(bool, bool);") # "bool," but within "( )"

def test_explicit_struct_IO_FILE_rejected():
# Explicitly giving a body for the struct that cffi uses internally
# to back the opaque 'FILE' type used to build fine but then crash
# the whole process with a fatal error the first time the type was
# used (issue #149). It's now rejected right away in cdef(). This
# is a realistic case: it happens when cdef() is fed a header that
# went through the C preprocessor, which spells out the real
# definition of 'struct _IO_FILE' coming from <stdio.h>.
ffi = FFI()
e = pytest.raises(CDefError, ffi.cdef, """
struct _IO_FILE {
int dummy;
};
typedef struct _IO_FILE FILE;
""")
assert 'struct _IO_FILE' in str(e.value)
assert 'FILE' in str(e.value)
# a forward declaration only (no body) is not ambiguous and is fine
ffi = FFI()
ffi.cdef("""
struct _IO_FILE;
typedef struct _IO_FILE FILE;
int fputs(const char *, FILE *);
""")
# plain use of FILE without redefining the struct still works
ffi = FFI()
ffi.cdef("int fputs(const char *, FILE *);")

def test_bool():
ffi = FFI()
ffi.cdef("void f(bool);")
Expand Down
Loading