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
> 


Reply via email to