[v3,1/4] c: runtime checking for assigment of VM types

Message ID c29d04585dcc45792a43ba381bdf71dfd02a57ef.camel@tugraz.at
State New
Headers
Series [v3,1/4] c: runtime checking for assigment of VM types |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply

Commit Message

Martin Uecker July 15, 2024, 7:19 a.m. UTC
  When checking compatibility of types during assignment, collect
all pairs of types where the outermost bound needs to match at
run-time. This list is then processed to add runtime checks for
each bound.

gcc/c-family:
 * c-opt (fvla-bounds): New flag.

gcc/c:
 * c-typeck.cc (struct instrument_data): New structure.
 (comp_target_types_instr convert_for_assignment_instrument): New
 interfaces for existing functions.
 (struct comptypes_data): Add instrumentation.
 (comptypes_check_enum_int_intr): New interface.
 (comptypes_check_enum_int): Old interface (calls new).
 (comptypes_internal): Collect VLA types needed for UBSan.
 (comp_target_types_instr): New interface.
 (comp_target_types): Old interface (calls new).
 (function_types_compatible_p): No instrumentation for function
 arguments.
 (process_vm_constraints): New function.
 (convert_argument): Adapt.
 (convert_for_assignment_instrument): New interface.
 (convert_for_assignment): Instrument assignments.
 (c_instrument_vm_assign): Helper function.
 (process_vm_constraints): Helper function.

gcc/doc/:
 * invoke.texi (fvla-bounds): Document new flag.

gcc/testsuite:
 * gcc.dg/vla-bounds-1.c: New test.
 * gcc.dg/vla-bounds-assign-1.c: New test.
 * gcc.dg/vla-bounds-assign-2.c: New test.
 * gcc.dg/vla-bounds-assign-3.c: New test.
 * gcc.dg/vla-bounds-assign-4.c: New test.
 * gcc.dg/vla-bounds-func-1.c: New test.
 * gcc.dg/vla-bounds-init-1.c: New test.
 * gcc.dg/vla-bounds-init-2.c: New test.
 * gcc.dg/vla-bounds-init-3.c: New test.
 * gcc.dg/vla-bounds-init-4.c: New test.
 * gcc.dg/vla-bounds-nest-1.c: New test.
 * gcc.dg/vla-bounds-nest-2.c: New test.
 * gcc.dg/vla-bounds-ret-1.c: New test.
 * gcc.dg/vla-bounds-ret-2.c: New test.
---
 gcc/c-family/c.opt | 4 +
 gcc/c/c-typeck.cc | 171 ++++++++++++++++++---
 gcc/doc/invoke.texi | 9 ++
 gcc/testsuite/gcc.dg/vla-bounds-1.c | 74 +++++++++
 gcc/testsuite/gcc.dg/vla-bounds-assign-1.c | 32 ++++
 gcc/testsuite/gcc.dg/vla-bounds-assign-2.c | 30 ++++
 gcc/testsuite/gcc.dg/vla-bounds-assign-3.c | 43 ++++++
 gcc/testsuite/gcc.dg/vla-bounds-assign-4.c | 26 ++++
 gcc/testsuite/gcc.dg/vla-bounds-func-1.c | 54 +++++++
 gcc/testsuite/gcc.dg/vla-bounds-init-1.c | 26 ++++
 gcc/testsuite/gcc.dg/vla-bounds-init-2.c | 25 +++
 gcc/testsuite/gcc.dg/vla-bounds-init-3.c | 25 +++
 gcc/testsuite/gcc.dg/vla-bounds-init-4.c | 27 ++++
 gcc/testsuite/gcc.dg/vla-bounds-nest-1.c | 31 ++++
 gcc/testsuite/gcc.dg/vla-bounds-nest-2.c | 26 ++++
 gcc/testsuite/gcc.dg/vla-bounds-ret-1.c | 27 ++++
 gcc/testsuite/gcc.dg/vla-bounds-ret-2.c | 28 ++++
 17 files changed, 639 insertions(+), 19 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-func-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-2.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-3.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-4.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-ret-2.c
  

Comments

Qing Zhao July 18, 2024, 6:32 p.m. UTC | #1
Hi, Martin,

I briefly reviewed these 4 patches this week. 

Overall, I think that this is a nice new security feature to be added into gcc. 

At the same time, I have the following questions about the patches:

1. Does the name of the option mismatch the description of the option?

-fvla-bounds
Emit run-time consistency checks for variably-modified types.

Is “variably-modified type” equal to “via” (variable length array)? 

I see all other current options -Wvla-parameter, -Wvla, etc, the  “vla”s all are 
for "variable length array" in the description. 

So, a little confused with the “variable-modified type” you used in the documentation for
“vla”. 

I see that in GCC source code, the concept of “variable-modified type” includes “via”. 
but seems not limited to “vla”. 

2. I feel that the documentation of the option need some simple examples to show 
the new feature’s capabilities to help users to use it.

Currently. The doc of the option:
> +@item -fvla-bounds
> +This option is only available when compiling C code. If activated,
> +additional code is emitted that verifies at run time for assignments
> +involving variably-modified types that corresponding size expressions
> +evaluate to the same value.

Providing multiple simple examples below will be helpful. 
 
3.  From my understanding, with -fvla-bounds, when there is any bound mismatch 
during runtime, the program will trap, but without any information on why such
trap happens. 

Is this correct?

If so, I think that this might need to be documented to inform user the current behavior,
then the users will have an accurate expectation on what might happen with this
option. 

Later some error message might need to be issued at the same time when the trap happens.

> On Jul 15, 2024, at 03:19, Martin Uecker <uecker@tugraz.at> wrote:
> 
> 
> When checking compatibility of types during assignment, collect
> all pairs of types where the outermost bound needs to match at
> run-time. This list is then processed to add runtime checks for
> each bound.
> 
> gcc/c-family:
> * c-opt (fvla-bounds): New flag.
> 
> gcc/c:
> * c-typeck.cc (struct instrument_data): New structure.
> (comp_target_types_instr convert_for_assignment_instrument): New
> interfaces for existing functions.
> (struct comptypes_data): Add instrumentation.
> (comptypes_check_enum_int_intr): New interface.
> (comptypes_check_enum_int): Old interface (calls new).
> (comptypes_internal): Collect VLA types needed for UBSan.
> (comp_target_types_instr): New interface.
> (comp_target_types): Old interface (calls new).
> (function_types_compatible_p): No instrumentation for function
> arguments.
> (process_vm_constraints): New function.
> (convert_argument): Adapt.
> (convert_for_assignment_instrument): New interface.
> (convert_for_assignment): Instrument assignments.
> (c_instrument_vm_assign): Helper function.
> (process_vm_constraints): Helper function.

“process_vm_constraints” repeated twice in the above. 


thanks.

Qing
> 
> gcc/doc/:
> * invoke.texi (fvla-bounds): Document new flag.
> 
> gcc/testsuite:
> * gcc.dg/vla-bounds-1.c: New test.
> * gcc.dg/vla-bounds-assign-1.c: New test.
> * gcc.dg/vla-bounds-assign-2.c: New test.
> * gcc.dg/vla-bounds-assign-3.c: New test.
> * gcc.dg/vla-bounds-assign-4.c: New test.
> * gcc.dg/vla-bounds-func-1.c: New test.
> * gcc.dg/vla-bounds-init-1.c: New test.
> * gcc.dg/vla-bounds-init-2.c: New test.
> * gcc.dg/vla-bounds-init-3.c: New test.
> * gcc.dg/vla-bounds-init-4.c: New test.
> * gcc.dg/vla-bounds-nest-1.c: New test.
> * gcc.dg/vla-bounds-nest-2.c: New test.
> * gcc.dg/vla-bounds-ret-1.c: New test.
> * gcc.dg/vla-bounds-ret-2.c: New test.
> ---
> gcc/c-family/c.opt | 4 +
> gcc/c/c-typeck.cc | 171 ++++++++++++++++++---
> gcc/doc/invoke.texi | 9 ++
> gcc/testsuite/gcc.dg/vla-bounds-1.c | 74 +++++++++
> gcc/testsuite/gcc.dg/vla-bounds-assign-1.c | 32 ++++
> gcc/testsuite/gcc.dg/vla-bounds-assign-2.c | 30 ++++
> gcc/testsuite/gcc.dg/vla-bounds-assign-3.c | 43 ++++++
> gcc/testsuite/gcc.dg/vla-bounds-assign-4.c | 26 ++++
> gcc/testsuite/gcc.dg/vla-bounds-func-1.c | 54 +++++++
> gcc/testsuite/gcc.dg/vla-bounds-init-1.c | 26 ++++
> gcc/testsuite/gcc.dg/vla-bounds-init-2.c | 25 +++
> gcc/testsuite/gcc.dg/vla-bounds-init-3.c | 25 +++
> gcc/testsuite/gcc.dg/vla-bounds-init-4.c | 27 ++++
> gcc/testsuite/gcc.dg/vla-bounds-nest-1.c | 31 ++++
> gcc/testsuite/gcc.dg/vla-bounds-nest-2.c | 26 ++++
> gcc/testsuite/gcc.dg/vla-bounds-ret-1.c | 27 ++++
> gcc/testsuite/gcc.dg/vla-bounds-ret-2.c | 28 ++++
> 17 files changed, 639 insertions(+), 19 deletions(-)
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-1.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-func-1.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-1.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-2.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-3.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-4.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
> create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-ret-2.c
> 
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index a52682d835c..786cd4ce52a 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -2324,6 +2324,10 @@ fvisibility-ms-compat
> C++ ObjC++ Var(flag_visibility_ms_compat)
> Changes visibility to match Microsoft Visual Studio by default.
> +fvla-bounds
> +C Var(flag_vla_bounds)
> +Emit run-time consistency checks for variably-modified types.
> +
> fvtable-gc
> C++ ObjC++ WarnRemoved
> No longer supported.
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index 7e0f01ed22b..59940a29b53 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -97,11 +97,12 @@ static tree qualify_type (tree, tree);
> struct comptypes_data;
> static bool tagged_types_tu_compatible_p (const_tree, const_tree,
> struct comptypes_data *);
> -static bool comp_target_types (location_t, tree, tree);
> static bool function_types_compatible_p (const_tree, const_tree,
> struct comptypes_data *);
> static bool type_lists_compatible_p (const_tree, const_tree,
> struct comptypes_data *);
> +static bool comp_target_types_instr (location_t, tree, tree,
> + struct instrument_data **);
> static int convert_arguments (location_t, vec<location_t>, tree,
> vec<tree, va_gc> *, vec<tree, va_gc> *, tree,
> tree);
> @@ -109,6 +110,9 @@ static tree pointer_diff (location_t, tree, tree, tree *);
> static tree convert_for_assignment (location_t, location_t, tree, tree, tree,
> enum impl_conv, bool, tree, tree, int,
> int = 0);
> +static tree convert_for_assignment_instrument (location_t, location_t, tree, tree, tree,
> + enum impl_conv, bool, tree, tree, int, int,
> + struct instrument_data **);
> static tree valid_compound_expr_initializer (tree, tree);
> static void push_string (const char *);
> static void push_member_name (tree);
> @@ -1193,6 +1197,39 @@ comptypes_verify (tree type1, tree type2)
> return true;
> }
> +
> +/* Instrument assignment of variably modified types. */
> +
> +static tree
> +c_instrument_vm_assign (location_t loc, tree a, tree b)
> +{
> + gcc_assert (flag_vla_bounds);
> +
> + gcc_assert (TREE_CODE (a) == ARRAY_TYPE);
> + gcc_assert (TREE_CODE (b) == ARRAY_TYPE);
> +
> + tree as = TYPE_MAX_VALUE (TYPE_DOMAIN (a));
> + tree bs = TYPE_MAX_VALUE (TYPE_DOMAIN (b));
> +
> + as = fold_build2 (PLUS_EXPR, sizetype, as, size_one_node);
> + bs = fold_build2 (PLUS_EXPR, sizetype, bs, size_one_node);
> +
> + tree t = build2 (NE_EXPR, boolean_type_node, as, bs);
> + tree tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
> +
> + return build3 (COND_EXPR, void_type_node, t, tt, void_node);
> +}
> +
> +
> +
> +struct instrument_data {
> +
> + tree t1;
> + tree t2;
> + struct instrument_data *next;
> +};
> +
> +
> struct comptypes_data {
> bool enum_and_int_p;
> bool different_types_p;
> @@ -1202,6 +1239,8 @@ struct comptypes_data {
> bool equiv;
> const struct tagged_tu_seen_cache* cache;
> +
> + struct instrument_data **instr_vec;
> };
> /* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
> @@ -1241,11 +1280,14 @@ comptypes_same_p (tree type1, tree type2)
> /* Like comptypes, but if it returns non-zero because enum and int are
> compatible, it sets *ENUM_AND_INT_P to true. */
> -int
> -comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
> +static int
> +comptypes_check_enum_int_instr (tree type1, tree type2, bool *enum_and_int_p,
> + struct instrument_data **instr_vec)
> {
> struct comptypes_data data = { };
> + data.instr_vec = instr_vec;
> bool ret = comptypes_internal (type1, type2, &data);
> +
> *enum_and_int_p = data.enum_and_int_p;
> gcc_checking_assert (!ret || comptypes_verify (type1, type2));
> @@ -1253,6 +1295,12 @@ comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
> return ret ? (data.warning_needed ? 2 : 1) : 0;
> }
> +int
> +comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
> +{
> + return comptypes_check_enum_int_instr (type1, type2, enum_and_int_p, NULL);
> +}
> +
> /* Like comptypes, but if it returns nonzero for different types, it
> sets *DIFFERENT_TYPES_P to true. */
> @@ -1465,7 +1513,18 @@ comptypes_internal (const_tree type1, const_tree type2,
> if (d1_variable != d2_variable)
> data->different_types_p = true;
> if (d1_variable || d2_variable)
> - return true;
> + {
> + if (NULL != data->instr_vec)
> + {
> + struct instrument_data *id
> + = (struct instrument_data *)xmalloc (sizeof *id);;
> + id->t1 = TYPE_MAIN_VARIANT (t2);
> + id->t2 = TYPE_MAIN_VARIANT (t1);
> + id->next = *data->instr_vec;
> + *data->instr_vec = id;
> + }
> + return true;
> + }
> if (d1_zero && d2_zero)
> return true;
> if (d1_zero || d2_zero
> @@ -1501,7 +1560,8 @@ comptypes_internal (const_tree type1, const_tree type2,
> subset of the other. */
> static bool
> -comp_target_types (location_t location, tree ttl, tree ttr)
> +comp_target_types_instr (location_t location, tree ttl, tree ttr,
> + struct instrument_data **instr_vec)
> {
> int val;
> int val_ped;
> @@ -1535,8 +1595,7 @@ comp_target_types (location_t location, tree ttl, tree ttr)
> ? c_build_qualified_type (TYPE_MAIN_VARIANT (mvr), TYPE_QUAL_ATOMIC)
> : TYPE_MAIN_VARIANT (mvr));
> - enum_and_int_p = false;
> - val = comptypes_check_enum_int (mvl, mvr, &enum_and_int_p);
> + val = comptypes_check_enum_int_instr (mvl, mvr, &enum_and_int_p, instr_vec);
> if (val == 1 && val_ped != 1)
> pedwarn_c11 (location, OPT_Wpedantic, "invalid use of pointers to arrays with different qualifiers "
> @@ -1551,6 +1610,13 @@ comp_target_types (location_t location, tree ttl, tree ttr)
> return val;
> }
> +
> +static int
> +comp_target_types (location_t location, tree ttl, tree ttr)
> +{
> + return comp_target_types_instr (location, ttl, ttr, NULL);
> +}
> +
> 
> /* Subroutines of `comptypes'. */
> @@ -1815,8 +1881,13 @@ function_types_compatible_p (const_tree f1, const_tree f2,
> return val;
> }
> - /* Both types have argument lists: compare them and propagate results. */
> + /* Both types have argument lists: compare them and propagate results.
> + Turn off instrumentation for bounds as these are all arrays of
> + unspecified size. */
> + auto instr_vec_tmp = data->instr_vec;
> + data->instr_vec = NULL;
> val1 = type_lists_compatible_p (args1, args2, data);
> + data->instr_vec = instr_vec_tmp;
> return val1;
> }
> @@ -3857,10 +3928,11 @@ convert_argument (location_t ploc, tree function, tree fundecl,
> if (excess_precision)
> val = build1 (EXCESS_PRECISION_EXPR, valtype, val);
> - tree parmval = convert_for_assignment (ploc, ploc, type,
> - val, origtype, ic_argpass,
> - npc, fundecl, function,
> - parmnum + 1, warnopt);
> + tree parmval = convert_for_assignment_instrument (ploc, ploc, type,
> + val, origtype, ic_argpass,
> + npc, fundecl, function,
> + parmnum + 1, warnopt,
> + NULL);
> if (targetm.calls.promote_prototypes (fundecl ? TREE_TYPE (fundecl) : 0)
> && INTEGRAL_TYPE_P (type)
> @@ -3870,6 +3942,24 @@ convert_argument (location_t ploc, tree function, tree fundecl,
> return parmval;
> }
> +
> +/* Process all constraints for variably-modified types. */
> +
> +static tree
> +process_vm_constraints (location_t location,
> + struct instrument_data **instr_vec)
> +{
> + tree instr_expr = void_node;
> +
> + for (struct instrument_data* d = *instr_vec; d; d = d->next)
> + {
> + tree in = c_instrument_vm_assign (location, d->t1, d->t2);
> + instr_expr = fold_build2 (COMPOUND_EXPR, void_type_node, in, instr_expr);
> + }
> + return instr_expr;
> +}
> +
> +
> /* Convert the argument expressions in the vector VALUES
> to the types in the list TYPELIST.
> @@ -7313,7 +7403,50 @@ static tree
> convert_for_assignment (location_t location, location_t expr_loc, tree type,
> tree rhs, tree origtype, enum impl_conv errtype,
> bool null_pointer_constant, tree fundecl,
> - tree function, int parmnum, int warnopt /* = 0 */)
> + tree function, int parmnum, int warnopt)
> +{
> + struct instrument_data *instr_first = NULL;
> + struct instrument_data **instr_vec = NULL;
> +
> + if (flag_vla_bounds && (ic_init_const != errtype))
> + instr_vec = &instr_first;
> +
> + tree ret = convert_for_assignment_instrument (location, expr_loc, type,
> + rhs, origtype, errtype,
> + null_pointer_constant, fundecl,
> + function, parmnum, warnopt,
> + instr_vec);
> + if (instr_vec)
> + {
> + if (ret && ret != error_mark_node && instr_first != NULL)
> + {
> + /* We have to make sure that the rhs is evaluated first,
> + because we may use size expressions in it to check bounds. */
> + tree instr_expr = process_vm_constraints (location, instr_vec);
> + if (instr_expr != void_node)
> + {
> + ret = save_expr (ret);
> + instr_expr = fold_build2 (COMPOUND_EXPR, void_type_node, ret, instr_expr);
> + ret = fold_build2 (COMPOUND_EXPR, TREE_TYPE (ret), instr_expr, ret);
> + }
> + }
> + while (instr_first)
> + {
> + struct instrument_data *next = instr_first->next;
> + free (instr_first);
> + instr_first = next;
> + }
> + instr_vec = NULL;
> + }
> + return ret;
> +}
> +
> +static tree
> +convert_for_assignment_instrument (location_t location, location_t expr_loc, tree type,
> + tree rhs, tree origtype, enum impl_conv errtype,
> + bool null_pointer_constant, tree fundecl,
> + tree function, int parmnum, int warnopt,
> + struct instrument_data **instr_vec)
> {
> enum tree_code codel = TREE_CODE (type);
> tree orig_rhs = rhs;
> @@ -7557,11 +7690,11 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
> rhs = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (rhs)), rhs);
> SET_EXPR_LOCATION (rhs, location);
> - rhs = convert_for_assignment (location, expr_loc,
> - build_pointer_type (TREE_TYPE (type)),
> - rhs, origtype, errtype,
> - null_pointer_constant, fundecl, function,
> - parmnum, warnopt);
> + rhs = convert_for_assignment_instrument (location, expr_loc,
> + build_pointer_type (TREE_TYPE (type)),
> + rhs, origtype, errtype,
> + null_pointer_constant, fundecl, function,
> + parmnum, warnopt, instr_vec);
> if (rhs == error_mark_node)
> return error_mark_node;
> @@ -7955,7 +8088,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
> Meanwhile, the lhs target must have all the qualifiers of the rhs. */
> if ((VOID_TYPE_P (ttl) && !TYPE_ATOMIC (ttl))
> || (VOID_TYPE_P (ttr) && !TYPE_ATOMIC (ttr))
> - || (target_cmp = comp_target_types (location, type, rhstype))
> + || (target_cmp = comp_target_types_instr (location, type, rhstype, instr_vec))
> || is_opaque_pointer
> || ((c_common_unsigned_type (mvl)
> == c_common_unsigned_type (mvr))
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 403ea9da1ab..8d6d68e4f27 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -10698,6 +10698,7 @@ void g (int n)
> @option{-Warray-parameter} option triggers warnings for similar problems
> involving ordinary array arguments.
> +
> @opindex Wvolatile-register-var
> @opindex Wno-volatile-register-var
> @item -Wvolatile-register-var
> @@ -20831,6 +20832,14 @@ computing CRC32).
> The @var{string} should be different for every file you compile.
> +@opindex fvla-bounds
> +@item -fvla-bounds
> +This option is only available when compiling C code. If activated,
> +additional code is emitted that verifies at run time for assignments
> +involving variably-modified types that corresponding size expressions
> +evaluate to the same value.
> +
> +
> @opindex save-temps
> @item -save-temps
> Store the usual ``temporary'' intermediate files permanently; name them
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-1.c b/gcc/testsuite/gcc.dg/vla-bounds-1.c
> new file mode 100644
> index 00000000000..093f5e7ba25
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-1.c
> @@ -0,0 +1,74 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +// test various valid initializations and assignments
> +
> +int main()
> +{
> + int m = 4, n = 3;
> +
> + int u = 4;
> + int v = 3;
> +
> + /* initialization */
> +
> + char a[4];
> + char (*pa)[u] = &a;
> + char (*qa)[v];
> +
> + char b[u];
> + const char (*pb)[u] = &b;
> + char (*qb)[v];
> +
> + char c[4][3];
> + char (*pc0)[u][v] = &c;
> + char (*qc0)[v][u];
> +
> + char (*pc1)[u][3] = &c;
> + char (*qc1)[v][3];
> +
> + char (*pc2)[4][v] = &c;
> + char (*qc2)[4][u];
> +
> + char (*pc3)[][v] = &c; 
> +
> + char d[u][v];
> + char (*pd0)[4][3] = &d;
> + char (*qd0)[3][4];
> +
> + char (*pd1)[u][3] = &d; 
> + char (*qd1)[v][4];
> +
> + char (*pd2)[4][v] = &d;
> + char (*qd2)[3][u];
> +
> + char (*pd3)[u][v] = &d;
> + char (*qd3)[v][u];
> +
> + char e[4][v];
> + char (*pe0)[4][3] = &e;
> + char (*qe0)[4][4];
> +
> + char f[u][3];
> + char (*pf)[4][3] = &f;
> + char (*qf)[3][3];
> +
> + char (*g[u])[v];
> + char (*(*pg)[u])[v] = &g;
> +
> + /* assignment */
> +
> + pa = &a;
> + pb = &b;
> + pc0 = &c;
> + pc1 = &c;
> + pc2 = &c;
> + pd0 = &d;
> + pd1 = &d;
> + pd2 = &d;
> + pd3 = &d;
> + pe0 = &e;
> + pf = &f;
> +
> + return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
> new file mode 100644
> index 00000000000..ffef7e502dd
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
> @@ -0,0 +1,32 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test valid and invalid assignments involving pointers to arrays of
> + variable length. */
> +
> +static void handler(int) { exit(0); }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + int u = 4;
> + int v = 3;
> +
> + char a[4];
> + char (*pa)[u];
> + char (*qa)[v];
> +
> + char b[u];
> + const char (*pb)[u];
> +
> + pa = &a;
> + pb = &b;
> + qa = &a; // 3 != 4
> + abort();
> +
> + return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
> new file mode 100644
> index 00000000000..cc445dea459
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
> @@ -0,0 +1,30 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test valid and invalid assignment of a pointer to a 2D array to pointers
> + to 2D arrays of variable length. */
> +
> +static void handler(int) { exit(0); }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + int u = 4;
> + int v = 3;
> +
> + char c[4][3];
> + char (*pc0)[u][v];
> + char (*qc0)[v][u];
> + char (*pc1)[u][3];
> + char (*pc2)[4][v];
> +
> + pc0 = &c;
> + pc1 = &c;
> + pc2 = &c;
> + qc0 = &c; // 3 != 4, 4 != 3
> + abort();
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
> new file mode 100644
> index 00000000000..068e9a57e65
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
> @@ -0,0 +1,43 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test valid and invalid assignment of a pointer to a 2D VLA to pointers
> + to 2D arrays of variable length. */
> +
> +static void handler(int) { exit(0); }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + int u = 4;
> + int v = 3;
> +
> + char d[u][v];
> + char (*pd0)[4][3];
> + char (*pd1)[u][3]; 
> + char (*pd2)[4][v];
> + char (*pd3)[u][v];
> +
> + char e[4][v];
> + char (*pe0)[4][3];
> +
> + char f[u][3];
> + char (*pf)[4][3];
> + char (*qf)[3][3];
> +
> + /* assignment */
> +
> + pd0 = &d;
> + pd1 = &d;
> + pd2 = &d;
> + pd3 = &d;
> + pe0 = &e;
> +
> + pf = &f;
> + qf = &f; // 3 != 4
> + abort();
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
> new file mode 100644
> index 00000000000..b274e237eba
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
> @@ -0,0 +1,26 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test assignment of pointer to function returning 2D array to pointer to
> + function returning a pointer to a variable length array. */
> +
> +static void handler(int) { exit(0); }
> +
> +int m, n;
> +static char (*z0(void))[5][5] { char (*p)[m][n] = 0; return p; }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + m = 4, n = 3;
> +
> + int u = 4;
> + int v = 3;
> +
> + char (*(*p)(void))[u][v] = &z0; // 4 != 5, 3 != 5
> + abort();
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-func-1.c b/gcc/testsuite/gcc.dg/vla-bounds-func-1.c
> new file mode 100644
> index 00000000000..8256999fc50
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-func-1.c
> @@ -0,0 +1,54 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +// make sure we do not ICE on any of these
> +
> +const char* name = "hallo";
> +
> +typedef void (*ht)(int n, int m, char x[n][m]);
> +void e(ht) { }
> +
> +int n, m;
> +static void f0(char a[n][m]) { }
> +static void f1(int u, int v, char a[u][v]) { }
> +static void f2(int u, int v, char a[u][v]) { }
> +
> +void f(void)
> +{
> + int x = 1;
> + int (*m)[x] = 0;
> + m = ({ long* d2; (int (*)[d2[0]])(0); });
> +
> + /* function pointer assignments */
> +
> + void (*gp)(char x[4][3]) = f0;
> + void (*hp)(int n, int m, char x[n][m]) = f1;
> + ht hp2 = f1;
> + e(f1);
> +
> + /* composite type */
> +
> + int u = 3; int v = 4;
> + char a[u][v];
> + (1 ? f1 : f2)(u, v, a);
> +}
> +
> +/* size expression in parameter */
> +
> +extern void a(long N, char (*a)[N]);
> +
> +static void b(void)
> +{
> + a(1, ({ int d = 0; (char (*)[d])0; }) );
> +}
> +
> +/* composite type */
> +
> +int c(int u, char (*a)[u]);
> +int c(int u, char (*a)[u]) { }
> +
> +int d(void)
> +{
> + char a[3];
> + c(3, &a);
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-1.c b/gcc/testsuite/gcc.dg/vla-bounds-init-1.c
> new file mode 100644
> index 00000000000..3e608ba0c34
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-init-1.c
> @@ -0,0 +1,26 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test valid and invalid initialization of pointers to arrays of
> + variable length. */
> +
> +static void handler(int) { exit(0); }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + int u = 4;
> + int v = 3;
> +
> + char b[u];
> + const char (*pb)[u] = &b;
> +
> + char a[4];
> + char (*pa)[u] = &a;
> + char (*qa)[v] = &a; // 3 != 4
> + abort();
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-2.c b/gcc/testsuite/gcc.dg/vla-bounds-init-2.c
> new file mode 100644
> index 00000000000..d26683e6c97
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-init-2.c
> @@ -0,0 +1,25 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test valid and invalid initialization of pointers to 2D arrays of
> + variable length with pointer to 2D regular array. */
> +
> +static void handler(int) { exit(0); }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + int u = 4;
> + int v = 3;
> +
> + char c[4][3];
> + char (*pc1)[u][3] = &c;
> + char (*pc2)[4][v] = &c;
> + char (*pc0)[u][v] = &c;
> + char (*qc0)[v][u] = &c; // 3 != 4, 4 != 3
> + abort();
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-3.c b/gcc/testsuite/gcc.dg/vla-bounds-init-3.c
> new file mode 100644
> index 00000000000..7ab706af62e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-init-3.c
> @@ -0,0 +1,25 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test valid and invalid initialization of pointers to 2D arrays of
> + variable length with incomplete first dimension. */
> +
> +static void handler(int) { exit(0); }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + int u = 4;
> + int v = 3;
> +
> + /* initialization */
> +
> + char c[4][3];
> + char (*pc3)[][v] = &c; 
> + char (*qc3)[][u] = &c; // 4 != 3
> + abort();
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-4.c b/gcc/testsuite/gcc.dg/vla-bounds-init-4.c
> new file mode 100644
> index 00000000000..f20de91c156
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-init-4.c
> @@ -0,0 +1,27 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test valid and invalid initialization of pointers to 2D arrays of
> + variable length with pointer to 2D VLA. */
> +
> +static void handler(int) { exit(0); }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + int u = 4;
> + int v = 3;
> +
> + /* initialization */
> + char d[u][v];
> + char (*pd0)[4][3] = &d;
> + char (*pd1)[u][3] = &d; 
> + char (*pd2)[4][v] = &d;
> + char (*pd3)[u][v] = &d;
> + char (*qd0)[3][4] = &d; // 3 != 4, 4 != 3
> + abort();
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c b/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
> new file mode 100644
> index 00000000000..7f33bc95e29
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
> @@ -0,0 +1,31 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +/* { dg-require-effective-target trampolines } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test valid and invalid assignment of a nested function mit variably
> + modified return type. */
> +
> +void handler(int) { exit(0); }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + int n = 3;
> + char b[4];
> + char (*f())[++n] { return &b; }
> +
> + if (4 != sizeof(*f()))
> + abort();
> +
> + char (*(*p)())[n++] = &f;
> +
> + if (4 != sizeof(*(*p)()))
> + abort();
> +
> + char (*(*p2)())[++n] = &f;
> + abort();
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c b/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
> new file mode 100644
> index 00000000000..13f77d4e185
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
> @@ -0,0 +1,26 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +/* { dg-require-effective-target trampolines } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test valid and invalid assignment of regular function pointer
> + return array to pointer to function with variably modified
> + return type. */
> +
> +void handler(int) { exit(0); }
> +
> +static char bb[4][4];
> +static char (*g())[4][4] { return &bb; }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + int n = 4;
> +
> + char (*(*p)())[n][4] = &g;
> + char (*(*q)())[++n][4] = &g;;
> + abort();
> +}
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c b/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
> new file mode 100644
> index 00000000000..43919927ab8
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
> @@ -0,0 +1,27 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test returning a pointer to a 2D variable length array in
> + a function returning a pointer to a regular 2D array. */
> +
> +static void handler(int) { exit(0); }
> +
> +int m, n;
> +
> +static char (*z0(void))[5][5] { char (*p)[m][n] = 0; return p; }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + m = 5, n = 5;
> + z0();
> +
> + m = 4, n = 3;
> + z0(); // 5 != 3, 5, != 3
> + abort();
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c b/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c
> new file mode 100644
> index 00000000000..af8475e9a5f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c
> @@ -0,0 +1,28 @@
> +/* { dg-do run } */
> +/* { dg-options "-fvla-bounds" } */
> +
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +/* Test returning a pointer to a 2D variable length array with
> + a fist constant dimension in a function returning a pointer
> + to a regular 2D array. */
> +
> +static void handler(int) { exit(0); }
> +
> +int n, m;
> +
> +static char (*z1(void))[5][5] { char (*p)[5][n] = 0; return p; }
> +
> +int main()
> +{
> + signal(SIGILL, handler);
> +
> + m = 5, n = 5;
> + z1();
> +
> + m = 4, n = 3;
> + z1(); // 4 != 3
> + abort();
> +}
> +
> -- 
> 2.39.2
> 
> 
>
  
Martin Uecker July 18, 2024, 8:39 p.m. UTC | #2
Am Donnerstag, dem 18.07.2024 um 18:32 +0000 schrieb Qing Zhao:
> Hi, Martin,
> 
> I briefly reviewed these 4 patches this week. 
> 
> Overall, I think that this is a nice new security feature to be added into gcc. 

Thank you! (not only for security! it currently helps me with
my numerics)

> 
> At the same time, I have the following questions about the patches:
> 
> 1. Does the name of the option mismatch the description of the option?
> 
> -fvla-bounds
> Emit run-time consistency checks for variably-modified types.
> 
> Is “variably-modified type” equal to “via” (variable length array)? 
> 
> I see all other current options -Wvla-parameter, -Wvla, etc, the  “vla”s all are 
> for "variable length array" in the description. 
> 
> So, a little confused with the “variable-modified type” you used in the documentation for
> “vla”. 
> 
> I see that in GCC source code, the concept of “variable-modified type” includes “via”. 
> but seems not limited to “vla”. 

Variably modified types are types that are a VLA
or are derived from a VLA somehow, e.g. a pointer to a
VLA.  So I would say both terms are correct in this
context because we check all bounds in a variably
modified type but each bound belongs to an embedded
VLA type.  But anyway, I agree that the description
would better if it would not refer to variably modified
types.

I also wonder about the name of the option too. Maybe
there is a better name? 

> 
> 2. I feel that the documentation of the option need some simple examples to show 
> the new feature’s capabilities to help users to use it.
> 
> Currently. The doc of the option:
> > +@item -fvla-bounds
> > +This option is only available when compiling C code. If activated,
> > +additional code is emitted that verifies at run time for assignments
> > +involving variably-modified types that corresponding size expressions
> > +evaluate to the same value.
> 
> Providing multiple simple examples below will be helpful. 

Yes.
>  
> 3.  From my understanding, with -fvla-bounds, when there is any bound mismatch 
> during runtime, the program will trap, but without any information on why such
> trap happens. 
> 
> Is this correct?
> 
> If so, I think that this might need to be documented to inform user the current behavior,
> then the users will have an accurate expectation on what might happen with this
> option. 

That makes sense.
> 
> Later some error message might need to be issued at the same time when the trap happens.
> 
> > On Jul 15, 2024, at 03:19, Martin Uecker <uecker@tugraz.at> wrote:
> > 
> > 
> > When checking compatibility of types during assignment, collect
> > all pairs of types where the outermost bound needs to match at
> > run-time. This list is then processed to add runtime checks for
> > each bound.
> > 
> > gcc/c-family:
> > * c-opt (fvla-bounds): New flag.
> > 
> > gcc/c:
> > * c-typeck.cc (struct instrument_data): New structure.
> > (comp_target_types_instr convert_for_assignment_instrument): New
> > interfaces for existing functions.
> > (struct comptypes_data): Add instrumentation.
> > (comptypes_check_enum_int_intr): New interface.
> > (comptypes_check_enum_int): Old interface (calls new).
> > (comptypes_internal): Collect VLA types needed for UBSan.
> > (comp_target_types_instr): New interface.
> > (comp_target_types): Old interface (calls new).
> > (function_types_compatible_p): No instrumentation for function
> > arguments.
> > (process_vm_constraints): New function.
> > (convert_argument): Adapt.
> > (convert_for_assignment_instrument): New interface.
> > (convert_for_assignment): Instrument assignments.
> > (c_instrument_vm_assign): Helper function.
> > (process_vm_constraints): Helper function.
> 
> “process_vm_constraints” repeated twice in the above. 
> 
> 
> thanks.

Thank you!

Martin

> 
> Qing
> > 
> > gcc/doc/:
> > * invoke.texi (fvla-bounds): Document new flag.
> > 
> > gcc/testsuite:
> > * gcc.dg/vla-bounds-1.c: New test.
> > * gcc.dg/vla-bounds-assign-1.c: New test.
> > * gcc.dg/vla-bounds-assign-2.c: New test.
> > * gcc.dg/vla-bounds-assign-3.c: New test.
> > * gcc.dg/vla-bounds-assign-4.c: New test.
> > * gcc.dg/vla-bounds-func-1.c: New test.
> > * gcc.dg/vla-bounds-init-1.c: New test.
> > * gcc.dg/vla-bounds-init-2.c: New test.
> > * gcc.dg/vla-bounds-init-3.c: New test.
> > * gcc.dg/vla-bounds-init-4.c: New test.
> > * gcc.dg/vla-bounds-nest-1.c: New test.
> > * gcc.dg/vla-bounds-nest-2.c: New test.
> > * gcc.dg/vla-bounds-ret-1.c: New test.
> > * gcc.dg/vla-bounds-ret-2.c: New test.
> > ---
> > gcc/c-family/c.opt | 4 +
> > gcc/c/c-typeck.cc | 171 ++++++++++++++++++---
> > gcc/doc/invoke.texi | 9 ++
> > gcc/testsuite/gcc.dg/vla-bounds-1.c | 74 +++++++++
> > gcc/testsuite/gcc.dg/vla-bounds-assign-1.c | 32 ++++
> > gcc/testsuite/gcc.dg/vla-bounds-assign-2.c | 30 ++++
> > gcc/testsuite/gcc.dg/vla-bounds-assign-3.c | 43 ++++++
> > gcc/testsuite/gcc.dg/vla-bounds-assign-4.c | 26 ++++
> > gcc/testsuite/gcc.dg/vla-bounds-func-1.c | 54 +++++++
> > gcc/testsuite/gcc.dg/vla-bounds-init-1.c | 26 ++++
> > gcc/testsuite/gcc.dg/vla-bounds-init-2.c | 25 +++
> > gcc/testsuite/gcc.dg/vla-bounds-init-3.c | 25 +++
> > gcc/testsuite/gcc.dg/vla-bounds-init-4.c | 27 ++++
> > gcc/testsuite/gcc.dg/vla-bounds-nest-1.c | 31 ++++
> > gcc/testsuite/gcc.dg/vla-bounds-nest-2.c | 26 ++++
> > gcc/testsuite/gcc.dg/vla-bounds-ret-1.c | 27 ++++
> > gcc/testsuite/gcc.dg/vla-bounds-ret-2.c | 28 ++++
> > 17 files changed, 639 insertions(+), 19 deletions(-)
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-1.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-func-1.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-1.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-2.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-3.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-init-4.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
> > create mode 100644 gcc/testsuite/gcc.dg/vla-bounds-ret-2.c
> > 
> > diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> > index a52682d835c..786cd4ce52a 100644
> > --- a/gcc/c-family/c.opt
> > +++ b/gcc/c-family/c.opt
> > @@ -2324,6 +2324,10 @@ fvisibility-ms-compat
> > C++ ObjC++ Var(flag_visibility_ms_compat)
> > Changes visibility to match Microsoft Visual Studio by default.
> > +fvla-bounds
> > +C Var(flag_vla_bounds)
> > +Emit run-time consistency checks for variably-modified types.
> > +
> > fvtable-gc
> > C++ ObjC++ WarnRemoved
> > No longer supported.
> > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> > index 7e0f01ed22b..59940a29b53 100644
> > --- a/gcc/c/c-typeck.cc
> > +++ b/gcc/c/c-typeck.cc
> > @@ -97,11 +97,12 @@ static tree qualify_type (tree, tree);
> > struct comptypes_data;
> > static bool tagged_types_tu_compatible_p (const_tree, const_tree,
> > struct comptypes_data *);
> > -static bool comp_target_types (location_t, tree, tree);
> > static bool function_types_compatible_p (const_tree, const_tree,
> > struct comptypes_data *);
> > static bool type_lists_compatible_p (const_tree, const_tree,
> > struct comptypes_data *);
> > +static bool comp_target_types_instr (location_t, tree, tree,
> > + struct instrument_data **);
> > static int convert_arguments (location_t, vec<location_t>, tree,
> > vec<tree, va_gc> *, vec<tree, va_gc> *, tree,
> > tree);
> > @@ -109,6 +110,9 @@ static tree pointer_diff (location_t, tree, tree, tree *);
> > static tree convert_for_assignment (location_t, location_t, tree, tree, tree,
> > enum impl_conv, bool, tree, tree, int,
> > int = 0);
> > +static tree convert_for_assignment_instrument (location_t, location_t, tree, tree, tree,
> > + enum impl_conv, bool, tree, tree, int, int,
> > + struct instrument_data **);
> > static tree valid_compound_expr_initializer (tree, tree);
> > static void push_string (const char *);
> > static void push_member_name (tree);
> > @@ -1193,6 +1197,39 @@ comptypes_verify (tree type1, tree type2)
> > return true;
> > }
> > +
> > +/* Instrument assignment of variably modified types. */
> > +
> > +static tree
> > +c_instrument_vm_assign (location_t loc, tree a, tree b)
> > +{
> > + gcc_assert (flag_vla_bounds);
> > +
> > + gcc_assert (TREE_CODE (a) == ARRAY_TYPE);
> > + gcc_assert (TREE_CODE (b) == ARRAY_TYPE);
> > +
> > + tree as = TYPE_MAX_VALUE (TYPE_DOMAIN (a));
> > + tree bs = TYPE_MAX_VALUE (TYPE_DOMAIN (b));
> > +
> > + as = fold_build2 (PLUS_EXPR, sizetype, as, size_one_node);
> > + bs = fold_build2 (PLUS_EXPR, sizetype, bs, size_one_node);
> > +
> > + tree t = build2 (NE_EXPR, boolean_type_node, as, bs);
> > + tree tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
> > +
> > + return build3 (COND_EXPR, void_type_node, t, tt, void_node);
> > +}
> > +
> > +
> > +
> > +struct instrument_data {
> > +
> > + tree t1;
> > + tree t2;
> > + struct instrument_data *next;
> > +};
> > +
> > +
> > struct comptypes_data {
> > bool enum_and_int_p;
> > bool different_types_p;
> > @@ -1202,6 +1239,8 @@ struct comptypes_data {
> > bool equiv;
> > const struct tagged_tu_seen_cache* cache;
> > +
> > + struct instrument_data **instr_vec;
> > };
> > /* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
> > @@ -1241,11 +1280,14 @@ comptypes_same_p (tree type1, tree type2)
> > /* Like comptypes, but if it returns non-zero because enum and int are
> > compatible, it sets *ENUM_AND_INT_P to true. */
> > -int
> > -comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
> > +static int
> > +comptypes_check_enum_int_instr (tree type1, tree type2, bool *enum_and_int_p,
> > + struct instrument_data **instr_vec)
> > {
> > struct comptypes_data data = { };
> > + data.instr_vec = instr_vec;
> > bool ret = comptypes_internal (type1, type2, &data);
> > +
> > *enum_and_int_p = data.enum_and_int_p;
> > gcc_checking_assert (!ret || comptypes_verify (type1, type2));
> > @@ -1253,6 +1295,12 @@ comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
> > return ret ? (data.warning_needed ? 2 : 1) : 0;
> > }
> > +int
> > +comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
> > +{
> > + return comptypes_check_enum_int_instr (type1, type2, enum_and_int_p, NULL);
> > +}
> > +
> > /* Like comptypes, but if it returns nonzero for different types, it
> > sets *DIFFERENT_TYPES_P to true. */
> > @@ -1465,7 +1513,18 @@ comptypes_internal (const_tree type1, const_tree type2,
> > if (d1_variable != d2_variable)
> > data->different_types_p = true;
> > if (d1_variable || d2_variable)
> > - return true;
> > + {
> > + if (NULL != data->instr_vec)
> > + {
> > + struct instrument_data *id
> > + = (struct instrument_data *)xmalloc (sizeof *id);;
> > + id->t1 = TYPE_MAIN_VARIANT (t2);
> > + id->t2 = TYPE_MAIN_VARIANT (t1);
> > + id->next = *data->instr_vec;
> > + *data->instr_vec = id;
> > + }
> > + return true;
> > + }
> > if (d1_zero && d2_zero)
> > return true;
> > if (d1_zero || d2_zero
> > @@ -1501,7 +1560,8 @@ comptypes_internal (const_tree type1, const_tree type2,
> > subset of the other. */
> > static bool
> > -comp_target_types (location_t location, tree ttl, tree ttr)
> > +comp_target_types_instr (location_t location, tree ttl, tree ttr,
> > + struct instrument_data **instr_vec)
> > {
> > int val;
> > int val_ped;
> > @@ -1535,8 +1595,7 @@ comp_target_types (location_t location, tree ttl, tree ttr)
> > ? c_build_qualified_type (TYPE_MAIN_VARIANT (mvr), TYPE_QUAL_ATOMIC)
> > : TYPE_MAIN_VARIANT (mvr));
> > - enum_and_int_p = false;
> > - val = comptypes_check_enum_int (mvl, mvr, &enum_and_int_p);
> > + val = comptypes_check_enum_int_instr (mvl, mvr, &enum_and_int_p, instr_vec);
> > if (val == 1 && val_ped != 1)
> > pedwarn_c11 (location, OPT_Wpedantic, "invalid use of pointers to arrays with different qualifiers "
> > @@ -1551,6 +1610,13 @@ comp_target_types (location_t location, tree ttl, tree ttr)
> > return val;
> > }
> > +
> > +static int
> > +comp_target_types (location_t location, tree ttl, tree ttr)
> > +{
> > + return comp_target_types_instr (location, ttl, ttr, NULL);
> > +}
> > +
> > 
> > /* Subroutines of `comptypes'. */
> > @@ -1815,8 +1881,13 @@ function_types_compatible_p (const_tree f1, const_tree f2,
> > return val;
> > }
> > - /* Both types have argument lists: compare them and propagate results. */
> > + /* Both types have argument lists: compare them and propagate results.
> > + Turn off instrumentation for bounds as these are all arrays of
> > + unspecified size. */
> > + auto instr_vec_tmp = data->instr_vec;
> > + data->instr_vec = NULL;
> > val1 = type_lists_compatible_p (args1, args2, data);
> > + data->instr_vec = instr_vec_tmp;
> > return val1;
> > }
> > @@ -3857,10 +3928,11 @@ convert_argument (location_t ploc, tree function, tree fundecl,
> > if (excess_precision)
> > val = build1 (EXCESS_PRECISION_EXPR, valtype, val);
> > - tree parmval = convert_for_assignment (ploc, ploc, type,
> > - val, origtype, ic_argpass,
> > - npc, fundecl, function,
> > - parmnum + 1, warnopt);
> > + tree parmval = convert_for_assignment_instrument (ploc, ploc, type,
> > + val, origtype, ic_argpass,
> > + npc, fundecl, function,
> > + parmnum + 1, warnopt,
> > + NULL);
> > if (targetm.calls.promote_prototypes (fundecl ? TREE_TYPE (fundecl) : 0)
> > && INTEGRAL_TYPE_P (type)
> > @@ -3870,6 +3942,24 @@ convert_argument (location_t ploc, tree function, tree fundecl,
> > return parmval;
> > }
> > +
> > +/* Process all constraints for variably-modified types. */
> > +
> > +static tree
> > +process_vm_constraints (location_t location,
> > + struct instrument_data **instr_vec)
> > +{
> > + tree instr_expr = void_node;
> > +
> > + for (struct instrument_data* d = *instr_vec; d; d = d->next)
> > + {
> > + tree in = c_instrument_vm_assign (location, d->t1, d->t2);
> > + instr_expr = fold_build2 (COMPOUND_EXPR, void_type_node, in, instr_expr);
> > + }
> > + return instr_expr;
> > +}
> > +
> > +
> > /* Convert the argument expressions in the vector VALUES
> > to the types in the list TYPELIST.
> > @@ -7313,7 +7403,50 @@ static tree
> > convert_for_assignment (location_t location, location_t expr_loc, tree type,
> > tree rhs, tree origtype, enum impl_conv errtype,
> > bool null_pointer_constant, tree fundecl,
> > - tree function, int parmnum, int warnopt /* = 0 */)
> > + tree function, int parmnum, int warnopt)
> > +{
> > + struct instrument_data *instr_first = NULL;
> > + struct instrument_data **instr_vec = NULL;
> > +
> > + if (flag_vla_bounds && (ic_init_const != errtype))
> > + instr_vec = &instr_first;
> > +
> > + tree ret = convert_for_assignment_instrument (location, expr_loc, type,
> > + rhs, origtype, errtype,
> > + null_pointer_constant, fundecl,
> > + function, parmnum, warnopt,
> > + instr_vec);
> > + if (instr_vec)
> > + {
> > + if (ret && ret != error_mark_node && instr_first != NULL)
> > + {
> > + /* We have to make sure that the rhs is evaluated first,
> > + because we may use size expressions in it to check bounds. */
> > + tree instr_expr = process_vm_constraints (location, instr_vec);
> > + if (instr_expr != void_node)
> > + {
> > + ret = save_expr (ret);
> > + instr_expr = fold_build2 (COMPOUND_EXPR, void_type_node, ret, instr_expr);
> > + ret = fold_build2 (COMPOUND_EXPR, TREE_TYPE (ret), instr_expr, ret);
> > + }
> > + }
> > + while (instr_first)
> > + {
> > + struct instrument_data *next = instr_first->next;
> > + free (instr_first);
> > + instr_first = next;
> > + }
> > + instr_vec = NULL;
> > + }
> > + return ret;
> > +}
> > +
> > +static tree
> > +convert_for_assignment_instrument (location_t location, location_t expr_loc, tree type,
> > + tree rhs, tree origtype, enum impl_conv errtype,
> > + bool null_pointer_constant, tree fundecl,
> > + tree function, int parmnum, int warnopt,
> > + struct instrument_data **instr_vec)
> > {
> > enum tree_code codel = TREE_CODE (type);
> > tree orig_rhs = rhs;
> > @@ -7557,11 +7690,11 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
> > rhs = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (rhs)), rhs);
> > SET_EXPR_LOCATION (rhs, location);
> > - rhs = convert_for_assignment (location, expr_loc,
> > - build_pointer_type (TREE_TYPE (type)),
> > - rhs, origtype, errtype,
> > - null_pointer_constant, fundecl, function,
> > - parmnum, warnopt);
> > + rhs = convert_for_assignment_instrument (location, expr_loc,
> > + build_pointer_type (TREE_TYPE (type)),
> > + rhs, origtype, errtype,
> > + null_pointer_constant, fundecl, function,
> > + parmnum, warnopt, instr_vec);
> > if (rhs == error_mark_node)
> > return error_mark_node;
> > @@ -7955,7 +8088,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
> > Meanwhile, the lhs target must have all the qualifiers of the rhs. */
> > if ((VOID_TYPE_P (ttl) && !TYPE_ATOMIC (ttl))
> > > > (VOID_TYPE_P (ttr) && !TYPE_ATOMIC (ttr))
> > - || (target_cmp = comp_target_types (location, type, rhstype))
> > + || (target_cmp = comp_target_types_instr (location, type, rhstype, instr_vec))
> > > > is_opaque_pointer
> > > > ((c_common_unsigned_type (mvl)
> > == c_common_unsigned_type (mvr))
> > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > index 403ea9da1ab..8d6d68e4f27 100644
> > --- a/gcc/doc/invoke.texi
> > +++ b/gcc/doc/invoke.texi
> > @@ -10698,6 +10698,7 @@ void g (int n)
> > @option{-Warray-parameter} option triggers warnings for similar problems
> > involving ordinary array arguments.
> > +
> > @opindex Wvolatile-register-var
> > @opindex Wno-volatile-register-var
> > @item -Wvolatile-register-var
> > @@ -20831,6 +20832,14 @@ computing CRC32).
> > The @var{string} should be different for every file you compile.
> > +@opindex fvla-bounds
> > +@item -fvla-bounds
> > +This option is only available when compiling C code. If activated,
> > +additional code is emitted that verifies at run time for assignments
> > +involving variably-modified types that corresponding size expressions
> > +evaluate to the same value.
> > +
> > +
> > @opindex save-temps
> > @item -save-temps
> > Store the usual ``temporary'' intermediate files permanently; name them
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-1.c b/gcc/testsuite/gcc.dg/vla-bounds-1.c
> > new file mode 100644
> > index 00000000000..093f5e7ba25
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-1.c
> > @@ -0,0 +1,74 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +// test various valid initializations and assignments
> > +
> > +int main()
> > +{
> > + int m = 4, n = 3;
> > +
> > + int u = 4;
> > + int v = 3;
> > +
> > + /* initialization */
> > +
> > + char a[4];
> > + char (*pa)[u] = &a;
> > + char (*qa)[v];
> > +
> > + char b[u];
> > + const char (*pb)[u] = &b;
> > + char (*qb)[v];
> > +
> > + char c[4][3];
> > + char (*pc0)[u][v] = &c;
> > + char (*qc0)[v][u];
> > +
> > + char (*pc1)[u][3] = &c;
> > + char (*qc1)[v][3];
> > +
> > + char (*pc2)[4][v] = &c;
> > + char (*qc2)[4][u];
> > +
> > + char (*pc3)[][v] = &c; 
> > +
> > + char d[u][v];
> > + char (*pd0)[4][3] = &d;
> > + char (*qd0)[3][4];
> > +
> > + char (*pd1)[u][3] = &d; 
> > + char (*qd1)[v][4];
> > +
> > + char (*pd2)[4][v] = &d;
> > + char (*qd2)[3][u];
> > +
> > + char (*pd3)[u][v] = &d;
> > + char (*qd3)[v][u];
> > +
> > + char e[4][v];
> > + char (*pe0)[4][3] = &e;
> > + char (*qe0)[4][4];
> > +
> > + char f[u][3];
> > + char (*pf)[4][3] = &f;
> > + char (*qf)[3][3];
> > +
> > + char (*g[u])[v];
> > + char (*(*pg)[u])[v] = &g;
> > +
> > + /* assignment */
> > +
> > + pa = &a;
> > + pb = &b;
> > + pc0 = &c;
> > + pc1 = &c;
> > + pc2 = &c;
> > + pd0 = &d;
> > + pd1 = &d;
> > + pd2 = &d;
> > + pd3 = &d;
> > + pe0 = &e;
> > + pf = &f;
> > +
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
> > new file mode 100644
> > index 00000000000..ffef7e502dd
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
> > @@ -0,0 +1,32 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test valid and invalid assignments involving pointers to arrays of
> > + variable length. */
> > +
> > +static void handler(int) { exit(0); }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + int u = 4;
> > + int v = 3;
> > +
> > + char a[4];
> > + char (*pa)[u];
> > + char (*qa)[v];
> > +
> > + char b[u];
> > + const char (*pb)[u];
> > +
> > + pa = &a;
> > + pb = &b;
> > + qa = &a; // 3 != 4
> > + abort();
> > +
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
> > new file mode 100644
> > index 00000000000..cc445dea459
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
> > @@ -0,0 +1,30 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test valid and invalid assignment of a pointer to a 2D array to pointers
> > + to 2D arrays of variable length. */
> > +
> > +static void handler(int) { exit(0); }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + int u = 4;
> > + int v = 3;
> > +
> > + char c[4][3];
> > + char (*pc0)[u][v];
> > + char (*qc0)[v][u];
> > + char (*pc1)[u][3];
> > + char (*pc2)[4][v];
> > +
> > + pc0 = &c;
> > + pc1 = &c;
> > + pc2 = &c;
> > + qc0 = &c; // 3 != 4, 4 != 3
> > + abort();
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
> > new file mode 100644
> > index 00000000000..068e9a57e65
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
> > @@ -0,0 +1,43 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test valid and invalid assignment of a pointer to a 2D VLA to pointers
> > + to 2D arrays of variable length. */
> > +
> > +static void handler(int) { exit(0); }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + int u = 4;
> > + int v = 3;
> > +
> > + char d[u][v];
> > + char (*pd0)[4][3];
> > + char (*pd1)[u][3]; 
> > + char (*pd2)[4][v];
> > + char (*pd3)[u][v];
> > +
> > + char e[4][v];
> > + char (*pe0)[4][3];
> > +
> > + char f[u][3];
> > + char (*pf)[4][3];
> > + char (*qf)[3][3];
> > +
> > + /* assignment */
> > +
> > + pd0 = &d;
> > + pd1 = &d;
> > + pd2 = &d;
> > + pd3 = &d;
> > + pe0 = &e;
> > +
> > + pf = &f;
> > + qf = &f; // 3 != 4
> > + abort();
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
> > new file mode 100644
> > index 00000000000..b274e237eba
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
> > @@ -0,0 +1,26 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test assignment of pointer to function returning 2D array to pointer to
> > + function returning a pointer to a variable length array. */
> > +
> > +static void handler(int) { exit(0); }
> > +
> > +int m, n;
> > +static char (*z0(void))[5][5] { char (*p)[m][n] = 0; return p; }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + m = 4, n = 3;
> > +
> > + int u = 4;
> > + int v = 3;
> > +
> > + char (*(*p)(void))[u][v] = &z0; // 4 != 5, 3 != 5
> > + abort();
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-func-1.c b/gcc/testsuite/gcc.dg/vla-bounds-func-1.c
> > new file mode 100644
> > index 00000000000..8256999fc50
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-func-1.c
> > @@ -0,0 +1,54 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +// make sure we do not ICE on any of these
> > +
> > +const char* name = "hallo";
> > +
> > +typedef void (*ht)(int n, int m, char x[n][m]);
> > +void e(ht) { }
> > +
> > +int n, m;
> > +static void f0(char a[n][m]) { }
> > +static void f1(int u, int v, char a[u][v]) { }
> > +static void f2(int u, int v, char a[u][v]) { }
> > +
> > +void f(void)
> > +{
> > + int x = 1;
> > + int (*m)[x] = 0;
> > + m = ({ long* d2; (int (*)[d2[0]])(0); });
> > +
> > + /* function pointer assignments */
> > +
> > + void (*gp)(char x[4][3]) = f0;
> > + void (*hp)(int n, int m, char x[n][m]) = f1;
> > + ht hp2 = f1;
> > + e(f1);
> > +
> > + /* composite type */
> > +
> > + int u = 3; int v = 4;
> > + char a[u][v];
> > + (1 ? f1 : f2)(u, v, a);
> > +}
> > +
> > +/* size expression in parameter */
> > +
> > +extern void a(long N, char (*a)[N]);
> > +
> > +static void b(void)
> > +{
> > + a(1, ({ int d = 0; (char (*)[d])0; }) );
> > +}
> > +
> > +/* composite type */
> > +
> > +int c(int u, char (*a)[u]);
> > +int c(int u, char (*a)[u]) { }
> > +
> > +int d(void)
> > +{
> > + char a[3];
> > + c(3, &a);
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-1.c b/gcc/testsuite/gcc.dg/vla-bounds-init-1.c
> > new file mode 100644
> > index 00000000000..3e608ba0c34
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-init-1.c
> > @@ -0,0 +1,26 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test valid and invalid initialization of pointers to arrays of
> > + variable length. */
> > +
> > +static void handler(int) { exit(0); }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + int u = 4;
> > + int v = 3;
> > +
> > + char b[u];
> > + const char (*pb)[u] = &b;
> > +
> > + char a[4];
> > + char (*pa)[u] = &a;
> > + char (*qa)[v] = &a; // 3 != 4
> > + abort();
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-2.c b/gcc/testsuite/gcc.dg/vla-bounds-init-2.c
> > new file mode 100644
> > index 00000000000..d26683e6c97
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-init-2.c
> > @@ -0,0 +1,25 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test valid and invalid initialization of pointers to 2D arrays of
> > + variable length with pointer to 2D regular array. */
> > +
> > +static void handler(int) { exit(0); }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + int u = 4;
> > + int v = 3;
> > +
> > + char c[4][3];
> > + char (*pc1)[u][3] = &c;
> > + char (*pc2)[4][v] = &c;
> > + char (*pc0)[u][v] = &c;
> > + char (*qc0)[v][u] = &c; // 3 != 4, 4 != 3
> > + abort();
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-3.c b/gcc/testsuite/gcc.dg/vla-bounds-init-3.c
> > new file mode 100644
> > index 00000000000..7ab706af62e
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-init-3.c
> > @@ -0,0 +1,25 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test valid and invalid initialization of pointers to 2D arrays of
> > + variable length with incomplete first dimension. */
> > +
> > +static void handler(int) { exit(0); }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + int u = 4;
> > + int v = 3;
> > +
> > + /* initialization */
> > +
> > + char c[4][3];
> > + char (*pc3)[][v] = &c; 
> > + char (*qc3)[][u] = &c; // 4 != 3
> > + abort();
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-4.c b/gcc/testsuite/gcc.dg/vla-bounds-init-4.c
> > new file mode 100644
> > index 00000000000..f20de91c156
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-init-4.c
> > @@ -0,0 +1,27 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test valid and invalid initialization of pointers to 2D arrays of
> > + variable length with pointer to 2D VLA. */
> > +
> > +static void handler(int) { exit(0); }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + int u = 4;
> > + int v = 3;
> > +
> > + /* initialization */
> > + char d[u][v];
> > + char (*pd0)[4][3] = &d;
> > + char (*pd1)[u][3] = &d; 
> > + char (*pd2)[4][v] = &d;
> > + char (*pd3)[u][v] = &d;
> > + char (*qd0)[3][4] = &d; // 3 != 4, 4 != 3
> > + abort();
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c b/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
> > new file mode 100644
> > index 00000000000..7f33bc95e29
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
> > @@ -0,0 +1,31 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +/* { dg-require-effective-target trampolines } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test valid and invalid assignment of a nested function mit variably
> > + modified return type. */
> > +
> > +void handler(int) { exit(0); }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + int n = 3;
> > + char b[4];
> > + char (*f())[++n] { return &b; }
> > +
> > + if (4 != sizeof(*f()))
> > + abort();
> > +
> > + char (*(*p)())[n++] = &f;
> > +
> > + if (4 != sizeof(*(*p)()))
> > + abort();
> > +
> > + char (*(*p2)())[++n] = &f;
> > + abort();
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c b/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
> > new file mode 100644
> > index 00000000000..13f77d4e185
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
> > @@ -0,0 +1,26 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +/* { dg-require-effective-target trampolines } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test valid and invalid assignment of regular function pointer
> > + return array to pointer to function with variably modified
> > + return type. */
> > +
> > +void handler(int) { exit(0); }
> > +
> > +static char bb[4][4];
> > +static char (*g())[4][4] { return &bb; }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + int n = 4;
> > +
> > + char (*(*p)())[n][4] = &g;
> > + char (*(*q)())[++n][4] = &g;;
> > + abort();
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c b/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
> > new file mode 100644
> > index 00000000000..43919927ab8
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
> > @@ -0,0 +1,27 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test returning a pointer to a 2D variable length array in
> > + a function returning a pointer to a regular 2D array. */
> > +
> > +static void handler(int) { exit(0); }
> > +
> > +int m, n;
> > +
> > +static char (*z0(void))[5][5] { char (*p)[m][n] = 0; return p; }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + m = 5, n = 5;
> > + z0();
> > +
> > + m = 4, n = 3;
> > + z0(); // 5 != 3, 5, != 3
> > + abort();
> > +}
> > +
> > diff --git a/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c b/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c
> > new file mode 100644
> > index 00000000000..af8475e9a5f
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c
> > @@ -0,0 +1,28 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-fvla-bounds" } */
> > +
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +
> > +/* Test returning a pointer to a 2D variable length array with
> > + a fist constant dimension in a function returning a pointer
> > + to a regular 2D array. */
> > +
> > +static void handler(int) { exit(0); }
> > +
> > +int n, m;
> > +
> > +static char (*z1(void))[5][5] { char (*p)[5][n] = 0; return p; }
> > +
> > +int main()
> > +{
> > + signal(SIGILL, handler);
> > +
> > + m = 5, n = 5;
> > + z1();
> > +
> > + m = 4, n = 3;
> > + z1(); // 4 != 3
> > + abort();
> > +}
> > +
> > -- 
> > 2.39.2
> > 
> > 
> > 
>
  

Patch

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index a52682d835c..786cd4ce52a 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -2324,6 +2324,10 @@  fvisibility-ms-compat
 C++ ObjC++ Var(flag_visibility_ms_compat)
 Changes visibility to match Microsoft Visual Studio by default.
+fvla-bounds
+C Var(flag_vla_bounds)
+Emit run-time consistency checks for variably-modified types.
+
 fvtable-gc
 C++ ObjC++ WarnRemoved
 No longer supported.
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 7e0f01ed22b..59940a29b53 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -97,11 +97,12 @@  static tree qualify_type (tree, tree);
 struct comptypes_data;
 static bool tagged_types_tu_compatible_p (const_tree, const_tree,
 struct comptypes_data *);
-static bool comp_target_types (location_t, tree, tree);
 static bool function_types_compatible_p (const_tree, const_tree,
 struct comptypes_data *);
 static bool type_lists_compatible_p (const_tree, const_tree,
 struct comptypes_data *);
+static bool comp_target_types_instr (location_t, tree, tree,
+ struct instrument_data **);
 static int convert_arguments (location_t, vec<location_t>, tree,
 vec<tree, va_gc> *, vec<tree, va_gc> *, tree,
 tree);
@@ -109,6 +110,9 @@  static tree pointer_diff (location_t, tree, tree, tree *);
 static tree convert_for_assignment (location_t, location_t, tree, tree, tree,
 enum impl_conv, bool, tree, tree, int,
 int = 0);
+static tree convert_for_assignment_instrument (location_t, location_t, tree, tree, tree,
+ enum impl_conv, bool, tree, tree, int, int,
+ struct instrument_data **);
 static tree valid_compound_expr_initializer (tree, tree);
 static void push_string (const char *);
 static void push_member_name (tree);
@@ -1193,6 +1197,39 @@  comptypes_verify (tree type1, tree type2)
 return true;
 }
+
+/* Instrument assignment of variably modified types. */
+
+static tree
+c_instrument_vm_assign (location_t loc, tree a, tree b)
+{
+ gcc_assert (flag_vla_bounds);
+
+ gcc_assert (TREE_CODE (a) == ARRAY_TYPE);
+ gcc_assert (TREE_CODE (b) == ARRAY_TYPE);
+
+ tree as = TYPE_MAX_VALUE (TYPE_DOMAIN (a));
+ tree bs = TYPE_MAX_VALUE (TYPE_DOMAIN (b));
+
+ as = fold_build2 (PLUS_EXPR, sizetype, as, size_one_node);
+ bs = fold_build2 (PLUS_EXPR, sizetype, bs, size_one_node);
+
+ tree t = build2 (NE_EXPR, boolean_type_node, as, bs);
+ tree tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+
+ return build3 (COND_EXPR, void_type_node, t, tt, void_node);
+}
+
+
+
+struct instrument_data {
+
+ tree t1;
+ tree t2;
+ struct instrument_data *next;
+};
+
+
 struct comptypes_data {
 bool enum_and_int_p;
 bool different_types_p;
@@ -1202,6 +1239,8 @@  struct comptypes_data {
 bool equiv;
 const struct tagged_tu_seen_cache* cache;
+
+ struct instrument_data **instr_vec;
 };
 /* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
@@ -1241,11 +1280,14 @@  comptypes_same_p (tree type1, tree type2)
 /* Like comptypes, but if it returns non-zero because enum and int are
 compatible, it sets *ENUM_AND_INT_P to true. */
-int
-comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
+static int
+comptypes_check_enum_int_instr (tree type1, tree type2, bool *enum_and_int_p,
+ struct instrument_data **instr_vec)
 {
 struct comptypes_data data = { };
+ data.instr_vec = instr_vec;
 bool ret = comptypes_internal (type1, type2, &data);
+
 *enum_and_int_p = data.enum_and_int_p;
 gcc_checking_assert (!ret || comptypes_verify (type1, type2));
@@ -1253,6 +1295,12 @@  comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
 return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
+int
+comptypes_check_enum_int (tree type1, tree type2, bool *enum_and_int_p)
+{
+ return comptypes_check_enum_int_instr (type1, type2, enum_and_int_p, NULL);
+}
+
 /* Like comptypes, but if it returns nonzero for different types, it
 sets *DIFFERENT_TYPES_P to true. */
@@ -1465,7 +1513,18 @@  comptypes_internal (const_tree type1, const_tree type2,
 if (d1_variable != d2_variable)
 data->different_types_p = true;
 if (d1_variable || d2_variable)
- return true;
+ {
+ if (NULL != data->instr_vec)
+ {
+ struct instrument_data *id
+ = (struct instrument_data *)xmalloc (sizeof *id);;
+ id->t1 = TYPE_MAIN_VARIANT (t2);
+ id->t2 = TYPE_MAIN_VARIANT (t1);
+ id->next = *data->instr_vec;
+ *data->instr_vec = id;
+ }
+ return true;
+ }
 if (d1_zero && d2_zero)
 return true;
 if (d1_zero || d2_zero
@@ -1501,7 +1560,8 @@  comptypes_internal (const_tree type1, const_tree type2,
 subset of the other. */
 static bool
-comp_target_types (location_t location, tree ttl, tree ttr)
+comp_target_types_instr (location_t location, tree ttl, tree ttr,
+ struct instrument_data **instr_vec)
 {
 int val;
 int val_ped;
@@ -1535,8 +1595,7 @@  comp_target_types (location_t location, tree ttl, tree ttr)
 ? c_build_qualified_type (TYPE_MAIN_VARIANT (mvr), TYPE_QUAL_ATOMIC)
 : TYPE_MAIN_VARIANT (mvr));
- enum_and_int_p = false;
- val = comptypes_check_enum_int (mvl, mvr, &enum_and_int_p);
+ val = comptypes_check_enum_int_instr (mvl, mvr, &enum_and_int_p, instr_vec);
 if (val == 1 && val_ped != 1)
 pedwarn_c11 (location, OPT_Wpedantic, "invalid use of pointers to arrays with different qualifiers "
@@ -1551,6 +1610,13 @@  comp_target_types (location_t location, tree ttl, tree ttr)
 return val;
 }
+
+static int
+comp_target_types (location_t location, tree ttl, tree ttr)
+{
+ return comp_target_types_instr (location, ttl, ttr, NULL);
+}
+
 
 /* Subroutines of `comptypes'. */
@@ -1815,8 +1881,13 @@  function_types_compatible_p (const_tree f1, const_tree f2,
 return val;
 }
- /* Both types have argument lists: compare them and propagate results. */
+ /* Both types have argument lists: compare them and propagate results.
+ Turn off instrumentation for bounds as these are all arrays of
+ unspecified size. */
+ auto instr_vec_tmp = data->instr_vec;
+ data->instr_vec = NULL;
 val1 = type_lists_compatible_p (args1, args2, data);
+ data->instr_vec = instr_vec_tmp;
 return val1;
 }
@@ -3857,10 +3928,11 @@  convert_argument (location_t ploc, tree function, tree fundecl,
 if (excess_precision)
 val = build1 (EXCESS_PRECISION_EXPR, valtype, val);
- tree parmval = convert_for_assignment (ploc, ploc, type,
- val, origtype, ic_argpass,
- npc, fundecl, function,
- parmnum + 1, warnopt);
+ tree parmval = convert_for_assignment_instrument (ploc, ploc, type,
+ val, origtype, ic_argpass,
+ npc, fundecl, function,
+ parmnum + 1, warnopt,
+ NULL);
 if (targetm.calls.promote_prototypes (fundecl ? TREE_TYPE (fundecl) : 0)
 && INTEGRAL_TYPE_P (type)
@@ -3870,6 +3942,24 @@  convert_argument (location_t ploc, tree function, tree fundecl,
 return parmval;
 }
+
+/* Process all constraints for variably-modified types. */
+
+static tree
+process_vm_constraints (location_t location,
+ struct instrument_data **instr_vec)
+{
+ tree instr_expr = void_node;
+
+ for (struct instrument_data* d = *instr_vec; d; d = d->next)
+ {
+ tree in = c_instrument_vm_assign (location, d->t1, d->t2);
+ instr_expr = fold_build2 (COMPOUND_EXPR, void_type_node, in, instr_expr);
+ }
+ return instr_expr;
+}
+
+
 /* Convert the argument expressions in the vector VALUES
 to the types in the list TYPELIST.
@@ -7313,7 +7403,50 @@  static tree
 convert_for_assignment (location_t location, location_t expr_loc, tree type,
 tree rhs, tree origtype, enum impl_conv errtype,
 bool null_pointer_constant, tree fundecl,
- tree function, int parmnum, int warnopt /* = 0 */)
+ tree function, int parmnum, int warnopt)
+{
+ struct instrument_data *instr_first = NULL;
+ struct instrument_data **instr_vec = NULL;
+
+ if (flag_vla_bounds && (ic_init_const != errtype))
+ instr_vec = &instr_first;
+
+ tree ret = convert_for_assignment_instrument (location, expr_loc, type,
+ rhs, origtype, errtype,
+ null_pointer_constant, fundecl,
+ function, parmnum, warnopt,
+ instr_vec);
+ if (instr_vec)
+ {
+ if (ret && ret != error_mark_node && instr_first != NULL)
+ {
+ /* We have to make sure that the rhs is evaluated first,
+ because we may use size expressions in it to check bounds. */
+ tree instr_expr = process_vm_constraints (location, instr_vec);
+ if (instr_expr != void_node)
+ {
+ ret = save_expr (ret);
+ instr_expr = fold_build2 (COMPOUND_EXPR, void_type_node, ret, instr_expr);
+ ret = fold_build2 (COMPOUND_EXPR, TREE_TYPE (ret), instr_expr, ret);
+ }
+ }
+ while (instr_first)
+ {
+ struct instrument_data *next = instr_first->next;
+ free (instr_first);
+ instr_first = next;
+ }
+ instr_vec = NULL;
+ }
+ return ret;
+}
+
+static tree
+convert_for_assignment_instrument (location_t location, location_t expr_loc, tree type,
+ tree rhs, tree origtype, enum impl_conv errtype,
+ bool null_pointer_constant, tree fundecl,
+ tree function, int parmnum, int warnopt,
+ struct instrument_data **instr_vec)
 {
 enum tree_code codel = TREE_CODE (type);
 tree orig_rhs = rhs;
@@ -7557,11 +7690,11 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 rhs = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (rhs)), rhs);
 SET_EXPR_LOCATION (rhs, location);
- rhs = convert_for_assignment (location, expr_loc,
- build_pointer_type (TREE_TYPE (type)),
- rhs, origtype, errtype,
- null_pointer_constant, fundecl, function,
- parmnum, warnopt);
+ rhs = convert_for_assignment_instrument (location, expr_loc,
+ build_pointer_type (TREE_TYPE (type)),
+ rhs, origtype, errtype,
+ null_pointer_constant, fundecl, function,
+ parmnum, warnopt, instr_vec);
 if (rhs == error_mark_node)
 return error_mark_node;
@@ -7955,7 +8088,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 Meanwhile, the lhs target must have all the qualifiers of the rhs. */
 if ((VOID_TYPE_P (ttl) && !TYPE_ATOMIC (ttl))
 || (VOID_TYPE_P (ttr) && !TYPE_ATOMIC (ttr))
- || (target_cmp = comp_target_types (location, type, rhstype))
+ || (target_cmp = comp_target_types_instr (location, type, rhstype, instr_vec))
 || is_opaque_pointer
 || ((c_common_unsigned_type (mvl)
 == c_common_unsigned_type (mvr))
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 403ea9da1ab..8d6d68e4f27 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10698,6 +10698,7 @@  void g (int n)
 @option{-Warray-parameter} option triggers warnings for similar problems
 involving ordinary array arguments.
+
 @opindex Wvolatile-register-var
 @opindex Wno-volatile-register-var
 @item -Wvolatile-register-var
@@ -20831,6 +20832,14 @@  computing CRC32).
 The @var{string} should be different for every file you compile.
+@opindex fvla-bounds
+@item -fvla-bounds
+This option is only available when compiling C code. If activated,
+additional code is emitted that verifies at run time for assignments
+involving variably-modified types that corresponding size expressions
+evaluate to the same value.
+
+
 @opindex save-temps
 @item -save-temps
 Store the usual ``temporary'' intermediate files permanently; name them
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-1.c b/gcc/testsuite/gcc.dg/vla-bounds-1.c
new file mode 100644
index 00000000000..093f5e7ba25
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-1.c
@@ -0,0 +1,74 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+// test various valid initializations and assignments
+
+int main()
+{
+ int m = 4, n = 3;
+
+ int u = 4;
+ int v = 3;
+
+ /* initialization */
+
+ char a[4];
+ char (*pa)[u] = &a;
+ char (*qa)[v];
+
+ char b[u];
+ const char (*pb)[u] = &b;
+ char (*qb)[v];
+
+ char c[4][3];
+ char (*pc0)[u][v] = &c;
+ char (*qc0)[v][u];
+
+ char (*pc1)[u][3] = &c;
+ char (*qc1)[v][3];
+
+ char (*pc2)[4][v] = &c;
+ char (*qc2)[4][u];
+
+ char (*pc3)[][v] = &c; 
+
+ char d[u][v];
+ char (*pd0)[4][3] = &d;
+ char (*qd0)[3][4];
+
+ char (*pd1)[u][3] = &d; 
+ char (*qd1)[v][4];
+
+ char (*pd2)[4][v] = &d;
+ char (*qd2)[3][u];
+
+ char (*pd3)[u][v] = &d;
+ char (*qd3)[v][u];
+
+ char e[4][v];
+ char (*pe0)[4][3] = &e;
+ char (*qe0)[4][4];
+
+ char f[u][3];
+ char (*pf)[4][3] = &f;
+ char (*qf)[3][3];
+
+ char (*g[u])[v];
+ char (*(*pg)[u])[v] = &g;
+
+ /* assignment */
+
+ pa = &a;
+ pb = &b;
+ pc0 = &c;
+ pc1 = &c;
+ pc2 = &c;
+ pd0 = &d;
+ pd1 = &d;
+ pd2 = &d;
+ pd3 = &d;
+ pe0 = &e;
+ pf = &f;
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
new file mode 100644
index 00000000000..ffef7e502dd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-1.c
@@ -0,0 +1,32 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid assignments involving pointers to arrays of
+ variable length. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ char a[4];
+ char (*pa)[u];
+ char (*qa)[v];
+
+ char b[u];
+ const char (*pb)[u];
+
+ pa = &a;
+ pb = &b;
+ qa = &a; // 3 != 4
+ abort();
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
new file mode 100644
index 00000000000..cc445dea459
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-2.c
@@ -0,0 +1,30 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid assignment of a pointer to a 2D array to pointers
+ to 2D arrays of variable length. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ char c[4][3];
+ char (*pc0)[u][v];
+ char (*qc0)[v][u];
+ char (*pc1)[u][3];
+ char (*pc2)[4][v];
+
+ pc0 = &c;
+ pc1 = &c;
+ pc2 = &c;
+ qc0 = &c; // 3 != 4, 4 != 3
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
new file mode 100644
index 00000000000..068e9a57e65
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-3.c
@@ -0,0 +1,43 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid assignment of a pointer to a 2D VLA to pointers
+ to 2D arrays of variable length. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ char d[u][v];
+ char (*pd0)[4][3];
+ char (*pd1)[u][3]; 
+ char (*pd2)[4][v];
+ char (*pd3)[u][v];
+
+ char e[4][v];
+ char (*pe0)[4][3];
+
+ char f[u][3];
+ char (*pf)[4][3];
+ char (*qf)[3][3];
+
+ /* assignment */
+
+ pd0 = &d;
+ pd1 = &d;
+ pd2 = &d;
+ pd3 = &d;
+ pe0 = &e;
+
+ pf = &f;
+ qf = &f; // 3 != 4
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c b/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
new file mode 100644
index 00000000000..b274e237eba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-assign-4.c
@@ -0,0 +1,26 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test assignment of pointer to function returning 2D array to pointer to
+ function returning a pointer to a variable length array. */
+
+static void handler(int) { exit(0); }
+
+int m, n;
+static char (*z0(void))[5][5] { char (*p)[m][n] = 0; return p; }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ m = 4, n = 3;
+
+ int u = 4;
+ int v = 3;
+
+ char (*(*p)(void))[u][v] = &z0; // 4 != 5, 3 != 5
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-func-1.c b/gcc/testsuite/gcc.dg/vla-bounds-func-1.c
new file mode 100644
index 00000000000..8256999fc50
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-func-1.c
@@ -0,0 +1,54 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fvla-bounds" } */
+
+// make sure we do not ICE on any of these
+
+const char* name = "hallo";
+
+typedef void (*ht)(int n, int m, char x[n][m]);
+void e(ht) { }
+
+int n, m;
+static void f0(char a[n][m]) { }
+static void f1(int u, int v, char a[u][v]) { }
+static void f2(int u, int v, char a[u][v]) { }
+
+void f(void)
+{
+ int x = 1;
+ int (*m)[x] = 0;
+ m = ({ long* d2; (int (*)[d2[0]])(0); });
+
+ /* function pointer assignments */
+
+ void (*gp)(char x[4][3]) = f0;
+ void (*hp)(int n, int m, char x[n][m]) = f1;
+ ht hp2 = f1;
+ e(f1);
+
+ /* composite type */
+
+ int u = 3; int v = 4;
+ char a[u][v];
+ (1 ? f1 : f2)(u, v, a);
+}
+
+/* size expression in parameter */
+
+extern void a(long N, char (*a)[N]);
+
+static void b(void)
+{
+ a(1, ({ int d = 0; (char (*)[d])0; }) );
+}
+
+/* composite type */
+
+int c(int u, char (*a)[u]);
+int c(int u, char (*a)[u]) { }
+
+int d(void)
+{
+ char a[3];
+ c(3, &a);
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-1.c b/gcc/testsuite/gcc.dg/vla-bounds-init-1.c
new file mode 100644
index 00000000000..3e608ba0c34
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-init-1.c
@@ -0,0 +1,26 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid initialization of pointers to arrays of
+ variable length. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ char b[u];
+ const char (*pb)[u] = &b;
+
+ char a[4];
+ char (*pa)[u] = &a;
+ char (*qa)[v] = &a; // 3 != 4
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-2.c b/gcc/testsuite/gcc.dg/vla-bounds-init-2.c
new file mode 100644
index 00000000000..d26683e6c97
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-init-2.c
@@ -0,0 +1,25 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid initialization of pointers to 2D arrays of
+ variable length with pointer to 2D regular array. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ char c[4][3];
+ char (*pc1)[u][3] = &c;
+ char (*pc2)[4][v] = &c;
+ char (*pc0)[u][v] = &c;
+ char (*qc0)[v][u] = &c; // 3 != 4, 4 != 3
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-3.c b/gcc/testsuite/gcc.dg/vla-bounds-init-3.c
new file mode 100644
index 00000000000..7ab706af62e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-init-3.c
@@ -0,0 +1,25 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid initialization of pointers to 2D arrays of
+ variable length with incomplete first dimension. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ /* initialization */
+
+ char c[4][3];
+ char (*pc3)[][v] = &c; 
+ char (*qc3)[][u] = &c; // 4 != 3
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-init-4.c b/gcc/testsuite/gcc.dg/vla-bounds-init-4.c
new file mode 100644
index 00000000000..f20de91c156
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-init-4.c
@@ -0,0 +1,27 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid initialization of pointers to 2D arrays of
+ variable length with pointer to 2D VLA. */
+
+static void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int u = 4;
+ int v = 3;
+
+ /* initialization */
+ char d[u][v];
+ char (*pd0)[4][3] = &d;
+ char (*pd1)[u][3] = &d; 
+ char (*pd2)[4][v] = &d;
+ char (*pd3)[u][v] = &d;
+ char (*qd0)[3][4] = &d; // 3 != 4, 4 != 3
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c b/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
new file mode 100644
index 00000000000..7f33bc95e29
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-nest-1.c
@@ -0,0 +1,31 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+/* { dg-require-effective-target trampolines } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid assignment of a nested function mit variably
+ modified return type. */
+
+void handler(int) { exit(0); }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int n = 3;
+ char b[4];
+ char (*f())[++n] { return &b; }
+
+ if (4 != sizeof(*f()))
+ abort();
+
+ char (*(*p)())[n++] = &f;
+
+ if (4 != sizeof(*(*p)()))
+ abort();
+
+ char (*(*p2)())[++n] = &f;
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c b/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
new file mode 100644
index 00000000000..13f77d4e185
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-nest-2.c
@@ -0,0 +1,26 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+/* { dg-require-effective-target trampolines } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test valid and invalid assignment of regular function pointer
+ return array to pointer to function with variably modified
+ return type. */
+
+void handler(int) { exit(0); }
+
+static char bb[4][4];
+static char (*g())[4][4] { return &bb; }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ int n = 4;
+
+ char (*(*p)())[n][4] = &g;
+ char (*(*q)())[++n][4] = &g;;
+ abort();
+}
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c b/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
new file mode 100644
index 00000000000..43919927ab8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-ret-1.c
@@ -0,0 +1,27 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test returning a pointer to a 2D variable length array in
+ a function returning a pointer to a regular 2D array. */
+
+static void handler(int) { exit(0); }
+
+int m, n;
+
+static char (*z0(void))[5][5] { char (*p)[m][n] = 0; return p; }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ m = 5, n = 5;
+ z0();
+
+ m = 4, n = 3;
+ z0(); // 5 != 3, 5, != 3
+ abort();
+}
+
diff --git a/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c b/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c
new file mode 100644
index 00000000000..af8475e9a5f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vla-bounds-ret-2.c
@@ -0,0 +1,28 @@ 
+/* { dg-do run } */
+/* { dg-options "-fvla-bounds" } */
+
+#include <signal.h>
+#include <stdlib.h>
+
+/* Test returning a pointer to a 2D variable length array with
+ a fist constant dimension in a function returning a pointer
+ to a regular 2D array. */
+
+static void handler(int) { exit(0); }
+
+int n, m;
+
+static char (*z1(void))[5][5] { char (*p)[5][n] = 0; return p; }
+
+int main()
+{
+ signal(SIGILL, handler);
+
+ m = 5, n = 5;
+ z1();
+
+ m = 4, n = 3;
+ z1(); // 4 != 3
+ abort();
+}
+