From eccad804611139dc0e03e730342b8e07717f46cd Mon Sep 17 00:00:00 2001 From: Daniel Kroening Date: Thu, 3 Nov 2022 10:14:20 +0000 Subject: [PATCH] disallow flexible array members in unions This change yields an error if a flexible array member is used in a union. C11 allows them in structs, but not unions. --- .../union/union_flexible_array_member.c | 7 +++ .../union/union_flexible_array_member.desc | 7 +++ .../union/union_flexible_array_member_msvc.c | 13 ++++++ .../union_flexible_array_member_msvc.desc | 6 +++ ...union_flexible_array_member_msvc_address.c | 19 ++++++++ ...on_flexible_array_member_msvc_address.desc | 7 +++ .../union/union_flexible_array_member_named.c | 9 ++++ .../union_flexible_array_member_named.desc | 7 +++ .../ansi-c/union/union_zero_length_array.c | 18 ++++++++ .../ansi-c/union/union_zero_length_array.desc | 7 +++ src/ansi-c/c_typecheck_type.cpp | 46 ++++++++++++------- 11 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 regression/ansi-c/union/union_flexible_array_member.c create mode 100644 regression/ansi-c/union/union_flexible_array_member.desc create mode 100644 regression/ansi-c/union/union_flexible_array_member_msvc.c create mode 100644 regression/ansi-c/union/union_flexible_array_member_msvc.desc create mode 100644 regression/ansi-c/union/union_flexible_array_member_msvc_address.c create mode 100644 regression/ansi-c/union/union_flexible_array_member_msvc_address.desc create mode 100644 regression/ansi-c/union/union_flexible_array_member_named.c create mode 100644 regression/ansi-c/union/union_flexible_array_member_named.desc create mode 100644 regression/ansi-c/union/union_zero_length_array.c create mode 100644 regression/ansi-c/union/union_zero_length_array.desc diff --git a/regression/ansi-c/union/union_flexible_array_member.c b/regression/ansi-c/union/union_flexible_array_member.c new file mode 100644 index 00000000000..5533dbb562e --- /dev/null +++ b/regression/ansi-c/union/union_flexible_array_member.c @@ -0,0 +1,7 @@ +// C11 6.7.2.1 §18 allows flexible array members in structures, +// but not unions. + +union +{ + char flexible_array_member[]; +}; diff --git a/regression/ansi-c/union/union_flexible_array_member.desc b/regression/ansi-c/union/union_flexible_array_member.desc new file mode 100644 index 00000000000..3f6f52b982b --- /dev/null +++ b/regression/ansi-c/union/union_flexible_array_member.desc @@ -0,0 +1,7 @@ +CORE gcc-only +union_flexible_array_member.c + +^EXIT=(1|64)$ +^SIGNAL=0$ +^CONVERSION ERROR$ +-- diff --git a/regression/ansi-c/union/union_flexible_array_member_msvc.c b/regression/ansi-c/union/union_flexible_array_member_msvc.c new file mode 100644 index 00000000000..ea48c0cae40 --- /dev/null +++ b/regression/ansi-c/union/union_flexible_array_member_msvc.c @@ -0,0 +1,13 @@ +// Flexible array members in unions are allowed as an extension on Windows. + +union U +{ + int n; + char flexible_array_member[]; +}; + +int main() +{ + union U u; + u.n = 42; +} diff --git a/regression/ansi-c/union/union_flexible_array_member_msvc.desc b/regression/ansi-c/union/union_flexible_array_member_msvc.desc new file mode 100644 index 00000000000..5c330bb91a1 --- /dev/null +++ b/regression/ansi-c/union/union_flexible_array_member_msvc.desc @@ -0,0 +1,6 @@ +CORE +union_flexible_array_member_msvc.c +--i386-win32 +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/ansi-c/union/union_flexible_array_member_msvc_address.c b/regression/ansi-c/union/union_flexible_array_member_msvc_address.c new file mode 100644 index 00000000000..4ccf1f58057 --- /dev/null +++ b/regression/ansi-c/union/union_flexible_array_member_msvc_address.c @@ -0,0 +1,19 @@ +// This test exercises the Microsoft extension that allows flexible array +// members in unions by actually taking the address of the member and +// indexing into it, not just assigning to a sibling. This ensures the +// zero-length encoding produced by the type-checker survives downstream +// processing. + +union U +{ + int n; + char flexible_array_member[]; +}; + +int main() +{ + union U u; + u.n = 42; + char *p = &u.flexible_array_member[0]; + (void)p; +} diff --git a/regression/ansi-c/union/union_flexible_array_member_msvc_address.desc b/regression/ansi-c/union/union_flexible_array_member_msvc_address.desc new file mode 100644 index 00000000000..bd211ffb74e --- /dev/null +++ b/regression/ansi-c/union/union_flexible_array_member_msvc_address.desc @@ -0,0 +1,7 @@ +CORE +union_flexible_array_member_msvc_address.c +--i386-win32 +^EXIT=0$ +^SIGNAL=0$ +-- +^CONVERSION ERROR$ diff --git a/regression/ansi-c/union/union_flexible_array_member_named.c b/regression/ansi-c/union/union_flexible_array_member_named.c new file mode 100644 index 00000000000..d34f527cc3b --- /dev/null +++ b/regression/ansi-c/union/union_flexible_array_member_named.c @@ -0,0 +1,9 @@ +// C11 6.7.2.1 §18 allows flexible array members in structures, but not in +// unions. This file exercises the named-union form (the other, anonymous +// form is covered by union_flexible_array_member.c). + +union U +{ + int n; + char flexible_array_member[]; +}; diff --git a/regression/ansi-c/union/union_flexible_array_member_named.desc b/regression/ansi-c/union/union_flexible_array_member_named.desc new file mode 100644 index 00000000000..6331deeb41a --- /dev/null +++ b/regression/ansi-c/union/union_flexible_array_member_named.desc @@ -0,0 +1,7 @@ +CORE gcc-only +union_flexible_array_member_named.c + +^EXIT=(1|64)$ +^SIGNAL=0$ +^CONVERSION ERROR$ +-- diff --git a/regression/ansi-c/union/union_zero_length_array.c b/regression/ansi-c/union/union_zero_length_array.c new file mode 100644 index 00000000000..1277e773901 --- /dev/null +++ b/regression/ansi-c/union/union_zero_length_array.c @@ -0,0 +1,18 @@ +// A zero-length array [0] is a gcc extension that has historically been +// accepted anywhere in a struct or union, independently of the more recent +// C11 rule on flexible array members ([]). This test guards that +// distinction: zero-length arrays in unions must remain accepted on all +// targets (both ISO and Windows), even after the tightening of flexible +// array members in unions. + +union U +{ + int n; + char zero_length[0]; +}; + +int main() +{ + union U u; + u.n = 42; +} diff --git a/regression/ansi-c/union/union_zero_length_array.desc b/regression/ansi-c/union/union_zero_length_array.desc new file mode 100644 index 00000000000..7882906f514 --- /dev/null +++ b/regression/ansi-c/union/union_zero_length_array.desc @@ -0,0 +1,7 @@ +CORE +union_zero_length_array.c + +^EXIT=0$ +^SIGNAL=0$ +-- +^CONVERSION ERROR$ diff --git a/src/ansi-c/c_typecheck_type.cpp b/src/ansi-c/c_typecheck_type.cpp index 64867d5ed52..2362e3966f6 100644 --- a/src/ansi-c/c_typecheck_type.cpp +++ b/src/ansi-c/c_typecheck_type.cpp @@ -1070,33 +1070,45 @@ void c_typecheck_baset::typecheck_compound_body( } } - // We allow an incomplete (C99) array as _last_ member! - // Zero-length is allowed everywhere. - - if(type.id()==ID_struct || - type.id()==ID_union) - { - for(struct_union_typet::componentst::iterator - it=components.begin(); - it!=components.end(); + // We allow an incomplete array as _last_ member of a struct, + // C11 6.7.2.1 §18. These are called "flexible array members". + // Zero-length (a gcc extension) is allowed everywhere. + // Flexible array members are not allowed in unions in ISO C, but we allow + // them as an extension on Windows. + if(type.id() == ID_struct || type.id() == ID_union) + { + for(struct_union_typet::componentst::iterator it = components.begin(); + it != components.end(); it++) { - typet &c_type=it->type(); + typet &component_type = it->type(); + + if( + component_type.id() != ID_array || + !to_array_type(component_type).is_incomplete()) + { + continue; + } - if(c_type.id()==ID_array && - to_array_type(c_type).is_incomplete()) + if(type.id() == ID_struct) { // needs to be last member - if(type.id()==ID_struct && it!=--components.end()) + if(it != --components.end()) { throw errort().with_location(it->source_location()) << "flexible struct member must be last member"; } - - // make it zero-length - to_array_type(c_type).size() = from_integer(0, c_index_type()); - c_type.set(ID_C_flexible_array_member, true); } + else if(config.ansi_c.os != configt::ansi_ct::ost::OS_WIN) + { + throw errort().with_location(it->source_location()) + << "flexible array members in a union are a Microsoft " + "extension, and not permitted in ISO C"; + } + + // make it zero-length + to_array_type(component_type).size() = from_integer(0, c_index_type()); + component_type.set(ID_C_flexible_array_member, true); } }