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 <uec...@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
> > 
> > 
> > 
> 

Reply via email to