Ping on this patch. Thanks.
Qing > On Dec 22, 2025, at 11:38, Qing Zhao <[email protected]> wrote: > > Hi, > > This is the 4th version of the patch. > > Compared to the 3rd version, the major difference are: > https://gcc.gnu.org/pipermail/gcc-patches/2025-December/703480.html > > 1. In grokfield, use declspecs->type instead of TREE_TYPE(value). > before checking "declspecs->type", add one more condition > "declspecs->typespec_kind == ctsk_tagdef". > 2. As a result, in the routine "verify_counted_by_for_top_anonymous_type" > eliminate the code for POINTER_TYPE_P (type). > > 3. Additional testing cases for fields with anonymous structure type to cover > nested pointers to, arrays of pointers, pointers to functons, etc. > (in gcc/testsuite/gcc.dg/counted-by-anonymous.c). > > Bootstrapped and regression tested on both X86 and aarch64, Okay for > committing? > > Thanks a lot. > > Qing > > =============================================== > Currently, GCC does not fully support the cases when a FAM or pointer field > and > its corresponding counted_by field are in different anonymous structure/unions > of a common named structure. > > For example: > > struct nested_mixed { > struct { > union { > int b; > float f; > }; > int n; > }; > struct { > PTR_TYPE *pointer __attribute__((__counted_by__(n))); > FAM_TYPE c[] __attribute__((__counted_by__(b))); > }; > } *nested_mixed_annotated; > > In order to support such cases, we always need to locate the first outer > named structure as the root, and then lookup_field inside this named > structure. When building the component_ref for the counted_by field, > we need to build a chain of component_ref starting from the root structure. > > When supporting the above in general, we also need to handle the following > several special cases correctly: > > A. Support an untagged type as its own top-level type. */ > struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; > > B. Support an unnamed field with a named struct/union. */ > struct s { struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; } > *y; > > C. When -fms-extensions is enabled: > > C.1 Do not support the inward-to-outward counted-by field reference > since checking the validity of such reference depends on unknown > situation at the end of the structure definition. > > struct bar { > char *buf __counted_by (n); /* { dg-error "attribute is not a field > declaration in the same structure as" } */ > }; > > C.2 support the outward-to-inward counted-by field reference. > > PR C/122495 > PR C/122496 > > gcc/c/ChangeLog: > > * c-decl.cc (grokfield): Call verify_counted_by_for_top_anonymous_type > for named field. > (verify_counted_by_attribute): Change the prototype to a recursive > routine. > (verify_counted_by_for_top_anonymous_type): New routine. > (finish_struct): Set C_TYPE_FIELDS_HAS_COUNTED_BY and call the routine > verify_counted_by_attribute only for named structure. > * c-parser.cc (c_parser_declaration_or_fndef): Call > verify_counted_by_for_top_anonymous_type for the decl. > (c_parser_parameter_declaration): Call > verify_counted_by_for_top_anonymous_type for the parameter. > (c_parser_type_name): Call verify_counted_by_for_top_anonymous_type > for the type. > * c-tree.h (C_TYPE_FIELDS_HAS_COUNTED_BY): New flag. > (verify_counted_by_for_top_anonymous_type): New routine. > * c-typeck.cc (build_counted_by_ref): Locate the root named structure, > build a chain of component_ref starting from the root structure. > > gcc/testsuite/ChangeLog: > > * gcc.dg/counted-by-anonymous-2-char.c: New test. > * gcc.dg/counted-by-anonymous-2-float.c: New test. > * gcc.dg/counted-by-anonymous-2-struct.c: New test. > * gcc.dg/counted-by-anonymous-2-union.c: New test. > * gcc.dg/counted-by-anonymous-2.c: New test. > * gcc.dg/counted-by-anonymous-3.c: New test. > * gcc.dg/counted-by-anonymous.c: New test. > * gcc.dg/ubsan/counted-by-anonymous-bounds-1.c: New test. > * gcc.dg/ubsan/counted-by-anonymous-bounds-2.c: New test. > * gcc.dg/ubsan/counted-by-anonymous-bounds.c: New test. > --- > gcc/c/c-decl.cc | 139 +++++++++------ > gcc/c/c-parser.cc | 14 ++ > gcc/c/c-tree.h | 6 + > gcc/c/c-typeck.cc | 49 ++++-- > .../gcc.dg/counted-by-anonymous-2-char.c | 8 + > .../gcc.dg/counted-by-anonymous-2-float.c | 8 + > .../gcc.dg/counted-by-anonymous-2-struct.c | 16 ++ > .../gcc.dg/counted-by-anonymous-2-union.c | 16 ++ > gcc/testsuite/gcc.dg/counted-by-anonymous-2.c | 66 +++++++ > gcc/testsuite/gcc.dg/counted-by-anonymous-3.c | 44 +++++ > gcc/testsuite/gcc.dg/counted-by-anonymous.c | 163 ++++++++++++++++++ > .../ubsan/counted-by-anonymous-bounds-1.c | 60 +++++++ > .../ubsan/counted-by-anonymous-bounds-2.c | 66 +++++++ > .../ubsan/counted-by-anonymous-bounds.c | 23 +++ > 14 files changed, 610 insertions(+), 68 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous-2-char.c > create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous-2-float.c > create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous-2-struct.c > create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous-2-union.c > create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous-2.c > create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous-3.c > create mode 100644 gcc/testsuite/gcc.dg/counted-by-anonymous.c > create mode 100644 gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-1.c > create mode 100644 gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-2.c > create mode 100644 gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds.c > > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc > index e79f77e6883..0ee7c624aa8 100644 > --- a/gcc/c/c-decl.cc > +++ b/gcc/c/c-decl.cc > @@ -9086,6 +9086,12 @@ grokfield (location_t loc, > width ? &width : NULL, decl_attrs, expr, NULL, > DEPRECATED_NORMAL); > > + /* When this field has name, its type is a top level type, we should > + call verify_counted_by_for_top_anonymous_type. */ > + if (DECL_NAME (value) != NULL_TREE > + && declspecs->typespec_kind == ctsk_tagdef) > + verify_counted_by_for_top_anonymous_type (declspecs->type); > + > finish_decl (value, loc, NULL_TREE, NULL_TREE, NULL_TREE); > DECL_INITIAL (value) = width; > if (width) > @@ -9485,65 +9491,90 @@ c_update_type_canonical (tree t) > } > } > > -/* Verify the argument of the counted_by attribute of each of the > - FIELDS_WITH_COUNTED_BY is a valid field of the containing structure, > - STRUCT_TYPE, Report error and remove the corresponding attribute > - when it's not. */ > +/* Verify the argument of the counted_by attribute of each field of > + the containing structure, OUTMOST_STRUCT_TYPE, including its inner > + anonymous struct/union, Report error and remove the corresponding > + attribute when it's not. */ > > static void > -verify_counted_by_attribute (tree struct_type, > - auto_vec<tree> *fields_with_counted_by) > +verify_counted_by_attribute (tree outmost_struct_type, > + tree cur_struct_type) > { > - for (tree field_decl : *fields_with_counted_by) > + for (tree field = TYPE_FIELDS (cur_struct_type); field; > + field = TREE_CHAIN (field)) > { > - tree attr_counted_by = lookup_attribute ("counted_by", > - DECL_ATTRIBUTES (field_decl)); > + if (c_flexible_array_member_type_p (TREE_TYPE (field)) > + || TREE_CODE (TREE_TYPE (field)) == POINTER_TYPE) > + { > + tree attr_counted_by = lookup_attribute ("counted_by", > + DECL_ATTRIBUTES (field)); > > - if (!attr_counted_by) > - continue; > + if (!attr_counted_by) > + continue; > > - /* If there is an counted_by attribute attached to the field, > - verify it. */ > + /* If there is an counted_by attribute attached to the field, > + verify it. */ > > - tree fieldname = TREE_VALUE (TREE_VALUE (attr_counted_by)); > + tree fieldname = TREE_VALUE (TREE_VALUE (attr_counted_by)); > > - /* Verify the argument of the attrbute is a valid field of the > - containing structure. */ > + /* Verify the argument of the attrbute is a valid field of the > + containing structure. */ > > - tree counted_by_field = lookup_field (struct_type, fieldname); > + tree counted_by_field = lookup_field (outmost_struct_type, > + fieldname); > > - /* Error when the field is not found in the containing structure and > - remove the corresponding counted_by attribute from the field_decl. */ > - if (!counted_by_field) > - { > - error_at (DECL_SOURCE_LOCATION (field_decl), > + /* Error when the field is not found in the containing structure > + and remove the corresponding counted_by attribute from the > + field_decl. */ > + if (!counted_by_field) > + { > + error_at (DECL_SOURCE_LOCATION (field), > "argument %qE to the %<counted_by%> attribute" > " is not a field declaration in the same structure" > - " as %qD", fieldname, field_decl); > - DECL_ATTRIBUTES (field_decl) > - = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); > - } > - else > - /* Error when the field is not with an integer type. */ > - { > - while (TREE_CHAIN (counted_by_field)) > - counted_by_field = TREE_CHAIN (counted_by_field); > - tree real_field = TREE_VALUE (counted_by_field); > - > - if (!INTEGRAL_TYPE_P (TREE_TYPE (real_field))) > + " as %qD", fieldname, field); > + DECL_ATTRIBUTES (field) > + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field)); > + } > + else > + /* Error when the field is not with an integer type. */ > { > - error_at (DECL_SOURCE_LOCATION (field_decl), > + while (TREE_CHAIN (counted_by_field)) > + counted_by_field = TREE_CHAIN (counted_by_field); > + tree real_field = TREE_VALUE (counted_by_field); > + > + if (!INTEGRAL_TYPE_P (TREE_TYPE (real_field))) > + { > + error_at (DECL_SOURCE_LOCATION (field), > "argument %qE to the %<counted_by%> attribute" > " is not a field declaration with an integer type", > fieldname); > - DECL_ATTRIBUTES (field_decl) > - = remove_attribute ("counted_by", > - DECL_ATTRIBUTES (field_decl)); > + DECL_ATTRIBUTES (field) > + = remove_attribute ("counted_by", > + DECL_ATTRIBUTES (field)); > + } > } > } > + else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (field)) > + && (DECL_NAME (field) == NULL_TREE)) > + verify_counted_by_attribute (outmost_struct_type, TREE_TYPE (field)); > } > } > > +/* Caller should make sure the TYPE is a top-level type (i.e. not being > + nested in other structure/uniona). For such type, verify its counted_by > + if it is an anonymous structure/union. */ > + > +void > +verify_counted_by_for_top_anonymous_type (tree type) > +{ > + if (!RECORD_OR_UNION_TYPE_P (type)) > + return; > + > + if (C_TYPE_FIELDS_HAS_COUNTED_BY (type) > + && c_type_tag (type) == NULL_TREE) > + verify_counted_by_attribute (type, type); > +} > + > /* TYPE is a struct or union that we're applying may_alias to after the body > is > parsed. Fixup any POINTER_TO types. */ > > @@ -9615,7 +9646,6 @@ finish_struct (location_t loc, tree t, tree fieldlist, > tree attributes, > until now.) */ > > bool saw_named_field = false; > - auto_vec<tree> fields_with_counted_by; > for (x = fieldlist; x; x = DECL_CHAIN (x)) > { > /* Whether this field is the last field of the structure or union. > @@ -9691,20 +9721,22 @@ finish_struct (location_t loc, tree t, tree > fieldlist, tree attributes, > pedwarn (DECL_SOURCE_LOCATION (x), OPT_Wpedantic, > "flexible array member in a struct with no named " > "members is a GCC extension"); > - > - /* If there is a counted_by attribute attached to this field, > - record it here and do more verification later after the > - whole structure is complete. */ > if (lookup_attribute ("counted_by", DECL_ATTRIBUTES (x))) > - fields_with_counted_by.safe_push (x); > + C_TYPE_FIELDS_HAS_COUNTED_BY (t) = 1; > } > > - if (TREE_CODE (TREE_TYPE (x)) == POINTER_TYPE) > - /* If there is a counted_by attribute attached to this field, > - record it here and do more verification later after the > - whole structure is complete. */ > - if (lookup_attribute ("counted_by", DECL_ATTRIBUTES (x))) > - fields_with_counted_by.safe_push (x); > + if (TREE_CODE (TREE_TYPE (x)) == POINTER_TYPE > + && lookup_attribute ("counted_by", DECL_ATTRIBUTES (x))) > + C_TYPE_FIELDS_HAS_COUNTED_BY (t) = 1; > + > + /* If the field is an anonymous structure that includes a field > + with counted_by attribute, this structure should also be marked > + too. */ > + if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (x)) > + && C_TYPE_FIELDS_HAS_COUNTED_BY (TREE_TYPE (x)) > + && DECL_NAME (x) == NULL_TREE > + && c_type_tag (TREE_TYPE (x)) == NULL_TREE) > + C_TYPE_FIELDS_HAS_COUNTED_BY (t) = 1; > > if (pedantic && TREE_CODE (t) == RECORD_TYPE > && flexible_array_type_p (TREE_TYPE (x))) > @@ -9971,6 +10003,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, > tree attributes, > C_TYPE_FIELDS_READONLY (x) = C_TYPE_FIELDS_READONLY (t); > C_TYPE_FIELDS_VOLATILE (x) = C_TYPE_FIELDS_VOLATILE (t); > C_TYPE_FIELDS_NON_CONSTEXPR (x) = C_TYPE_FIELDS_NON_CONSTEXPR (t); > + C_TYPE_FIELDS_HAS_COUNTED_BY (x) = C_TYPE_FIELDS_HAS_COUNTED_BY (t); > C_TYPE_VARIABLE_SIZE (x) = C_TYPE_VARIABLE_SIZE (t); > C_TYPE_VARIABLY_MODIFIED (x) = C_TYPE_VARIABLY_MODIFIED (t); > C_TYPE_INCOMPLETE_VARS (x) = NULL_TREE; > @@ -10011,8 +10044,10 @@ finish_struct (location_t loc, tree t, tree > fieldlist, tree attributes, > struct_parse_info->struct_types.safe_push (t); > } > > - if (fields_with_counted_by.length () > 0) > - verify_counted_by_attribute (t, &fields_with_counted_by); > + /* Only when the enclosing struct/union type is not anonymous, do more > + verification on the fields with counted_by attributes. */ > + if (C_TYPE_FIELDS_HAS_COUNTED_BY (t) && c_type_tag (t) != NULL_TREE) > + verify_counted_by_attribute (t, t); > > return t; > } > diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc > index 8065d3000e6..9640ed5d269 100644 > --- a/gcc/c/c-parser.cc > +++ b/gcc/c/c-parser.cc > @@ -2571,6 +2571,11 @@ c_parser_declaration_or_fndef (c_parser *parser, bool > fndef_ok, > } > > finish_declspecs (specs); > + /* When the decl is declared, its type is a top level type, we should > + call verify_counted_by_for_top_anonymous_type. */ > + if (specs->typespec_kind == ctsk_tagdef) > + verify_counted_by_for_top_anonymous_type (specs->type); > + > bool gnu_auto_type_p = specs->typespec_word == cts_auto_type; > bool std_auto_type_p = specs->c23_auto_p; > bool any_auto_type_p = gnu_auto_type_p || std_auto_type_p; > @@ -5497,6 +5502,11 @@ c_parser_parameter_declaration (c_parser *parser, tree > attrs, > c_parser_declspecs (parser, specs, true, true, true, true, false, > !have_gnu_attrs, true, cla_nonabstract_decl); > finish_declspecs (specs); > + /* When the param is declared, its type is a top level type, we should > + call verify_counted_by_for_top_anonymous_type. */ > + if (specs->typespec_kind == ctsk_tagdef) > + verify_counted_by_for_top_anonymous_type (specs->type); > + > pending_xref_error (); > prefix_attrs = specs->attrs; > specs->attrs = NULL_TREE; > @@ -6499,6 +6509,10 @@ c_parser_type_name (c_parser *parser, bool alignas_ok) > { > pending_xref_error (); > finish_declspecs (specs); > + /* When the typename is declared, its type is a top level type, we > should > + call verify_counted_by_for_top_anonymous_type. */ > + if (specs->typespec_kind == ctsk_tagdef) > + verify_counted_by_for_top_anonymous_type (specs->type); > } > declarator = c_parser_declarator (parser, > specs->typespec_kind != ctsk_none, > diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h > index 80b14715403..e1cd28fd1d5 100644 > --- a/gcc/c/c-tree.h > +++ b/gcc/c/c-tree.h > @@ -39,6 +39,10 @@ along with GCC; see the file COPYING3. If not see > permitted for a constexpr object. */ > #define C_TYPE_FIELDS_NON_CONSTEXPR(TYPE) TREE_LANG_FLAG_4 (TYPE) > > +/* In a RECORD_TYPE or UNION_TYPE, nonzero if any component has a > + counted_by attribute. */ > +#define C_TYPE_FIELDS_HAS_COUNTED_BY(TYPE) TYPE_LANG_FLAG_3 (TYPE) > + > /* In a RECORD_TYPE or UNION_TYPE or ENUMERAL_TYPE > nonzero if the definition of the type has already started. */ > #define C_TYPE_BEING_DEFINED(TYPE) TYPE_LANG_FLAG_0 (TYPE) > @@ -986,6 +990,8 @@ extern void c_pushtag (location_t, tree, tree); > extern void c_bind (location_t, tree, bool); > extern bool tag_exists_p (enum tree_code, tree); > > +extern void verify_counted_by_for_top_anonymous_type (tree); > + > /* In c-errors.cc */ > extern bool pedwarn_c90 (location_t, diagnostics::option_id, const char *, > ...) > ATTRIBUTE_GCC_DIAG(3,4); > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc > index cab21e29004..f9800284e33 100644 > --- a/gcc/c/c-typeck.cc > +++ b/gcc/c/c-typeck.cc > @@ -3090,7 +3090,6 @@ static tree > build_counted_by_ref (tree datum, tree subdatum, > tree *counted_by_type) > { > - tree type = TREE_TYPE (datum); > tree sub_type = TREE_TYPE (subdatum); > if (!c_flexible_array_member_type_p (sub_type) > && TREE_CODE (sub_type) != POINTER_TYPE) > @@ -3098,28 +3097,46 @@ build_counted_by_ref (tree datum, tree subdatum, > > tree attr_counted_by = lookup_attribute ("counted_by", > DECL_ATTRIBUTES (subdatum)); > + if (!attr_counted_by) > + return NULL_TREE; > + > tree counted_by_ref = NULL_TREE; > *counted_by_type = NULL_TREE; > - if (attr_counted_by) > + > + tree type = TREE_TYPE (datum); > + > + /* If the type of the containing structure is an anonymous struct/union, > + and this anonymous struct/union is not a root type, get the first > + outer named structure/union type. */ > + while (TREE_CODE (datum) == COMPONENT_REF > + && c_type_tag (type) == NULL_TREE > + && DECL_NAME (TREE_OPERAND (datum, 1)) == NULL_TREE) > { > - tree field_id = TREE_VALUE (TREE_VALUE (attr_counted_by)); > - counted_by_ref > - = build_component_ref (UNKNOWN_LOCATION, > - datum, field_id, > - UNKNOWN_LOCATION, UNKNOWN_LOCATION); > - counted_by_ref = build_fold_addr_expr (counted_by_ref); > + datum = TREE_OPERAND (datum, 0); > + type = TREE_TYPE (datum); > + } > > + tree field_id = TREE_VALUE (TREE_VALUE (attr_counted_by)); > + tree counted_by_field = lookup_field (type, field_id); > + gcc_assert (counted_by_field); > + > + tree counted_by_subdatum; > + do > + { > + counted_by_subdatum = TREE_VALUE (counted_by_field); > /* Get the TYPE of the counted_by field. */ > - tree counted_by_field = lookup_field (type, field_id); > - gcc_assert (counted_by_field); > + *counted_by_type = TREE_TYPE (counted_by_subdatum); > > - do > - { > - *counted_by_type = TREE_TYPE (TREE_VALUE (counted_by_field)); > - counted_by_field = TREE_CHAIN (counted_by_field); > - } > - while (counted_by_field); > + counted_by_ref > + = build3 (COMPONENT_REF, TREE_TYPE (counted_by_subdatum), > + datum, counted_by_subdatum, NULL_TREE); > + > + datum = counted_by_ref; > + counted_by_field = TREE_CHAIN (counted_by_field); > } > + while (counted_by_field); > + > + counted_by_ref = build_fold_addr_expr (counted_by_ref); > return counted_by_ref; > } > > diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous-2-char.c > b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-char.c > new file mode 100644 > index 00000000000..20067a29816 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-char.c > @@ -0,0 +1,8 @@ > +/* Test the attribute counted_by for pointer field in anonymous struct/union > + and its usage in __builtin_dynamic_object_size. */ > +/* { dg-do run } */ > +/* { dg-options "-O2" } */ > + > +#define PTR_TYPE char > +#define FAM_TYPE char > +#include "counted-by-anonymous-2.c" > diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous-2-float.c > b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-float.c > new file mode 100644 > index 00000000000..bb3560141f1 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-float.c > @@ -0,0 +1,8 @@ > +/* Test the attribute counted_by for pointer field in anonymous struct/union > + and its usage in __builtin_dynamic_object_size. */ > +/* { dg-do run } */ > +/* { dg-options "-O2" } */ > + > +#define PTR_TYPE float > +#define FAM_TYPE float > +#include "counted-by-anonymous-2.c" > diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous-2-struct.c > b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-struct.c > new file mode 100644 > index 00000000000..37e011decd2 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-struct.c > @@ -0,0 +1,16 @@ > +/* Test the attribute counted_by for pointer field in anonymous struct/union > + and its usage in __builtin_dynamic_object_size. */ > +/* { dg-do run } */ > +/* { dg-options "-O2" } */ > + > +struct A { > + int a; > + char *b; > +}; > +struct B { > + float a; > + double b; > +}; > +#define PTR_TYPE struct A > +#define FAM_TYPE struct B > +#include "counted-by-anonymous-2.c" > diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous-2-union.c > b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-union.c > new file mode 100644 > index 00000000000..eebb4d63e3a > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-2-union.c > @@ -0,0 +1,16 @@ > +/* Test the attribute counted_by for pointer field in anonymous struct/union > + and its usage in __builtin_dynamic_object_size. */ > +/* { dg-do run } */ > +/* { dg-options "-O2" } */ > + > +union A { > + int a; > + char *b; > +}; > +union B { > + float a; > + double b; > +}; > +#define PTR_TYPE union A > +#define FAM_TYPE union B > +#include "counted-by-anonymous-2.c" > diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous-2.c > b/gcc/testsuite/gcc.dg/counted-by-anonymous-2.c > new file mode 100644 > index 00000000000..000524aa6d4 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-2.c > @@ -0,0 +1,66 @@ > +/* Test the attribute counted_by for pointer field in anonymous struct/union > + and its usage in __builtin_dynamic_object_size. */ > +/* { dg-do run } */ > +/* { dg-options "-O2" } */ > + > +#include "builtin-object-size-common.h" > +#ifndef PTR_TYPE > +#define PTR_TYPE int > +#endif > +#ifndef FAM_TYPE > +#define FAM_TYPE int > +#endif > + > +#define __counted_by(member) \ > + __attribute__((__counted_by__(member))) > + > +struct nested_mixed { > + struct { > + union { > + int b; > + float f; > + }; > + int n; > + }; > + struct { > + PTR_TYPE *pointer __counted_by(n); > + FAM_TYPE c[] __counted_by(b); > + }; > +} *nested_mixed_annotated; > + > + > +void __attribute__((__noinline__)) setup (int pointer_array_count, > + int fam_count) > +{ > + nested_mixed_annotated > + = (struct nested_mixed *) malloc (sizeof (struct nested_mixed) > + + fam_count * sizeof (FAM_TYPE)); > + nested_mixed_annotated->pointer > + = (PTR_TYPE *) malloc (sizeof (PTR_TYPE) * pointer_array_count); > + nested_mixed_annotated->b = fam_count; > + nested_mixed_annotated->n = pointer_array_count; > + return; > +} > + > +void __attribute__((__noinline__)) test () > +{ > + EXPECT(__builtin_dynamic_object_size(nested_mixed_annotated->c, 1), > + nested_mixed_annotated->b * sizeof (FAM_TYPE)); > + EXPECT(__builtin_dynamic_object_size(nested_mixed_annotated->pointer, 1), > + nested_mixed_annotated->n * sizeof (PTR_TYPE)); > +} > + > +void cleanup () > +{ > + free (nested_mixed_annotated->pointer); > + free (nested_mixed_annotated); > +} > + > +int main(int argc, char *argv[]) > +{ > + setup (10,20); > + test (); > + DONE (); > + cleanup (); > + return 0; > +} > diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous-3.c > b/gcc/testsuite/gcc.dg/counted-by-anonymous-3.c > new file mode 100644 > index 00000000000..0867539e3d2 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/counted-by-anonymous-3.c > @@ -0,0 +1,44 @@ > +/* Testing the correct usage of attribute counted_by for anonymous > + structures with -fms-extensions. */ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fms-extensions" } */ > + > +#define __counted_by(member) \ > + __attribute__((__counted_by__(member))) > + > +/* Do not support the inward-to-outward counted-by field reference for > + ms-extensions since checking the validity of such reference depends > + on unknown situation at the end of the structure definition. */ > +struct bar { > + char *buf __counted_by (n); /* { dg-error "attribute is not a field > declaration in the same structure as" } */ > +}; > + > +struct foo { > + int n; > + struct bar; > +}; > + > +/* However, at the same time, support the outward-to-inward counted-by > + field reference for ms-extensions. */ > +struct ids > +{ > + int length_ad; > + int length_na; > +}; > + > +typedef union > +{ > + int length_hb; > + float other; > +} ids_2; > + > +struct person > +{ > + int age; > + int weight; > + struct ids; // Anonymous structure, no name needed > + ids_2; // Anonymous union, no name needed > + char *address __attribute__ ((counted_by (length_ad))); > + char *hobby __attribute__ ((counted_by (length_hb))); > + char name[] __attribute__ ((counted_by (length_na))); > +}; > diff --git a/gcc/testsuite/gcc.dg/counted-by-anonymous.c > b/gcc/testsuite/gcc.dg/counted-by-anonymous.c > new file mode 100644 > index 00000000000..7882b47ffae > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/counted-by-anonymous.c > @@ -0,0 +1,163 @@ > +/* Testing the correct usage of attribute counted_by for anonymous > + structures. */ > +/* { dg-do compile } */ > +/* { dg-options "-O2" } */ > + > +#define __counted_by(member) \ > + __attribute__((__counted_by__(member))) > + > +struct fam_in_union { > + int count; > + union { > + char a; > + char fam[] __counted_by(count); > + }; > +}; > + > +struct fam_in_union_2 { > + int count; > + union inside { > + char a; > + char fam[] __counted_by(count); /* { dg-error "attribute is not a field > declaration in the same structure as" } */ > + } inside_u; > +}; > + > +struct fam_in_struct { > + int count; > + struct { > + char a; > + char fam[] __counted_by(count); > + }; > +}; > + > +struct fam_in_struct_2 { > + int count; > + struct insidestruct { > + char a; > + char fam[] __counted_by(count); /* { dg-error "attribute is not a field > declaration in the same structure as" } */ > + } inside_s; > +}; > + > +struct pointer_in_union { > + union { > + char a; > + char* p __counted_by(count); > + }; > + int count; > +}; > + > +struct pointer_in_union_2 { > + union insideunion { > + char a; > + char* p __counted_by(count); /* { dg-error "attribute is not a field > declaration in the same structure as" } */ > + } inside_u; > + int count; > +}; > + > +struct pointer_in_union_3 { > + union { > + char b; > + char* p __counted_by(countp); /* { dg-error "attribute is not a field > declaration with an integer type" } */ > + > + }; > + float countp; > +}; > + > +struct pointer_in_struct { > + struct { > + int count_q; > + char *p __counted_by(count_p); > + float *q __counted_by(count_q); > + int count_fam; > + struct { > + int count_p; > + char a; > + char fam[] __counted_by(count_fam); > + }; > + }; > +}; > + > +struct nested_mixed { > + struct { > + union { > + int b; > + float f; > + }; > + int n; > + }; > + struct { > + int *pointer __counted_by(n); > + float *pointer_2 __counted_by(f); /* { dg-error "attribute is not a > field declaration with an integer type" } */ > + char c[] __counted_by(b); > + }; > +} *array_nested_annotated; > + > +/* Support an untagged type as its own top-level type. */ > + > +/* A. Variable declaration. */ > + > +struct { int a0; char b0[] __attribute__ ((counted_by (a0))); } x0; > +struct { char b0[] __attribute__ ((counted_by (a0))); } x00; /* { dg-error > "attribute is not a field declaration in the same structure as" } */ > + > +struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; > +struct { char b1[] __attribute__ ((counted_by (a1))); } *x1; /* { dg-error > "attribute is not a field declaration in the same structure as" } */ > + > +struct { char *e __attribute__ ((counted_by (f))); int f; } *x2; > +struct { char *e1 __attribute__ ((counted_by (f1))); } *x3; /* { dg-error > "attribute is not a field declaration in the same structure as" } */ > + > +struct { char *e4 __attribute__ ((counted_by (f4))); int f4; } **x4; > +struct { char *e5 __attribute__ ((counted_by (f5))); } **x5; /* { dg-error > "attribute is not a field declaration in the same structure as" } */ > + > +/* B. Function declaration and definitions. */ > +struct { int c0; char d0[] __attribute__ ((counted_by (c0))); } func0 (int > a, int b); > +struct { char d[] __attribute__ ((counted_by (c))); } func00 (int a, int b); > /* { dg-error "attribute is not a field declaration in the same structure as" > } */ > + > +struct { int c; char d[] __attribute__ ((counted_by (c))); } *func (int a, > int b); > +struct { char d1[] __attribute__ ((counted_by (c1))); } *func1 (int a, int > b); /* { dg-error "attribute is not a field declaration in the same structure > as" } */ > + > +struct { int c2; char *d2 __attribute__ ((counted_by (c2))); } *func2 () { > return 0; } > +struct { char *d3 __attribute__ ((counted_by (c3))); } *func3 () { return 0; > } /* { dg-error "attribute is not a field declaration in the same structure > as" } */ > + > +struct { int c4; char *d4 __attribute__ ((counted_by (c4))); } **func4 () { > return 0; } > +struct { char *d5 __attribute__ ((counted_by (c5))); } **func5 () { return > 0; } /* { dg-error "attribute is not a field declaration in the same > structure as" } */ > + > +/* C. Parameter declaration. */ > +void func6 (struct { float *f __attribute__ ((counted_by (g))); int g; } > *pa, int count); /* { dg-warning "anonymous struct declared inside parameter > list will not be visible outside" } */ > +void func7 (struct { float *f1 __attribute__ ((counted_by (g1)));} *pa, int > count); /* { dg-error "attribute is not a field declaration in the same > structure as" } */ > + /* { dg-warning "anonymous struct declared inside parameter list will > not be visible outside" "" { target *-*-* } .-1 } */ > + > +/* D. Typename. */ > +int foo () > +{ > + int res = sizeof (struct {int count; char *p __attribute__ ((counted_by > (count))); }); > + > + res += alignof (struct {char *p1 __attribute__ ((counted_by (count1))); > }); /* { dg-error "attribute is not a field declaration in the same structure > as" } */ > + return res; > +} > + > +typedef struct { > + int mc; > + char *d __attribute__ ((counted_by (mc))); > +} mys; > + > +typedef struct { > + char *md1 __attribute__ ((counted_by (mc1))); /* { dg-error "attribute is > not a field declaration in the same structure as" } */ > +} mys1; > + > + > +/* Support an unnamed field with a named struct/union. */ > +struct s0 { struct { int a0; char b0[] __attribute__ ((counted_by (a0))); } > x; } yy0; > +struct s00 { struct { char c0[] __attribute__ ((counted_by (d0))); } x; } > yy00; /* { dg-error "attribute is not a field declaration in the same > structure as" } */ > + > +struct s { struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; > } *y; > +struct s1 { struct { char c[] __attribute__ ((counted_by (d))); } *x; } *yy; > /* { dg-error "attribute is not a field declaration in the same structure as" > } */ > + > +struct s2 { struct { char *b __attribute__ ((counted_by (a))); int a; } *x; > } *y2; > +struct s3 { struct { char *c __attribute__ ((counted_by (d))); } *x; } *y3; > /* { dg-error "attribute is not a field declaration in the same structure as" > } */ > + > +struct s4 { struct { char *b4 __attribute__ ((counted_by (a4))); int a4; } > x[3]; } *y4; > +struct s5 { struct { char *c5 __attribute__ ((counted_by (d5))); } x[4]; } > *y5; /* { dg-error "attribute is not a field declaration in the same > structure as" } */ > + > +struct s6 { struct { char *b6 __attribute__ ((counted_by (a6))); int a6; } > *x[3]; } *y6; > + > +struct s7 { struct { char *c7 __attribute__ ((counted_by (d7))); } *x[4]; } > *y7; /* { dg-error "attribute is not a field declaration in the same > structure as" } */ > diff --git a/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-1.c > b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-1.c > new file mode 100644 > index 00000000000..837d0261c49 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-1.c > @@ -0,0 +1,60 @@ > +/* Testing the attribute counted_by for anonymous structures as > ms-extensions. > + used in bounds sanitizer. */ > +/* { dg-do run } */ > +/* { dg-options "-O2 -fms-extensions -fsanitize=bounds" } */ > +/* { dg-output "index 12 out of bounds for type 'char > \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ > +/* { dg-output "\[^\n\r]*index 22 out of bounds for type 'char > \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ > +/* { dg-output "\[^\n\r]*index 31 out of bounds for type 'char > \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ > + > + > +struct ids > +{ > + int length_ad; > + int length_na; > +}; > + > +typedef union > +{ > + int length_hb; > + float other; > +} ids_2; > + > +struct person > +{ > + int age; > + int weight; > + struct ids; // Anonymous structure, no name needed > + ids_2; // Anonymous union, no name needed > + char *address __attribute__ ((counted_by (length_ad))); > + char *hobby __attribute__ ((counted_by (length_hb))); > + char name[] __attribute__ ((counted_by (length_na))); > +} *Jim; > + > +static void > +setup (int address_l, int name_l, int hb_l) > +{ > + Jim = (struct person *) __builtin_malloc (sizeof (struct person) > + + name_l * sizeof (char)); > + Jim->length_na = name_l; > + Jim->address = (char *) __builtin_malloc (sizeof (char) * address_l); > + Jim->length_ad = address_l; > + Jim->hobby = (char *) __builtin_malloc (sizeof (char) * hb_l); > + Jim->length_hb = hb_l; > +} > + > +static void > +cleanup () > +{ > + __builtin_free (Jim->address); > + __builtin_free (Jim->hobby); > + __builtin_free (Jim); > +} > + > +int main() > +{ > + setup (20, 10, 30); > + Jim->name[12] = 'a'; > + Jim->address[22] = 'k'; > + Jim->hobby[31] = 'h'; > + return 0; > +} > diff --git a/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-2.c > b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-2.c > new file mode 100644 > index 00000000000..a837951f74c > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds-2.c > @@ -0,0 +1,66 @@ > +/* Testing the attribute counted_by for anonymous structures as > plan9-extensions. > + used in bounds sanitizer. */ > +/* { dg-do run } */ > +/* { dg-options "-O2 -fplan9-extensions -fsanitize=bounds" } */ > +/* { dg-output "index 12 out of bounds for type 'char > \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ > +/* { dg-output "\[^\n\r]*index 22 out of bounds for type 'char > \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ > +/* { dg-output "\[^\n\r]*index 31 out of bounds for type 'char > \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ > + > +struct ids > +{ > + int length_ad; > + int length_na; > +}; > + > +typedef union > +{ > + int length_hb; > + float other; > +} ids_2; > + > +struct person > +{ > + int age; > + int weight; > + struct ids; // Anonymous structure, no name needed > + ids_2; // Anonymous union, no name needed > + char *address __attribute__ ((counted_by (length_ad))); > + char *hobby __attribute__ ((counted_by (length_hb))); > + char name[] __attribute__ ((counted_by (length_na))); > +} *Jim; > + > +static void > +set_counted_by (struct ids *p, ids_2 *p2, > + int address_l, int name_l, int hb_l) > +{ > + p->length_ad = address_l; > + p->length_na = name_l; > + p2->length_hb = hb_l; > +} > + > +static void > +setup (int address_l, int name_l, int hb_l) > +{ > + Jim = (struct person *) __builtin_malloc (sizeof (struct person) > + + name_l * sizeof (char)); > + Jim->address = (char *) __builtin_malloc (sizeof (char) * address_l); > + Jim->hobby = (char *) __builtin_malloc (sizeof (char) * hb_l); > + set_counted_by (Jim, Jim, address_l, name_l, hb_l); > +} > + > +static void > +cleanup () > +{ > + __builtin_free (Jim->address); > + __builtin_free (Jim->hobby); > + __builtin_free (Jim); > +} > + > +int main() > +{ > + setup (20, 10, 30); > + Jim->name[12] = 'a'; > + Jim->address[22] = 'k'; > + Jim->hobby[31] = 'h'; > + return 0; > +} > diff --git a/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds.c > b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds.c > new file mode 100644 > index 00000000000..8c348560d07 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/ubsan/counted-by-anonymous-bounds.c > @@ -0,0 +1,23 @@ > +/* Testing the attribute counted_by for anonymous structures as the top-level > + type and as the type for an unnamed field. */ > +/* { dg-do run } */ > +/* { dg-options "-O2 -fsanitize=bounds" } */ > +/* { dg-output "index 11 out of bounds for type 'char > \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ > +/* { dg-output "\[^\n\r]*index 22 out of bounds for type 'char > \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ > + > + > +struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; > +struct s { struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; > } *y; > + > +int main () > +{ > + x = (typeof (x)) __builtin_malloc (sizeof (*x) + sizeof (char) * 10); > + x->a = 10; > + x->b[11] = 0; > + > + y = (struct s *) __builtin_malloc (sizeof (struct s)); > + y->x = (typeof (y->x)) __builtin_malloc (sizeof (y->x) + sizeof (char) * > 20); > + y->x->a = 20; > + y->x->b[22] = 0; > + return 0; > +} > -- > 2.31.1 >
