Hi, Joseph, Could you please review the 2nd version of this patch?
Thanks. Qing > On Nov 25, 2025, at 09:49, Qing Zhao <[email protected]> wrote: > > Hi, > > this is the 2nd version of the patch. > > compared to the first version, the major difference are: > > 1. more testing cases: > A. Support an untagged type as its own top-level type. > B. Support an unnamed field with a named struct/union. > C. -fms-extensions (inward-to-outward and outward-to-inward references) > D. -fplan9-extersions. > 2. instead of using TYPE_NAME(type)== NULL, using c_tag_name (type)==NULL > && DECL_NAME(field)==NULL to determine an anonymous struct/union. > > bootstrapped and regression tested on both x86 and aarch64. > Okay for committing? > > thanks. > > 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 (verify_counted_by_attribute): Change the prototype to > a recursive routine. > (finish_struct): Set C_TYPE_FIELDS_HAS_COUNTED_BY and call the routine > verify_counted_by_attribute only for named structure. > * c-tree.h (C_TYPE_FIELDS_HAS_COUNTED_BY): New flag. > * 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 | 119 ++++++++++-------- > gcc/c/c-tree.h | 4 + > 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 | 99 +++++++++++++++ > .../ubsan/counted-by-anonymous-bounds-1.c | 60 +++++++++ > .../ubsan/counted-by-anonymous-bounds-2.c | 66 ++++++++++ > .../ubsan/counted-by-anonymous-bounds.c | 23 ++++ > 13 files changed, 510 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 0a368e410e5..6111b04c2e7 100644 > --- a/gcc/c/c-decl.cc > +++ b/gcc/c/c-decl.cc > @@ -9482,62 +9482,73 @@ 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) > + gcc_assert (c_type_tag (outmost_struct_type) != NULL); > + 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)); > } > } > > @@ -9612,7 +9623,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. > @@ -9688,20 +9698,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_tag (TREE_TYPE (x)) == NULL_TREE > + && DECL_NAME (x) == NULL_TREE > + && C_TYPE_FIELDS_HAS_COUNTED_BY (TREE_TYPE (x))) > + C_TYPE_FIELDS_HAS_COUNTED_BY (t) = 1; > > if (pedantic && TREE_CODE (t) == RECORD_TYPE > && flexible_array_type_p (TREE_TYPE (x))) > @@ -9961,6 +9973,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; > @@ -10001,8 +10014,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_tag (t) != NULL_TREE && C_TYPE_FIELDS_HAS_COUNTED_BY (t)) > + verify_counted_by_attribute (t, t); > > return t; > } > diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h > index ff63d69e85d..e758f672065 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) > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc > index 2cef4636bd7..f0495bd93e7 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..5ae611d4e96 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/counted-by-anonymous.c > @@ -0,0 +1,99 @@ > +/* 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. */ > +struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; > + > +/* Support an unnamed field with a named struct/union. */ > +struct s { struct { int a; char b[] __attribute__ ((counted_by (a))); } *x; > } *y; > 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 >
