Message ID | 20230825152425.2417656-2-qing.zhao@oracle.com |
---|---|
State | New |
Headers | show |
Series | New attribute "counted_by" to annotate bounds for C99 FAM(PR108896) | expand |
PIng. thanks. Qing > On Aug 25, 2023, at 11:24 AM, Qing Zhao <qing.zhao@oracle.com> wrote: > > Provide a new counted_by attribute to flexible array member field. > > 'counted_by (COUNT)' > The 'counted_by' attribute may be attached to the flexible array > member of a structure. It indicates that the number of the > elements of the array is given by the field named "COUNT" in the > same structure as the flexible array member. GCC uses this > information to improve the results of the array bound sanitizer and > the '__builtin_dynamic_object_size'. > > For instance, the following code: > > struct P { > size_t count; > char other; > char array[] __attribute__ ((counted_by (count))); > } *p; > > specifies that the 'array' is a flexible array member whose number > of elements is given by the field 'count' in the same structure. > > The field that represents the number of the elements should have an > integer type. An explicit 'counted_by' annotation defines a > relationship between two objects, 'p->array' and 'p->count', that > 'p->array' has _at least_ 'p->count' number of elements available. > This relationship must hold even after any of these related objects > are updated. It's the user's responsibility to make sure this > relationship to be kept all the time. Otherwise the results of the > array bound sanitizer and the '__builtin_dynamic_object_size' might > be incorrect. > > For instance, in the following example, the allocated array has > less elements than what's specified by the 'sbuf->count', this is > an user error. As a result, out-of-bounds access to the array > might not be detected. > > #define SIZE_BUMP 10 > struct P *sbuf; > void alloc_buf (size_t nelems) > { > sbuf = (struct P *) malloc (MAX (sizeof (struct P), > (offsetof (struct P, array[0]) > + nelems * sizeof (char)))); > sbuf->count = nelems + SIZE_BUMP; > /* This is invalid when the sbuf->array has less than sbuf->count > elements. */ > } > > In the following example, the 2nd update to the field 'sbuf->count' > of the above structure will permit out-of-bounds access to the > array 'sbuf>array' as well. > > #define SIZE_BUMP 10 > struct P *sbuf; > void alloc_buf (size_t nelems) > { > sbuf = (struct P *) malloc (MAX (sizeof (struct P), > (offsetof (struct P, array[0]) > + (nelems + SIZE_BUMP) * sizeof (char)))); > sbuf->count = nelems; > /* This is valid when the sbuf->array has at least sbuf->count > elements. */ > } > void use_buf (int index) > { > sbuf->count = sbuf->count + SIZE_BUMP + 1; > /* Now the value of sbuf->count is larger than the number > of elements of sbuf->array. */ > sbuf->array[index] = 0; > /* then the out-of-bound access to this array > might not be detected. */ > } > > gcc/c-family/ChangeLog: > > PR C/108896 > * c-attribs.cc (handle_counted_by_attribute): New function. > (attribute_takes_identifier_p): Add counted_by attribute to the list. > * c-common.cc (c_flexible_array_member_type_p): ...To this. > * c-common.h (c_flexible_array_member_type_p): New prototype. > > gcc/c/ChangeLog: > > PR C/108896 > * c-decl.cc (flexible_array_member_type_p): Renamed and moved to... > (add_flexible_array_elts_to_size): Use renamed function. > (is_flexible_array_member_p): Use renamed function. > (verify_counted_by_attribute): New function. > (finish_struct): Use renamed function and verify counted_by > attribute. > > gcc/ChangeLog: > > PR C/108896 > * doc/extend.texi: Document attribute counted_by. > * tree.cc (get_named_field): New function. > * tree.h (get_named_field): New prototype. > > gcc/testsuite/ChangeLog: > > PR C/108896 > * gcc.dg/flex-array-counted-by.c: New test. > --- > gcc/c-family/c-attribs.cc | 54 ++++++++++++- > gcc/c-family/c-common.cc | 13 ++++ > gcc/c-family/c-common.h | 1 + > gcc/c/c-decl.cc | 79 +++++++++++++++----- > gcc/doc/extend.texi | 77 +++++++++++++++++++ > gcc/testsuite/gcc.dg/flex-array-counted-by.c | 40 ++++++++++ > gcc/tree.cc | 40 ++++++++++ > gcc/tree.h | 5 ++ > 8 files changed, 291 insertions(+), 18 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/flex-array-counted-by.c > > diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc > index e2792ca6898b..65e4f6639109 100644 > --- a/gcc/c-family/c-attribs.cc > +++ b/gcc/c-family/c-attribs.cc > @@ -103,6 +103,8 @@ static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree, > int, bool *); > static tree handle_strict_flex_array_attribute (tree *, tree, tree, > int, bool *); > +static tree handle_counted_by_attribute (tree *, tree, tree, > + int, bool *); > static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; > static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ; > static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *); > @@ -373,6 +375,8 @@ const struct attribute_spec c_common_attribute_table[] = > handle_warn_if_not_aligned_attribute, NULL }, > { "strict_flex_array", 1, 1, true, false, false, false, > handle_strict_flex_array_attribute, NULL }, > + { "counted_by", 1, 1, true, false, false, false, > + handle_counted_by_attribute, NULL }, > { "weak", 0, 0, true, false, false, false, > handle_weak_attribute, NULL }, > { "noplt", 0, 0, true, false, false, false, > @@ -601,7 +605,8 @@ attribute_takes_identifier_p (const_tree attr_id) > else if (!strcmp ("mode", spec->name) > || !strcmp ("format", spec->name) > || !strcmp ("cleanup", spec->name) > - || !strcmp ("access", spec->name)) > + || !strcmp ("access", spec->name) > + || !strcmp ("counted_by", spec->name)) > return true; > else > return targetm.attribute_takes_identifier_p (attr_id); > @@ -2555,6 +2560,53 @@ handle_strict_flex_array_attribute (tree *node, tree name, > return NULL_TREE; > } > > +/* Handle a "counted_by" attribute; arguments as in > + struct attribute_spec.handler. */ > + > +static tree > +handle_counted_by_attribute (tree *node, tree name, > + tree args, int ARG_UNUSED (flags), > + bool *no_add_attrs) > +{ > + tree decl = *node; > + tree argval = TREE_VALUE (args); > + > + /* This attribute only applies to field decls of a structure. */ > + if (TREE_CODE (decl) != FIELD_DECL) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qE attribute may not be specified for non-field" > + " declaration %q+D", name, decl); > + *no_add_attrs = true; > + } > + /* This attribute only applies to field with array type. */ > + else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qE attribute may not be specified for a non-array field", > + name); > + *no_add_attrs = true; > + } > + /* This attribute only applies to a C99 flexible array member type. */ > + else if (! c_flexible_array_member_type_p (TREE_TYPE (decl))) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qE attribute may not be specified for a non" > + " flexible array member field", > + name); > + *no_add_attrs = true; > + } > + /* The argument should be an identifier. */ > + else if (TREE_CODE (argval) != IDENTIFIER_NODE) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%<counted_by%> argument not an identifier"); > + *no_add_attrs = true; > + } > + > + return NULL_TREE; > +} > + > /* Handle a "weak" attribute; arguments as in > struct attribute_spec.handler. */ > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc > index 9fbaeb437a12..a18937245c2a 100644 > --- a/gcc/c-family/c-common.cc > +++ b/gcc/c-family/c-common.cc > @@ -9521,6 +9521,19 @@ c_common_finalize_early_debug (void) > (*debug_hooks->early_global_decl) (cnode->decl); > } > > +/* Determine whether TYPE is a ISO C99 flexible array memeber type "[]". */ > +bool > +c_flexible_array_member_type_p (const_tree type) > +{ > + if (TREE_CODE (type) == ARRAY_TYPE > + && TYPE_SIZE (type) == NULL_TREE > + && TYPE_DOMAIN (type) != NULL_TREE > + && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) > + return true; > + > + return false; > +} > + > /* Get the LEVEL of the strict_flex_array for the ARRAY_FIELD based on the > values of attribute strict_flex_array and the flag_strict_flex_arrays. */ > unsigned int > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h > index 78fc5248ba68..c29bb429062b 100644 > --- a/gcc/c-family/c-common.h > +++ b/gcc/c-family/c-common.h > @@ -909,6 +909,7 @@ extern tree fold_for_warn (tree); > extern tree c_common_get_narrower (tree, int *); > extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *); > extern void c_common_finalize_early_debug (void); > +extern bool c_flexible_array_member_type_p (const_tree); > extern unsigned int c_strict_flex_array_level_of (tree); > extern bool c_option_is_from_cpp_diagnostics (int); > > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc > index 1f9eb44dbaa2..e943b49b5230 100644 > --- a/gcc/c/c-decl.cc > +++ b/gcc/c/c-decl.cc > @@ -5173,19 +5173,6 @@ set_array_declarator_inner (struct c_declarator *decl, > return decl; > } > > -/* Determine whether TYPE is a ISO C99 flexible array memeber type "[]". */ > -static bool > -flexible_array_member_type_p (const_tree type) > -{ > - if (TREE_CODE (type) == ARRAY_TYPE > - && TYPE_SIZE (type) == NULL_TREE > - && TYPE_DOMAIN (type) != NULL_TREE > - && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) > - return true; > - > - return false; > -} > - > /* Determine whether TYPE is a one-element array type "[1]". */ > static bool > one_element_array_type_p (const_tree type) > @@ -5222,7 +5209,7 @@ add_flexible_array_elts_to_size (tree decl, tree init) > > elt = CONSTRUCTOR_ELTS (init)->last ().value; > type = TREE_TYPE (elt); > - if (flexible_array_member_type_p (type)) > + if (c_flexible_array_member_type_p (type)) > { > complete_array_type (&type, elt, false); > DECL_SIZE (decl) > @@ -9094,7 +9081,7 @@ is_flexible_array_member_p (bool is_last_field, > > bool is_zero_length_array = zero_length_array_type_p (TREE_TYPE (x)); > bool is_one_element_array = one_element_array_type_p (TREE_TYPE (x)); > - bool is_flexible_array = flexible_array_member_type_p (TREE_TYPE (x)); > + bool is_flexible_array = c_flexible_array_member_type_p (TREE_TYPE (x)); > > unsigned int strict_flex_array_level = c_strict_flex_array_level_of (x); > > @@ -9124,6 +9111,61 @@ is_flexible_array_member_p (bool is_last_field, > return false; > } > > +/* Verify the argument of the counted_by attribute of the flexible array > + member FIELD_DECL is a valid field of the containing structure's fieldlist, > + FIELDLIST, Report error and remove this attribute when it's not. */ > +static void > +verify_counted_by_attribute (tree fieldlist, tree field_decl) > +{ > + tree attr_counted_by = lookup_attribute ("counted_by", > + DECL_ATTRIBUTES (field_decl)); > + > + if (!attr_counted_by) > + return; > + > + /* If there is an counted_by attribute attached to the field, > + verify it. */ > + > + const char *fieldname > + = IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (attr_counted_by))); > + > + /* Verify the argument of the attrbute is a valid field of the > + containing structure. */ > + > + tree counted_by_field = get_named_field (fieldlist, fieldname); > + > + /* Error when the field is not found in the containing structure. */ > + if (!counted_by_field) > + { > + error_at (DECL_SOURCE_LOCATION (field_decl), > + "%qE attribute argument not a field declaration" > + " in the same structure, ignore it", > + (get_attribute_name (attr_counted_by))); > + > + 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 (TREE_CODE (TREE_TYPE (real_field)) != INTEGER_TYPE) > + { > + error_at (DECL_SOURCE_LOCATION (field_decl), > + "%qE attribute argument not a field declaration" > + " with integer type, ignore it", > + (get_attribute_name (attr_counted_by))); > + > + DECL_ATTRIBUTES (field_decl) > + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); > + } > + } > + > + return; > +} > > /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T. > LOC is the location of the RECORD_TYPE or UNION_TYPE's definition. > @@ -9244,7 +9286,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, > DECL_PACKED (x) = 1; > > /* Detect flexible array member in an invalid context. */ > - if (flexible_array_member_type_p (TREE_TYPE (x))) > + if (c_flexible_array_member_type_p (TREE_TYPE (x))) > { > if (TREE_CODE (t) == UNION_TYPE) > { > @@ -9265,6 +9307,9 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, > "members"); > TREE_TYPE (x) = error_mark_node; > } > + /* if there is a counted_by attribute attached to this field, > + verify it. */ > + verify_counted_by_attribute (fieldlist, x); > } > > if (pedantic && TREE_CODE (t) == RECORD_TYPE > @@ -9279,7 +9324,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, > when x is an array and is the last field. */ > if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE) > TYPE_INCLUDES_FLEXARRAY (t) > - = is_last_field && flexible_array_member_type_p (TREE_TYPE (x)); > + = is_last_field && c_flexible_array_member_type_p (TREE_TYPE (x)); > /* Recursively set TYPE_INCLUDES_FLEXARRAY for the context of x, t > when x is an union or record and is the last field. */ > else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (x))) > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > index 97eaacf8a7ec..ea6240646936 100644 > --- a/gcc/doc/extend.texi > +++ b/gcc/doc/extend.texi > @@ -7617,6 +7617,83 @@ When both the attribute and the option present at the same time, the level of > the strictness for the specific trailing array field is determined by the > attribute. > > +@cindex @code{counted_by} variable attribute > +@item counted_by (@var{count}) > +The @code{counted_by} attribute may be attached to the flexible array > +member of a structure. It indicates that the number of the elements of the > +array is given by the field named "@var{count}" in the same structure as the > +flexible array member. GCC uses this information to improve the results of > +the array bound sanitizer and the @code{__builtin_dynamic_object_size}. > + > +For instance, the following code: > + > +@smallexample > +struct P @{ > + size_t count; > + char other; > + char array[] __attribute__ ((counted_by (count))); > +@} *p; > +@end smallexample > + > +@noindent > +specifies that the @code{array} is a flexible array member whose number of > +elements is given by the field @code{count} in the same structure. > + > +The field that represents the number of the elements should have an integer > +type. An explicit @code{counted_by} annotation defines a relationship between > +two objects, @code{p->array} and @code{p->count}, that @code{p->array} has > +@emph{at least} @code{p->count} number of elements available. This relationship > +must hold even after any of these related objects are updated. It's the user's > +responsibility to make sure this relationship to be kept all the time. > +Otherwise the results of the array bound sanitizer and the > +@code{__builtin_dynamic_object_size} might be incorrect. > + > +For instance, in the following example, the allocated array has less elements > +than what's specified by the @code{sbuf->count}, this is an user error. As a > +result, out-of-bounds access to the array might not be detected. > + > +@smallexample > +#define SIZE_BUMP 10 > +struct P *sbuf; > +void alloc_buf (size_t nelems) > +@{ > + sbuf = (struct P *) malloc (MAX (sizeof (struct P), > + (offsetof (struct P, array[0]) > + + nelems * sizeof (char)))); > + sbuf->count = nelems + SIZE_BUMP; > + /* This is invalid when the sbuf->array has less than sbuf->count > + elements. */ > +@} > +@end smallexample > + > +In the following example, the 2nd update to the field @code{sbuf->count} of > +the above structure will permit out-of-bounds access to the array > +@code{sbuf>array} as well. > + > +@smallexample > +#define SIZE_BUMP 10 > +struct P *sbuf; > +void alloc_buf (size_t nelems) > +@{ > + sbuf = (struct P *) malloc (MAX (sizeof (struct P), > + (offsetof (struct P, array[0]) > + + (nelems + SIZE_BUMP) * sizeof (char)))); > + sbuf->count = nelems; > + /* This is valid when the sbuf->array has at least sbuf->count > + elements. */ > +@} > +void use_buf (int index) > +@{ > + sbuf->count = sbuf->count + SIZE_BUMP + 1; > + /* Now the value of sbuf->count is larger than the number > + of elements of sbuf->array. */ > + sbuf->array[index] = 0; > + /* then the out-of-bound access to this array > + might not be detected. */ > +@} > +@end smallexample > + > + > @cindex @code{alloc_size} variable attribute > @item alloc_size (@var{position}) > @itemx alloc_size (@var{position-1}, @var{position-2}) > diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by.c b/gcc/testsuite/gcc.dg/flex-array-counted-by.c > new file mode 100644 > index 000000000000..f8ce9776bf86 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by.c > @@ -0,0 +1,40 @@ > +/* testing the correct usage of attribute counted_by. */ > +/* { dg-do compile } */ > +/* { dg-options "-O2" } */ > + > +#include <wchar.h> > + > +int size; > +int x __attribute ((counted_by (size))); /* { dg-error "attribute may not be specified for non-field declaration" } */ > + > +struct trailing { > + int count; > + int field __attribute ((counted_by (count))); /* { dg-error "attribute may not be specified for a non-array field" } */ > +}; > + > +struct trailing_1 { > + int count; > + int array_1[0] __attribute ((counted_by (count))); /* { dg-error "attribute may not be specified for a non flexible array member field" } */ > +}; > + > +int count; > +struct trailing_array_2 { > + int count; > + int array_2[] __attribute ((counted_by ("count"))); /* { dg-error "argument not an identifier" } */ > +}; > + > +struct trailing_array_3 { > + int other; > + int array_3[] __attribute ((counted_by (L"count"))); /* { dg-error "argument not an identifier" } */ > +}; > + > +struct trailing_array_4 { > + int other; > + int array_4[] __attribute ((counted_by (count))); /* { dg-error "attribute argument not a field declaration in the same structure, ignore it" } */ > +}; > + > +int count; > +struct trailing_array_5 { > + float count; > + int array_5[] __attribute ((counted_by (count))); /* { dg-error "attribute argument not a field declaration with integer type, ignore it" } */ > +}; > diff --git a/gcc/tree.cc b/gcc/tree.cc > index 420857b110c4..fcd36ae0cd74 100644 > --- a/gcc/tree.cc > +++ b/gcc/tree.cc > @@ -12745,6 +12745,46 @@ array_ref_element_size (tree exp) > return SUBSTITUTE_PLACEHOLDER_IN_EXPR (TYPE_SIZE_UNIT (elmt_type), exp); > } > > +/* Given a field list, FIELDLIST, of a structure/union, return a TREE_LIST, > + with each TREE_VALUE a FIELD_DECL stepping down the chain to the FIELD > + whose name is FIELDNAME, which is the last TREE_VALUE of the list. > + return NULL_TREE if such field is not found. Normally this list is of > + length one, but if the field is embedded with (nested) anonymous structures > + or unions, this list steps down the chain to the field. */ > +tree > +get_named_field (tree fieldlist, const char *fieldname) > +{ > + tree named_field = NULL_TREE; > + for (tree field = fieldlist; field; field = DECL_CHAIN (field)) > + { > + if (TREE_CODE (field) != FIELD_DECL) > + continue; > + if (DECL_NAME (field) != NULL) > + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (field)), fieldname) == 0) > + { > + named_field = tree_cons (NULL_TREE, field, named_field); > + break; > + } > + else > + continue; > + /* if the field is an anonymous struct/union, we will check the nested > + fields inside it recursively. */ > + else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))) > + if ((named_field = get_named_field (TYPE_FIELDS (TREE_TYPE (field)), > + fieldname)) != NULL_TREE) > + { > + named_field = tree_cons (NULL_TREE, field, named_field); > + break; > + } > + else > + continue; > + else > + continue; > + } > + return named_field; > +} > + > + > /* Return a tree representing the lower bound of the array mentioned in > EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ > > diff --git a/gcc/tree.h b/gcc/tree.h > index 4c04245e2b1b..4859becaa1e7 100644 > --- a/gcc/tree.h > +++ b/gcc/tree.h > @@ -5619,6 +5619,11 @@ extern tree get_base_address (tree t); > of EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ > extern tree array_ref_element_size (tree); > > +/* Given a field list, FIELDLIST, of a structure/union, return the FIELD whose > + name is FIELDNAME, return NULL_TREE if such field is not found. > + searching nested anonymous structure/union recursively. */ > +extern tree get_named_field (tree, const char *); > + > /* Return a typenode for the "standard" C type with a given name. */ > extern tree get_typenode_from_name (const char *); > > -- > 2.31.1 >
Hi, I’d like to ping this patch set one more time. Thanks Qing > On Aug 25, 2023, at 11:24 AM, Qing Zhao <qing.zhao@oracle.com> wrote: > > Provide a new counted_by attribute to flexible array member field. > > 'counted_by (COUNT)' > The 'counted_by' attribute may be attached to the flexible array > member of a structure. It indicates that the number of the > elements of the array is given by the field named "COUNT" in the > same structure as the flexible array member. GCC uses this > information to improve the results of the array bound sanitizer and > the '__builtin_dynamic_object_size'. > > For instance, the following code: > > struct P { > size_t count; > char other; > char array[] __attribute__ ((counted_by (count))); > } *p; > > specifies that the 'array' is a flexible array member whose number > of elements is given by the field 'count' in the same structure. > > The field that represents the number of the elements should have an > integer type. An explicit 'counted_by' annotation defines a > relationship between two objects, 'p->array' and 'p->count', that > 'p->array' has _at least_ 'p->count' number of elements available. > This relationship must hold even after any of these related objects > are updated. It's the user's responsibility to make sure this > relationship to be kept all the time. Otherwise the results of the > array bound sanitizer and the '__builtin_dynamic_object_size' might > be incorrect. > > For instance, in the following example, the allocated array has > less elements than what's specified by the 'sbuf->count', this is > an user error. As a result, out-of-bounds access to the array > might not be detected. > > #define SIZE_BUMP 10 > struct P *sbuf; > void alloc_buf (size_t nelems) > { > sbuf = (struct P *) malloc (MAX (sizeof (struct P), > (offsetof (struct P, array[0]) > + nelems * sizeof (char)))); > sbuf->count = nelems + SIZE_BUMP; > /* This is invalid when the sbuf->array has less than sbuf->count > elements. */ > } > > In the following example, the 2nd update to the field 'sbuf->count' > of the above structure will permit out-of-bounds access to the > array 'sbuf>array' as well. > > #define SIZE_BUMP 10 > struct P *sbuf; > void alloc_buf (size_t nelems) > { > sbuf = (struct P *) malloc (MAX (sizeof (struct P), > (offsetof (struct P, array[0]) > + (nelems + SIZE_BUMP) * sizeof (char)))); > sbuf->count = nelems; > /* This is valid when the sbuf->array has at least sbuf->count > elements. */ > } > void use_buf (int index) > { > sbuf->count = sbuf->count + SIZE_BUMP + 1; > /* Now the value of sbuf->count is larger than the number > of elements of sbuf->array. */ > sbuf->array[index] = 0; > /* then the out-of-bound access to this array > might not be detected. */ > } > > gcc/c-family/ChangeLog: > > PR C/108896 > * c-attribs.cc (handle_counted_by_attribute): New function. > (attribute_takes_identifier_p): Add counted_by attribute to the list. > * c-common.cc (c_flexible_array_member_type_p): ...To this. > * c-common.h (c_flexible_array_member_type_p): New prototype. > > gcc/c/ChangeLog: > > PR C/108896 > * c-decl.cc (flexible_array_member_type_p): Renamed and moved to... > (add_flexible_array_elts_to_size): Use renamed function. > (is_flexible_array_member_p): Use renamed function. > (verify_counted_by_attribute): New function. > (finish_struct): Use renamed function and verify counted_by > attribute. > > gcc/ChangeLog: > > PR C/108896 > * doc/extend.texi: Document attribute counted_by. > * tree.cc (get_named_field): New function. > * tree.h (get_named_field): New prototype. > > gcc/testsuite/ChangeLog: > > PR C/108896 > * gcc.dg/flex-array-counted-by.c: New test. > --- > gcc/c-family/c-attribs.cc | 54 ++++++++++++- > gcc/c-family/c-common.cc | 13 ++++ > gcc/c-family/c-common.h | 1 + > gcc/c/c-decl.cc | 79 +++++++++++++++----- > gcc/doc/extend.texi | 77 +++++++++++++++++++ > gcc/testsuite/gcc.dg/flex-array-counted-by.c | 40 ++++++++++ > gcc/tree.cc | 40 ++++++++++ > gcc/tree.h | 5 ++ > 8 files changed, 291 insertions(+), 18 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/flex-array-counted-by.c > > diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc > index e2792ca6898b..65e4f6639109 100644 > --- a/gcc/c-family/c-attribs.cc > +++ b/gcc/c-family/c-attribs.cc > @@ -103,6 +103,8 @@ static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree, > int, bool *); > static tree handle_strict_flex_array_attribute (tree *, tree, tree, > int, bool *); > +static tree handle_counted_by_attribute (tree *, tree, tree, > + int, bool *); > static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; > static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ; > static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *); > @@ -373,6 +375,8 @@ const struct attribute_spec c_common_attribute_table[] = > handle_warn_if_not_aligned_attribute, NULL }, > { "strict_flex_array", 1, 1, true, false, false, false, > handle_strict_flex_array_attribute, NULL }, > + { "counted_by", 1, 1, true, false, false, false, > + handle_counted_by_attribute, NULL }, > { "weak", 0, 0, true, false, false, false, > handle_weak_attribute, NULL }, > { "noplt", 0, 0, true, false, false, false, > @@ -601,7 +605,8 @@ attribute_takes_identifier_p (const_tree attr_id) > else if (!strcmp ("mode", spec->name) > || !strcmp ("format", spec->name) > || !strcmp ("cleanup", spec->name) > - || !strcmp ("access", spec->name)) > + || !strcmp ("access", spec->name) > + || !strcmp ("counted_by", spec->name)) > return true; > else > return targetm.attribute_takes_identifier_p (attr_id); > @@ -2555,6 +2560,53 @@ handle_strict_flex_array_attribute (tree *node, tree name, > return NULL_TREE; > } > > +/* Handle a "counted_by" attribute; arguments as in > + struct attribute_spec.handler. */ > + > +static tree > +handle_counted_by_attribute (tree *node, tree name, > + tree args, int ARG_UNUSED (flags), > + bool *no_add_attrs) > +{ > + tree decl = *node; > + tree argval = TREE_VALUE (args); > + > + /* This attribute only applies to field decls of a structure. */ > + if (TREE_CODE (decl) != FIELD_DECL) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qE attribute may not be specified for non-field" > + " declaration %q+D", name, decl); > + *no_add_attrs = true; > + } > + /* This attribute only applies to field with array type. */ > + else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qE attribute may not be specified for a non-array field", > + name); > + *no_add_attrs = true; > + } > + /* This attribute only applies to a C99 flexible array member type. */ > + else if (! c_flexible_array_member_type_p (TREE_TYPE (decl))) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qE attribute may not be specified for a non" > + " flexible array member field", > + name); > + *no_add_attrs = true; > + } > + /* The argument should be an identifier. */ > + else if (TREE_CODE (argval) != IDENTIFIER_NODE) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%<counted_by%> argument not an identifier"); > + *no_add_attrs = true; > + } > + > + return NULL_TREE; > +} > + > /* Handle a "weak" attribute; arguments as in > struct attribute_spec.handler. */ > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc > index 9fbaeb437a12..a18937245c2a 100644 > --- a/gcc/c-family/c-common.cc > +++ b/gcc/c-family/c-common.cc > @@ -9521,6 +9521,19 @@ c_common_finalize_early_debug (void) > (*debug_hooks->early_global_decl) (cnode->decl); > } > > +/* Determine whether TYPE is a ISO C99 flexible array memeber type "[]". */ > +bool > +c_flexible_array_member_type_p (const_tree type) > +{ > + if (TREE_CODE (type) == ARRAY_TYPE > + && TYPE_SIZE (type) == NULL_TREE > + && TYPE_DOMAIN (type) != NULL_TREE > + && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) > + return true; > + > + return false; > +} > + > /* Get the LEVEL of the strict_flex_array for the ARRAY_FIELD based on the > values of attribute strict_flex_array and the flag_strict_flex_arrays. */ > unsigned int > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h > index 78fc5248ba68..c29bb429062b 100644 > --- a/gcc/c-family/c-common.h > +++ b/gcc/c-family/c-common.h > @@ -909,6 +909,7 @@ extern tree fold_for_warn (tree); > extern tree c_common_get_narrower (tree, int *); > extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *); > extern void c_common_finalize_early_debug (void); > +extern bool c_flexible_array_member_type_p (const_tree); > extern unsigned int c_strict_flex_array_level_of (tree); > extern bool c_option_is_from_cpp_diagnostics (int); > > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc > index 1f9eb44dbaa2..e943b49b5230 100644 > --- a/gcc/c/c-decl.cc > +++ b/gcc/c/c-decl.cc > @@ -5173,19 +5173,6 @@ set_array_declarator_inner (struct c_declarator *decl, > return decl; > } > > -/* Determine whether TYPE is a ISO C99 flexible array memeber type "[]". */ > -static bool > -flexible_array_member_type_p (const_tree type) > -{ > - if (TREE_CODE (type) == ARRAY_TYPE > - && TYPE_SIZE (type) == NULL_TREE > - && TYPE_DOMAIN (type) != NULL_TREE > - && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) > - return true; > - > - return false; > -} > - > /* Determine whether TYPE is a one-element array type "[1]". */ > static bool > one_element_array_type_p (const_tree type) > @@ -5222,7 +5209,7 @@ add_flexible_array_elts_to_size (tree decl, tree init) > > elt = CONSTRUCTOR_ELTS (init)->last ().value; > type = TREE_TYPE (elt); > - if (flexible_array_member_type_p (type)) > + if (c_flexible_array_member_type_p (type)) > { > complete_array_type (&type, elt, false); > DECL_SIZE (decl) > @@ -9094,7 +9081,7 @@ is_flexible_array_member_p (bool is_last_field, > > bool is_zero_length_array = zero_length_array_type_p (TREE_TYPE (x)); > bool is_one_element_array = one_element_array_type_p (TREE_TYPE (x)); > - bool is_flexible_array = flexible_array_member_type_p (TREE_TYPE (x)); > + bool is_flexible_array = c_flexible_array_member_type_p (TREE_TYPE (x)); > > unsigned int strict_flex_array_level = c_strict_flex_array_level_of (x); > > @@ -9124,6 +9111,61 @@ is_flexible_array_member_p (bool is_last_field, > return false; > } > > +/* Verify the argument of the counted_by attribute of the flexible array > + member FIELD_DECL is a valid field of the containing structure's fieldlist, > + FIELDLIST, Report error and remove this attribute when it's not. */ > +static void > +verify_counted_by_attribute (tree fieldlist, tree field_decl) > +{ > + tree attr_counted_by = lookup_attribute ("counted_by", > + DECL_ATTRIBUTES (field_decl)); > + > + if (!attr_counted_by) > + return; > + > + /* If there is an counted_by attribute attached to the field, > + verify it. */ > + > + const char *fieldname > + = IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (attr_counted_by))); > + > + /* Verify the argument of the attrbute is a valid field of the > + containing structure. */ > + > + tree counted_by_field = get_named_field (fieldlist, fieldname); > + > + /* Error when the field is not found in the containing structure. */ > + if (!counted_by_field) > + { > + error_at (DECL_SOURCE_LOCATION (field_decl), > + "%qE attribute argument not a field declaration" > + " in the same structure, ignore it", > + (get_attribute_name (attr_counted_by))); > + > + 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 (TREE_CODE (TREE_TYPE (real_field)) != INTEGER_TYPE) > + { > + error_at (DECL_SOURCE_LOCATION (field_decl), > + "%qE attribute argument not a field declaration" > + " with integer type, ignore it", > + (get_attribute_name (attr_counted_by))); > + > + DECL_ATTRIBUTES (field_decl) > + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); > + } > + } > + > + return; > +} > > /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T. > LOC is the location of the RECORD_TYPE or UNION_TYPE's definition. > @@ -9244,7 +9286,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, > DECL_PACKED (x) = 1; > > /* Detect flexible array member in an invalid context. */ > - if (flexible_array_member_type_p (TREE_TYPE (x))) > + if (c_flexible_array_member_type_p (TREE_TYPE (x))) > { > if (TREE_CODE (t) == UNION_TYPE) > { > @@ -9265,6 +9307,9 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, > "members"); > TREE_TYPE (x) = error_mark_node; > } > + /* if there is a counted_by attribute attached to this field, > + verify it. */ > + verify_counted_by_attribute (fieldlist, x); > } > > if (pedantic && TREE_CODE (t) == RECORD_TYPE > @@ -9279,7 +9324,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, > when x is an array and is the last field. */ > if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE) > TYPE_INCLUDES_FLEXARRAY (t) > - = is_last_field && flexible_array_member_type_p (TREE_TYPE (x)); > + = is_last_field && c_flexible_array_member_type_p (TREE_TYPE (x)); > /* Recursively set TYPE_INCLUDES_FLEXARRAY for the context of x, t > when x is an union or record and is the last field. */ > else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (x))) > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > index 97eaacf8a7ec..ea6240646936 100644 > --- a/gcc/doc/extend.texi > +++ b/gcc/doc/extend.texi > @@ -7617,6 +7617,83 @@ When both the attribute and the option present at the same time, the level of > the strictness for the specific trailing array field is determined by the > attribute. > > +@cindex @code{counted_by} variable attribute > +@item counted_by (@var{count}) > +The @code{counted_by} attribute may be attached to the flexible array > +member of a structure. It indicates that the number of the elements of the > +array is given by the field named "@var{count}" in the same structure as the > +flexible array member. GCC uses this information to improve the results of > +the array bound sanitizer and the @code{__builtin_dynamic_object_size}. > + > +For instance, the following code: > + > +@smallexample > +struct P @{ > + size_t count; > + char other; > + char array[] __attribute__ ((counted_by (count))); > +@} *p; > +@end smallexample > + > +@noindent > +specifies that the @code{array} is a flexible array member whose number of > +elements is given by the field @code{count} in the same structure. > + > +The field that represents the number of the elements should have an integer > +type. An explicit @code{counted_by} annotation defines a relationship between > +two objects, @code{p->array} and @code{p->count}, that @code{p->array} has > +@emph{at least} @code{p->count} number of elements available. This relationship > +must hold even after any of these related objects are updated. It's the user's > +responsibility to make sure this relationship to be kept all the time. > +Otherwise the results of the array bound sanitizer and the > +@code{__builtin_dynamic_object_size} might be incorrect. > + > +For instance, in the following example, the allocated array has less elements > +than what's specified by the @code{sbuf->count}, this is an user error. As a > +result, out-of-bounds access to the array might not be detected. > + > +@smallexample > +#define SIZE_BUMP 10 > +struct P *sbuf; > +void alloc_buf (size_t nelems) > +@{ > + sbuf = (struct P *) malloc (MAX (sizeof (struct P), > + (offsetof (struct P, array[0]) > + + nelems * sizeof (char)))); > + sbuf->count = nelems + SIZE_BUMP; > + /* This is invalid when the sbuf->array has less than sbuf->count > + elements. */ > +@} > +@end smallexample > + > +In the following example, the 2nd update to the field @code{sbuf->count} of > +the above structure will permit out-of-bounds access to the array > +@code{sbuf>array} as well. > + > +@smallexample > +#define SIZE_BUMP 10 > +struct P *sbuf; > +void alloc_buf (size_t nelems) > +@{ > + sbuf = (struct P *) malloc (MAX (sizeof (struct P), > + (offsetof (struct P, array[0]) > + + (nelems + SIZE_BUMP) * sizeof (char)))); > + sbuf->count = nelems; > + /* This is valid when the sbuf->array has at least sbuf->count > + elements. */ > +@} > +void use_buf (int index) > +@{ > + sbuf->count = sbuf->count + SIZE_BUMP + 1; > + /* Now the value of sbuf->count is larger than the number > + of elements of sbuf->array. */ > + sbuf->array[index] = 0; > + /* then the out-of-bound access to this array > + might not be detected. */ > +@} > +@end smallexample > + > + > @cindex @code{alloc_size} variable attribute > @item alloc_size (@var{position}) > @itemx alloc_size (@var{position-1}, @var{position-2}) > diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by.c b/gcc/testsuite/gcc.dg/flex-array-counted-by.c > new file mode 100644 > index 000000000000..f8ce9776bf86 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by.c > @@ -0,0 +1,40 @@ > +/* testing the correct usage of attribute counted_by. */ > +/* { dg-do compile } */ > +/* { dg-options "-O2" } */ > + > +#include <wchar.h> > + > +int size; > +int x __attribute ((counted_by (size))); /* { dg-error "attribute may not be specified for non-field declaration" } */ > + > +struct trailing { > + int count; > + int field __attribute ((counted_by (count))); /* { dg-error "attribute may not be specified for a non-array field" } */ > +}; > + > +struct trailing_1 { > + int count; > + int array_1[0] __attribute ((counted_by (count))); /* { dg-error "attribute may not be specified for a non flexible array member field" } */ > +}; > + > +int count; > +struct trailing_array_2 { > + int count; > + int array_2[] __attribute ((counted_by ("count"))); /* { dg-error "argument not an identifier" } */ > +}; > + > +struct trailing_array_3 { > + int other; > + int array_3[] __attribute ((counted_by (L"count"))); /* { dg-error "argument not an identifier" } */ > +}; > + > +struct trailing_array_4 { > + int other; > + int array_4[] __attribute ((counted_by (count))); /* { dg-error "attribute argument not a field declaration in the same structure, ignore it" } */ > +}; > + > +int count; > +struct trailing_array_5 { > + float count; > + int array_5[] __attribute ((counted_by (count))); /* { dg-error "attribute argument not a field declaration with integer type, ignore it" } */ > +}; > diff --git a/gcc/tree.cc b/gcc/tree.cc > index 420857b110c4..fcd36ae0cd74 100644 > --- a/gcc/tree.cc > +++ b/gcc/tree.cc > @@ -12745,6 +12745,46 @@ array_ref_element_size (tree exp) > return SUBSTITUTE_PLACEHOLDER_IN_EXPR (TYPE_SIZE_UNIT (elmt_type), exp); > } > > +/* Given a field list, FIELDLIST, of a structure/union, return a TREE_LIST, > + with each TREE_VALUE a FIELD_DECL stepping down the chain to the FIELD > + whose name is FIELDNAME, which is the last TREE_VALUE of the list. > + return NULL_TREE if such field is not found. Normally this list is of > + length one, but if the field is embedded with (nested) anonymous structures > + or unions, this list steps down the chain to the field. */ > +tree > +get_named_field (tree fieldlist, const char *fieldname) > +{ > + tree named_field = NULL_TREE; > + for (tree field = fieldlist; field; field = DECL_CHAIN (field)) > + { > + if (TREE_CODE (field) != FIELD_DECL) > + continue; > + if (DECL_NAME (field) != NULL) > + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (field)), fieldname) == 0) > + { > + named_field = tree_cons (NULL_TREE, field, named_field); > + break; > + } > + else > + continue; > + /* if the field is an anonymous struct/union, we will check the nested > + fields inside it recursively. */ > + else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))) > + if ((named_field = get_named_field (TYPE_FIELDS (TREE_TYPE (field)), > + fieldname)) != NULL_TREE) > + { > + named_field = tree_cons (NULL_TREE, field, named_field); > + break; > + } > + else > + continue; > + else > + continue; > + } > + return named_field; > +} > + > + > /* Return a tree representing the lower bound of the array mentioned in > EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ > > diff --git a/gcc/tree.h b/gcc/tree.h > index 4c04245e2b1b..4859becaa1e7 100644 > --- a/gcc/tree.h > +++ b/gcc/tree.h > @@ -5619,6 +5619,11 @@ extern tree get_base_address (tree t); > of EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ > extern tree array_ref_element_size (tree); > > +/* Given a field list, FIELDLIST, of a structure/union, return the FIELD whose > + name is FIELDNAME, return NULL_TREE if such field is not found. > + searching nested anonymous structure/union recursively. */ > +extern tree get_named_field (tree, const char *); > + > /* Return a typenode for the "standard" C type with a given name. */ > extern tree get_typenode_from_name (const char *); > > -- > 2.31.1 >
On 2023-08-25 11:24, Qing Zhao wrote: > Provide a new counted_by attribute to flexible array member field. The obligatory "I can't ack the patch but here's a review" disclaimer :) > > 'counted_by (COUNT)' > The 'counted_by' attribute may be attached to the flexible array > member of a structure. It indicates that the number of the > elements of the array is given by the field named "COUNT" in the > same structure as the flexible array member. GCC uses this > information to improve the results of the array bound sanitizer and > the '__builtin_dynamic_object_size'. > > For instance, the following code: > > struct P { > size_t count; > char other; > char array[] __attribute__ ((counted_by (count))); > } *p; > > specifies that the 'array' is a flexible array member whose number > of elements is given by the field 'count' in the same structure. > > The field that represents the number of the elements should have an > integer type. An explicit 'counted_by' annotation defines a > relationship between two objects, 'p->array' and 'p->count', that > 'p->array' has _at least_ 'p->count' number of elements available. > This relationship must hold even after any of these related objects > are updated. It's the user's responsibility to make sure this > relationship to be kept all the time. Otherwise the results of the > array bound sanitizer and the '__builtin_dynamic_object_size' might > be incorrect. > > For instance, in the following example, the allocated array has > less elements than what's specified by the 'sbuf->count', this is > an user error. As a result, out-of-bounds access to the array > might not be detected. > > #define SIZE_BUMP 10 > struct P *sbuf; > void alloc_buf (size_t nelems) > { > sbuf = (struct P *) malloc (MAX (sizeof (struct P), > (offsetof (struct P, array[0]) > + nelems * sizeof (char)))); > sbuf->count = nelems + SIZE_BUMP; > /* This is invalid when the sbuf->array has less than sbuf->count > elements. */ > } > > In the following example, the 2nd update to the field 'sbuf->count' > of the above structure will permit out-of-bounds access to the > array 'sbuf>array' as well. > > #define SIZE_BUMP 10 > struct P *sbuf; > void alloc_buf (size_t nelems) > { > sbuf = (struct P *) malloc (MAX (sizeof (struct P), > (offsetof (struct P, array[0]) > + (nelems + SIZE_BUMP) * sizeof (char)))); > sbuf->count = nelems; > /* This is valid when the sbuf->array has at least sbuf->count > elements. */ > } > void use_buf (int index) > { > sbuf->count = sbuf->count + SIZE_BUMP + 1; > /* Now the value of sbuf->count is larger than the number > of elements of sbuf->array. */ > sbuf->array[index] = 0; > /* then the out-of-bound access to this array > might not be detected. */ > } > > gcc/c-family/ChangeLog: > > PR C/108896 > * c-attribs.cc (handle_counted_by_attribute): New function. > (attribute_takes_identifier_p): Add counted_by attribute to the list. > * c-common.cc (c_flexible_array_member_type_p): ...To this. > * c-common.h (c_flexible_array_member_type_p): New prototype. > > gcc/c/ChangeLog: > > PR C/108896 > * c-decl.cc (flexible_array_member_type_p): Renamed and moved to... > (add_flexible_array_elts_to_size): Use renamed function. > (is_flexible_array_member_p): Use renamed function. > (verify_counted_by_attribute): New function. > (finish_struct): Use renamed function and verify counted_by > attribute. > > gcc/ChangeLog: > > PR C/108896 > * doc/extend.texi: Document attribute counted_by. > * tree.cc (get_named_field): New function. > * tree.h (get_named_field): New prototype. > > gcc/testsuite/ChangeLog: > > PR C/108896 > * gcc.dg/flex-array-counted-by.c: New test. > --- > gcc/c-family/c-attribs.cc | 54 ++++++++++++- > gcc/c-family/c-common.cc | 13 ++++ > gcc/c-family/c-common.h | 1 + > gcc/c/c-decl.cc | 79 +++++++++++++++----- > gcc/doc/extend.texi | 77 +++++++++++++++++++ > gcc/testsuite/gcc.dg/flex-array-counted-by.c | 40 ++++++++++ > gcc/tree.cc | 40 ++++++++++ > gcc/tree.h | 5 ++ > 8 files changed, 291 insertions(+), 18 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/flex-array-counted-by.c > > diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc > index e2792ca6898b..65e4f6639109 100644 > --- a/gcc/c-family/c-attribs.cc > +++ b/gcc/c-family/c-attribs.cc > @@ -103,6 +103,8 @@ static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree, > int, bool *); > static tree handle_strict_flex_array_attribute (tree *, tree, tree, > int, bool *); > +static tree handle_counted_by_attribute (tree *, tree, tree, > + int, bool *); > static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; > static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ; > static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *); > @@ -373,6 +375,8 @@ const struct attribute_spec c_common_attribute_table[] = > handle_warn_if_not_aligned_attribute, NULL }, > { "strict_flex_array", 1, 1, true, false, false, false, > handle_strict_flex_array_attribute, NULL }, > + { "counted_by", 1, 1, true, false, false, false, > + handle_counted_by_attribute, NULL }, > { "weak", 0, 0, true, false, false, false, > handle_weak_attribute, NULL }, > { "noplt", 0, 0, true, false, false, false, > @@ -601,7 +605,8 @@ attribute_takes_identifier_p (const_tree attr_id) > else if (!strcmp ("mode", spec->name) > || !strcmp ("format", spec->name) > || !strcmp ("cleanup", spec->name) > - || !strcmp ("access", spec->name)) > + || !strcmp ("access", spec->name) > + || !strcmp ("counted_by", spec->name)) > return true; > else > return targetm.attribute_takes_identifier_p (attr_id); > @@ -2555,6 +2560,53 @@ handle_strict_flex_array_attribute (tree *node, tree name, > return NULL_TREE; > } > > +/* Handle a "counted_by" attribute; arguments as in > + struct attribute_spec.handler. */ > + > +static tree > +handle_counted_by_attribute (tree *node, tree name, > + tree args, int ARG_UNUSED (flags), > + bool *no_add_attrs) > +{ > + tree decl = *node; > + tree argval = TREE_VALUE (args); > + > + /* This attribute only applies to field decls of a structure. */ > + if (TREE_CODE (decl) != FIELD_DECL) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qE attribute may not be specified for non-field" > + " declaration %q+D", name, decl); > + *no_add_attrs = true; > + } Applies only to struct fields. OK. > + /* This attribute only applies to field with array type. */ > + else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qE attribute may not be specified for a non-array field", > + name); > + *no_add_attrs = true; > + } The struct field should also be an array. OK. > + /* This attribute only applies to a C99 flexible array member type. */ > + else if (! c_flexible_array_member_type_p (TREE_TYPE (decl))) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%qE attribute may not be specified for a non" > + " flexible array member field", > + name); > + *no_add_attrs = true; > + } Additionally, the field should be a *flex* array. OK. Could this be reworded to: %qE attribute only applies to C99 flexible array members That would make it clear that the GNU extension flex arrays (i.e. any last array member of a struct) don't support this attribute. > + /* The argument should be an identifier. */ > + else if (TREE_CODE (argval) != IDENTIFIER_NODE) > + { > + error_at (DECL_SOURCE_LOCATION (decl), > + "%<counted_by%> argument not an identifier"); > + *no_add_attrs = true; > + } Argument should be an identifier, and the check for validity of the identifier comes later in finish_struct. OK. > + > + return NULL_TREE; > +} > + > /* Handle a "weak" attribute; arguments as in > struct attribute_spec.handler. */ > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc > index 9fbaeb437a12..a18937245c2a 100644 > --- a/gcc/c-family/c-common.cc > +++ b/gcc/c-family/c-common.cc > @@ -9521,6 +9521,19 @@ c_common_finalize_early_debug (void) > (*debug_hooks->early_global_decl) (cnode->decl); > } > > +/* Determine whether TYPE is a ISO C99 flexible array memeber type "[]". */ > +bool > +c_flexible_array_member_type_p (const_tree type) > +{ > + if (TREE_CODE (type) == ARRAY_TYPE > + && TYPE_SIZE (type) == NULL_TREE > + && TYPE_DOMAIN (type) != NULL_TREE > + && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) > + return true; > + > + return false; > +} > + Hoist flexible_array_member_type_p to use more widely. OK. > /* Get the LEVEL of the strict_flex_array for the ARRAY_FIELD based on the > values of attribute strict_flex_array and the flag_strict_flex_arrays. */ > unsigned int > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h > index 78fc5248ba68..c29bb429062b 100644 > --- a/gcc/c-family/c-common.h > +++ b/gcc/c-family/c-common.h > @@ -909,6 +909,7 @@ extern tree fold_for_warn (tree); > extern tree c_common_get_narrower (tree, int *); > extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *); > extern void c_common_finalize_early_debug (void); > +extern bool c_flexible_array_member_type_p (const_tree); > extern unsigned int c_strict_flex_array_level_of (tree); > extern bool c_option_is_from_cpp_diagnostics (int); > > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc > index 1f9eb44dbaa2..e943b49b5230 100644 > --- a/gcc/c/c-decl.cc > +++ b/gcc/c/c-decl.cc > @@ -5173,19 +5173,6 @@ set_array_declarator_inner (struct c_declarator *decl, > return decl; > } > > -/* Determine whether TYPE is a ISO C99 flexible array memeber type "[]". */ > -static bool > -flexible_array_member_type_p (const_tree type) > -{ > - if (TREE_CODE (type) == ARRAY_TYPE > - && TYPE_SIZE (type) == NULL_TREE > - && TYPE_DOMAIN (type) != NULL_TREE > - && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) > - return true; > - > - return false; > -} > - > /* Determine whether TYPE is a one-element array type "[1]". */ > static bool > one_element_array_type_p (const_tree type) > @@ -5222,7 +5209,7 @@ add_flexible_array_elts_to_size (tree decl, tree init) > > elt = CONSTRUCTOR_ELTS (init)->last ().value; > type = TREE_TYPE (elt); > - if (flexible_array_member_type_p (type)) > + if (c_flexible_array_member_type_p (type)) > { > complete_array_type (&type, elt, false); > DECL_SIZE (decl) > @@ -9094,7 +9081,7 @@ is_flexible_array_member_p (bool is_last_field, > > bool is_zero_length_array = zero_length_array_type_p (TREE_TYPE (x)); > bool is_one_element_array = one_element_array_type_p (TREE_TYPE (x)); > - bool is_flexible_array = flexible_array_member_type_p (TREE_TYPE (x)); > + bool is_flexible_array = c_flexible_array_member_type_p (TREE_TYPE (x)); > > unsigned int strict_flex_array_level = c_strict_flex_array_level_of (x); > Simple refactoring. OK. > @@ -9124,6 +9111,61 @@ is_flexible_array_member_p (bool is_last_field, > return false; > } > > +/* Verify the argument of the counted_by attribute of the flexible array Verify *that* the argument... > + member FIELD_DECL is a valid field of the containing structure's fieldlist, > + FIELDLIST, Report error and remove this attribute when it's not. */ > +static void > +verify_counted_by_attribute (tree fieldlist, tree field_decl) > +{ > + tree attr_counted_by = lookup_attribute ("counted_by", > + DECL_ATTRIBUTES (field_decl)); > + > + if (!attr_counted_by) > + return; > + > + /* If there is an counted_by attribute attached to the field, > + verify it. */ > + > + const char *fieldname > + = IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (attr_counted_by))); > + > + /* Verify the argument of the attrbute is a valid field of the s/attrbute/attribute/ > + containing structure. */ > + > + tree counted_by_field = get_named_field (fieldlist, fieldname); > + > + /* Error when the field is not found in the containing structure. */ > + if (!counted_by_field) > + { > + error_at (DECL_SOURCE_LOCATION (field_decl), > + "%qE attribute argument not a field declaration" > + " in the same structure, ignore it", > + (get_attribute_name (attr_counted_by))); Probably someone with English as a first language would make a better suggestion, but how about: Argument specified in %qE attribute is not a field declaration in the same structure, ignoring it. > + > + DECL_ATTRIBUTES (field_decl) > + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); > + } > + else > + /* Error when the field is not with an integer type. */ Suggest: Flag an error when the field is not of 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 (TREE_CODE (TREE_TYPE (real_field)) != INTEGER_TYPE) > + { > + error_at (DECL_SOURCE_LOCATION (field_decl), > + "%qE attribute argument not a field declaration" > + " with integer type, ignore it", > + (get_attribute_name (attr_counted_by))); Suggest: Argument specified in %qE attribute is not of an integer type, ignoring it. > + > + DECL_ATTRIBUTES (field_decl) > + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); > + } > + } > + > + return; > +} > > /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T. > LOC is the location of the RECORD_TYPE or UNION_TYPE's definition. > @@ -9244,7 +9286,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, > DECL_PACKED (x) = 1; > > /* Detect flexible array member in an invalid context. */ > - if (flexible_array_member_type_p (TREE_TYPE (x))) > + if (c_flexible_array_member_type_p (TREE_TYPE (x))) > { > if (TREE_CODE (t) == UNION_TYPE) > { > @@ -9265,6 +9307,9 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, > "members"); > TREE_TYPE (x) = error_mark_node; > } > + /* if there is a counted_by attribute attached to this field, > + verify it. */ > + verify_counted_by_attribute (fieldlist, x); > } > > if (pedantic && TREE_CODE (t) == RECORD_TYPE > @@ -9279,7 +9324,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, > when x is an array and is the last field. */ > if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE) > TYPE_INCLUDES_FLEXARRAY (t) > - = is_last_field && flexible_array_member_type_p (TREE_TYPE (x)); > + = is_last_field && c_flexible_array_member_type_p (TREE_TYPE (x)); > /* Recursively set TYPE_INCLUDES_FLEXARRAY for the context of x, t > when x is an union or record and is the last field. */ > else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (x))) > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > index 97eaacf8a7ec..ea6240646936 100644 > --- a/gcc/doc/extend.texi > +++ b/gcc/doc/extend.texi > @@ -7617,6 +7617,83 @@ When both the attribute and the option present at the same time, the level of > the strictness for the specific trailing array field is determined by the > attribute. > > +@cindex @code{counted_by} variable attribute > +@item counted_by (@var{count}) > +The @code{counted_by} attribute may be attached to the flexible array > +member of a structure. It indicates that the number of the elements of the > +array is given by the field named "@var{count}" in the same structure as the > +flexible array member. GCC uses this information to improve the results of > +the array bound sanitizer and the @code{__builtin_dynamic_object_size}. Maybe specify somehow that this only applies to C99 flexible arrays? Like: The @code{counted_by} attribute may be attached to the C99 flexible array member of a structure... > + > +For instance, the following code: > + > +@smallexample > +struct P @{ > + size_t count; > + char other; > + char array[] __attribute__ ((counted_by (count))); > +@} *p; > +@end smallexample > + > +@noindent > +specifies that the @code{array} is a flexible array member whose number of > +elements is given by the field @code{count} in the same structure. > + > +The field that represents the number of the elements should have an integer > +type. An explicit @code{counted_by} annotation defines a relationship between > +two objects, @code{p->array} and @code{p->count}, that @code{p->array} has > +@emph{at least} @code{p->count} number of elements available. This relationship > +must hold even after any of these related objects are updated. It's the user's > +responsibility to make sure this relationship to be kept all the time. > +Otherwise the results of the array bound sanitizer and the > +@code{__builtin_dynamic_object_size} might be incorrect. Suggest: The field that represents the number of the elements must have an integer type. An explicit @code{counted_by} annotation defines a relationship between two objects, @code{p->array} and @code{p->count}, that @code{p->array} has @emph{at least} @code{p->count} number of elements available. The user is responsible to ensure that this relationship is consistently maintained during the lifetime of the object. Failure to do so may result in results of the array bound sanitizer and the @code{__builtin_dynamic_object_size} being undefined. > + > +For instance, in the following example, the allocated array has less elements > +than what's specified by the @code{sbuf->count}, this is an user error. As a > +result, out-of-bounds access to the array might not be detected. > + > +@smallexample > +#define SIZE_BUMP 10 > +struct P *sbuf; > +void alloc_buf (size_t nelems) > +@{ > + sbuf = (struct P *) malloc (MAX (sizeof (struct P), > + (offsetof (struct P, array[0]) > + + nelems * sizeof (char)))); > + sbuf->count = nelems + SIZE_BUMP; > + /* This is invalid when the sbuf->array has less than sbuf->count > + elements. */ > +@} > +@end smallexample > + > +In the following example, the 2nd update to the field @code{sbuf->count} of > +the above structure will permit out-of-bounds access to the array > +@code{sbuf>array} as well. > + > +@smallexample > +#define SIZE_BUMP 10 > +struct P *sbuf; > +void alloc_buf (size_t nelems) > +@{ > + sbuf = (struct P *) malloc (MAX (sizeof (struct P), > + (offsetof (struct P, array[0]) > + + (nelems + SIZE_BUMP) * sizeof (char)))); > + sbuf->count = nelems; > + /* This is valid when the sbuf->array has at least sbuf->count > + elements. */ > +@} > +void use_buf (int index) > +@{ > + sbuf->count = sbuf->count + SIZE_BUMP + 1; > + /* Now the value of sbuf->count is larger than the number > + of elements of sbuf->array. */ > + sbuf->array[index] = 0; > + /* then the out-of-bound access to this array > + might not be detected. */ > +@} > +@end smallexample > + > + I'm unsure if we should be demonstrating example code with undefined behaviour. Maybe the documentation is better off without it. > @cindex @code{alloc_size} variable attribute > @item alloc_size (@var{position}) > @itemx alloc_size (@var{position-1}, @var{position-2}) > diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by.c b/gcc/testsuite/gcc.dg/flex-array-counted-by.c > new file mode 100644 > index 000000000000..f8ce9776bf86 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by.c > @@ -0,0 +1,40 @@ > +/* testing the correct usage of attribute counted_by. */ > +/* { dg-do compile } */ > +/* { dg-options "-O2" } */ > + > +#include <wchar.h> > + > +int size; > +int x __attribute ((counted_by (size))); /* { dg-error "attribute may not be specified for non-field declaration" } */ > + > +struct trailing { > + int count; > + int field __attribute ((counted_by (count))); /* { dg-error "attribute may not be specified for a non-array field" } */ > +}; > + > +struct trailing_1 { > + int count; > + int array_1[0] __attribute ((counted_by (count))); /* { dg-error "attribute may not be specified for a non flexible array member field" } */ > +}; > + > +int count; > +struct trailing_array_2 { > + int count; > + int array_2[] __attribute ((counted_by ("count"))); /* { dg-error "argument not an identifier" } */ > +}; > + > +struct trailing_array_3 { > + int other; > + int array_3[] __attribute ((counted_by (L"count"))); /* { dg-error "argument not an identifier" } */ > +}; > + > +struct trailing_array_4 { > + int other; > + int array_4[] __attribute ((counted_by (count))); /* { dg-error "attribute argument not a field declaration in the same structure, ignore it" } */ > +}; > + > +int count; > +struct trailing_array_5 { > + float count; > + int array_5[] __attribute ((counted_by (count))); /* { dg-error "attribute argument not a field declaration with integer type, ignore it" } */ > +}; Tests look OK in principle, but may need updating for the error message. > diff --git a/gcc/tree.cc b/gcc/tree.cc > index 420857b110c4..fcd36ae0cd74 100644 > --- a/gcc/tree.cc > +++ b/gcc/tree.cc > @@ -12745,6 +12745,46 @@ array_ref_element_size (tree exp) > return SUBSTITUTE_PLACEHOLDER_IN_EXPR (TYPE_SIZE_UNIT (elmt_type), exp); > } > > +/* Given a field list, FIELDLIST, of a structure/union, return a TREE_LIST, > + with each TREE_VALUE a FIELD_DECL stepping down the chain to the FIELD > + whose name is FIELDNAME, which is the last TREE_VALUE of the list. > + return NULL_TREE if such field is not found. Normally this list is of > + length one, but if the field is embedded with (nested) anonymous structures > + or unions, this list steps down the chain to the field. */ > +tree > +get_named_field (tree fieldlist, const char *fieldname) > +{ > + tree named_field = NULL_TREE; > + for (tree field = fieldlist; field; field = DECL_CHAIN (field)) > + { > + if (TREE_CODE (field) != FIELD_DECL) > + continue; > + if (DECL_NAME (field) != NULL) > + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (field)), fieldname) == 0) > + { > + named_field = tree_cons (NULL_TREE, field, named_field); > + break; > + } > + else > + continue; > + /* if the field is an anonymous struct/union, we will check the nested > + fields inside it recursively. */ > + else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))) > + if ((named_field = get_named_field (TYPE_FIELDS (TREE_TYPE (field)), > + fieldname)) != NULL_TREE) > + { > + named_field = tree_cons (NULL_TREE, field, named_field); > + break; > + } > + else > + continue; > + else > + continue; > + } > + return named_field; > +} Descending recursively into the field list of a struct to find an identifier with a matching name. OK. > + > + > /* Return a tree representing the lower bound of the array mentioned in > EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ > > diff --git a/gcc/tree.h b/gcc/tree.h > index 4c04245e2b1b..4859becaa1e7 100644 > --- a/gcc/tree.h > +++ b/gcc/tree.h > @@ -5619,6 +5619,11 @@ extern tree get_base_address (tree t); > of EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ > extern tree array_ref_element_size (tree); > > +/* Given a field list, FIELDLIST, of a structure/union, return the FIELD whose > + name is FIELDNAME, return NULL_TREE if such field is not found. > + searching nested anonymous structure/union recursively. */ > +extern tree get_named_field (tree, const char *); > + > /* Return a typenode for the "standard" C type with a given name. */ > extern tree get_typenode_from_name (const char *); >
On 2023-10-05 14:51, Siddhesh Poyarekar wrote: > On 2023-08-25 11:24, Qing Zhao wrote: >> Provide a new counted_by attribute to flexible array member field. > > The obligatory "I can't ack the patch but here's a review" disclaimer :) > >> >> 'counted_by (COUNT)' >> The 'counted_by' attribute may be attached to the flexible array >> member of a structure. It indicates that the number of the >> elements of the array is given by the field named "COUNT" in the >> same structure as the flexible array member. GCC uses this >> information to improve the results of the array bound sanitizer and >> the '__builtin_dynamic_object_size'. >> >> For instance, the following code: >> >> struct P { >> size_t count; >> char other; >> char array[] __attribute__ ((counted_by (count))); >> } *p; >> >> specifies that the 'array' is a flexible array member whose number >> of elements is given by the field 'count' in the same structure. >> >> The field that represents the number of the elements should have an >> integer type. An explicit 'counted_by' annotation defines a >> relationship between two objects, 'p->array' and 'p->count', that >> 'p->array' has _at least_ 'p->count' number of elements available. >> This relationship must hold even after any of these related objects >> are updated. It's the user's responsibility to make sure this >> relationship to be kept all the time. Otherwise the results of the >> array bound sanitizer and the '__builtin_dynamic_object_size' might >> be incorrect. >> >> For instance, in the following example, the allocated array has >> less elements than what's specified by the 'sbuf->count', this is >> an user error. As a result, out-of-bounds access to the array >> might not be detected. >> >> #define SIZE_BUMP 10 >> struct P *sbuf; >> void alloc_buf (size_t nelems) >> { >> sbuf = (struct P *) malloc (MAX (sizeof (struct P), >> (offsetof (struct P, >> array[0]) >> + nelems * sizeof (char)))); >> sbuf->count = nelems + SIZE_BUMP; >> /* This is invalid when the sbuf->array has less than >> sbuf->count >> elements. */ >> } >> >> In the following example, the 2nd update to the field 'sbuf->count' >> of the above structure will permit out-of-bounds access to the >> array 'sbuf>array' as well. >> >> #define SIZE_BUMP 10 >> struct P *sbuf; >> void alloc_buf (size_t nelems) >> { >> sbuf = (struct P *) malloc (MAX (sizeof (struct P), >> (offsetof (struct P, >> array[0]) >> + (nelems + SIZE_BUMP) * >> sizeof (char)))); >> sbuf->count = nelems; >> /* This is valid when the sbuf->array has at least >> sbuf->count >> elements. */ >> } >> void use_buf (int index) >> { >> sbuf->count = sbuf->count + SIZE_BUMP + 1; >> /* Now the value of sbuf->count is larger than the number >> of elements of sbuf->array. */ >> sbuf->array[index] = 0; >> /* then the out-of-bound access to this array >> might not be detected. */ >> } >> >> gcc/c-family/ChangeLog: >> >> PR C/108896 >> * c-attribs.cc (handle_counted_by_attribute): New function. >> (attribute_takes_identifier_p): Add counted_by attribute to the list. >> * c-common.cc (c_flexible_array_member_type_p): ...To this. >> * c-common.h (c_flexible_array_member_type_p): New prototype. >> >> gcc/c/ChangeLog: >> >> PR C/108896 >> * c-decl.cc (flexible_array_member_type_p): Renamed and moved to... >> (add_flexible_array_elts_to_size): Use renamed function. >> (is_flexible_array_member_p): Use renamed function. >> (verify_counted_by_attribute): New function. >> (finish_struct): Use renamed function and verify counted_by >> attribute. >> >> gcc/ChangeLog: >> >> PR C/108896 >> * doc/extend.texi: Document attribute counted_by. >> * tree.cc (get_named_field): New function. >> * tree.h (get_named_field): New prototype. >> >> gcc/testsuite/ChangeLog: >> >> PR C/108896 >> * gcc.dg/flex-array-counted-by.c: New test. >> --- >> gcc/c-family/c-attribs.cc | 54 ++++++++++++- >> gcc/c-family/c-common.cc | 13 ++++ >> gcc/c-family/c-common.h | 1 + >> gcc/c/c-decl.cc | 79 +++++++++++++++----- >> gcc/doc/extend.texi | 77 +++++++++++++++++++ >> gcc/testsuite/gcc.dg/flex-array-counted-by.c | 40 ++++++++++ >> gcc/tree.cc | 40 ++++++++++ >> gcc/tree.h | 5 ++ >> 8 files changed, 291 insertions(+), 18 deletions(-) >> create mode 100644 gcc/testsuite/gcc.dg/flex-array-counted-by.c >> >> diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc >> index e2792ca6898b..65e4f6639109 100644 >> --- a/gcc/c-family/c-attribs.cc >> +++ b/gcc/c-family/c-attribs.cc >> @@ -103,6 +103,8 @@ static tree handle_warn_if_not_aligned_attribute >> (tree *, tree, tree, >> int, bool *); >> static tree handle_strict_flex_array_attribute (tree *, tree, tree, >> int, bool *); >> +static tree handle_counted_by_attribute (tree *, tree, tree, >> + int, bool *); >> static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; >> static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ; >> static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, >> bool *); >> @@ -373,6 +375,8 @@ const struct attribute_spec >> c_common_attribute_table[] = >> handle_warn_if_not_aligned_attribute, NULL }, >> { "strict_flex_array", 1, 1, true, false, false, false, >> handle_strict_flex_array_attribute, NULL }, >> + { "counted_by", 1, 1, true, false, false, false, >> + handle_counted_by_attribute, NULL }, >> { "weak", 0, 0, true, false, false, false, >> handle_weak_attribute, NULL }, >> { "noplt", 0, 0, true, false, false, false, >> @@ -601,7 +605,8 @@ attribute_takes_identifier_p (const_tree attr_id) >> else if (!strcmp ("mode", spec->name) >> || !strcmp ("format", spec->name) >> || !strcmp ("cleanup", spec->name) >> - || !strcmp ("access", spec->name)) >> + || !strcmp ("access", spec->name) >> + || !strcmp ("counted_by", spec->name)) >> return true; >> else >> return targetm.attribute_takes_identifier_p (attr_id); >> @@ -2555,6 +2560,53 @@ handle_strict_flex_array_attribute (tree *node, >> tree name, >> return NULL_TREE; >> } >> +/* Handle a "counted_by" attribute; arguments as in >> + struct attribute_spec.handler. */ >> + >> +static tree >> +handle_counted_by_attribute (tree *node, tree name, >> + tree args, int ARG_UNUSED (flags), >> + bool *no_add_attrs) >> +{ >> + tree decl = *node; >> + tree argval = TREE_VALUE (args); >> + >> + /* This attribute only applies to field decls of a structure. */ >> + if (TREE_CODE (decl) != FIELD_DECL) >> + { >> + error_at (DECL_SOURCE_LOCATION (decl), >> + "%qE attribute may not be specified for non-field" >> + " declaration %q+D", name, decl); >> + *no_add_attrs = true; >> + } > > Applies only to struct fields. OK. > >> + /* This attribute only applies to field with array type. */ >> + else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE) >> + { >> + error_at (DECL_SOURCE_LOCATION (decl), >> + "%qE attribute may not be specified for a non-array field", >> + name); >> + *no_add_attrs = true; >> + } > > The struct field should also be an array. OK. > >> + /* This attribute only applies to a C99 flexible array member >> type. */ >> + else if (! c_flexible_array_member_type_p (TREE_TYPE (decl))) >> + { >> + error_at (DECL_SOURCE_LOCATION (decl), >> + "%qE attribute may not be specified for a non" >> + " flexible array member field", >> + name); >> + *no_add_attrs = true; >> + } > > Additionally, the field should be a *flex* array. OK. Could this be > reworded to: > > %qE attribute only applies to C99 flexible array members > > That would make it clear that the GNU extension flex arrays (i.e. any > last array member of a struct) don't support this attribute. > >> + /* The argument should be an identifier. */ >> + else if (TREE_CODE (argval) != IDENTIFIER_NODE) >> + { >> + error_at (DECL_SOURCE_LOCATION (decl), >> + "%<counted_by%> argument not an identifier"); >> + *no_add_attrs = true; >> + } > > Argument should be an identifier, and the check for validity of the > identifier comes later in finish_struct. OK. > >> + >> + return NULL_TREE; >> +} >> + >> /* Handle a "weak" attribute; arguments as in >> struct attribute_spec.handler. */ >> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc >> index 9fbaeb437a12..a18937245c2a 100644 >> --- a/gcc/c-family/c-common.cc >> +++ b/gcc/c-family/c-common.cc >> @@ -9521,6 +9521,19 @@ c_common_finalize_early_debug (void) >> (*debug_hooks->early_global_decl) (cnode->decl); >> } >> +/* Determine whether TYPE is a ISO C99 flexible array memeber type >> "[]". */ >> +bool >> +c_flexible_array_member_type_p (const_tree type) >> +{ >> + if (TREE_CODE (type) == ARRAY_TYPE >> + && TYPE_SIZE (type) == NULL_TREE >> + && TYPE_DOMAIN (type) != NULL_TREE >> + && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) >> + return true; >> + >> + return false; >> +} >> + > > Hoist flexible_array_member_type_p to use more widely. OK. > >> /* Get the LEVEL of the strict_flex_array for the ARRAY_FIELD based >> on the >> values of attribute strict_flex_array and the >> flag_strict_flex_arrays. */ >> unsigned int >> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h >> index 78fc5248ba68..c29bb429062b 100644 >> --- a/gcc/c-family/c-common.h >> +++ b/gcc/c-family/c-common.h >> @@ -909,6 +909,7 @@ extern tree fold_for_warn (tree); >> extern tree c_common_get_narrower (tree, int *); >> extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *); >> extern void c_common_finalize_early_debug (void); >> +extern bool c_flexible_array_member_type_p (const_tree); >> extern unsigned int c_strict_flex_array_level_of (tree); >> extern bool c_option_is_from_cpp_diagnostics (int); >> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc >> index 1f9eb44dbaa2..e943b49b5230 100644 >> --- a/gcc/c/c-decl.cc >> +++ b/gcc/c/c-decl.cc >> @@ -5173,19 +5173,6 @@ set_array_declarator_inner (struct c_declarator >> *decl, >> return decl; >> } >> -/* Determine whether TYPE is a ISO C99 flexible array memeber type >> "[]". */ >> -static bool >> -flexible_array_member_type_p (const_tree type) >> -{ >> - if (TREE_CODE (type) == ARRAY_TYPE >> - && TYPE_SIZE (type) == NULL_TREE >> - && TYPE_DOMAIN (type) != NULL_TREE >> - && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) >> - return true; >> - >> - return false; >> -} >> - >> /* Determine whether TYPE is a one-element array type "[1]". */ >> static bool >> one_element_array_type_p (const_tree type) >> @@ -5222,7 +5209,7 @@ add_flexible_array_elts_to_size (tree decl, tree >> init) >> elt = CONSTRUCTOR_ELTS (init)->last ().value; >> type = TREE_TYPE (elt); >> - if (flexible_array_member_type_p (type)) >> + if (c_flexible_array_member_type_p (type)) >> { >> complete_array_type (&type, elt, false); >> DECL_SIZE (decl) >> @@ -9094,7 +9081,7 @@ is_flexible_array_member_p (bool is_last_field, >> bool is_zero_length_array = zero_length_array_type_p (TREE_TYPE (x)); >> bool is_one_element_array = one_element_array_type_p (TREE_TYPE (x)); >> - bool is_flexible_array = flexible_array_member_type_p (TREE_TYPE (x)); >> + bool is_flexible_array = c_flexible_array_member_type_p (TREE_TYPE >> (x)); >> unsigned int strict_flex_array_level = >> c_strict_flex_array_level_of (x); > > Simple refactoring. OK. > >> @@ -9124,6 +9111,61 @@ is_flexible_array_member_p (bool is_last_field, >> return false; >> } >> +/* Verify the argument of the counted_by attribute of the flexible array > > Verify *that* the argument... > >> + member FIELD_DECL is a valid field of the containing structure's >> fieldlist, >> + FIELDLIST, Report error and remove this attribute when it's not. */ >> +static void >> +verify_counted_by_attribute (tree fieldlist, tree field_decl) >> +{ >> + tree attr_counted_by = lookup_attribute ("counted_by", >> + DECL_ATTRIBUTES (field_decl)); >> + >> + if (!attr_counted_by) >> + return; >> + >> + /* If there is an counted_by attribute attached to the field, >> + verify it. */ >> + >> + const char *fieldname >> + = IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (attr_counted_by))); >> + >> + /* Verify the argument of the attrbute is a valid field of the > > s/attrbute/attribute/ > >> + containing structure. */ >> + >> + tree counted_by_field = get_named_field (fieldlist, fieldname); >> + >> + /* Error when the field is not found in the containing structure. */ >> + if (!counted_by_field) >> + { >> + error_at (DECL_SOURCE_LOCATION (field_decl), >> + "%qE attribute argument not a field declaration" >> + " in the same structure, ignore it", >> + (get_attribute_name (attr_counted_by))); > > Probably someone with English as a first language would make a better > suggestion, but how about: > > Argument specified in %qE attribute is not a field declaration in the > same structure, ignoring it. > >> + >> + DECL_ATTRIBUTES (field_decl) >> + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); >> + } >> + else >> + /* Error when the field is not with an integer type. */ > > Suggest: Flag an error when the field is not of 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 (TREE_CODE (TREE_TYPE (real_field)) != INTEGER_TYPE) >> + { >> + error_at (DECL_SOURCE_LOCATION (field_decl), >> + "%qE attribute argument not a field declaration" >> + " with integer type, ignore it", >> + (get_attribute_name (attr_counted_by))); > > Suggest: > > Argument specified in %qE attribute is not of an integer type, > ignoring it. > >> + >> + DECL_ATTRIBUTES (field_decl) >> + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); >> + } >> + } >> + >> + return; I forgot to mention the redundant return here. >> +} >> /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T. >> LOC is the location of the RECORD_TYPE or UNION_TYPE's definition. >> @@ -9244,7 +9286,7 @@ finish_struct (location_t loc, tree t, tree >> fieldlist, tree attributes, >> DECL_PACKED (x) = 1; >> /* Detect flexible array member in an invalid context. */ >> - if (flexible_array_member_type_p (TREE_TYPE (x))) >> + if (c_flexible_array_member_type_p (TREE_TYPE (x))) >> { >> if (TREE_CODE (t) == UNION_TYPE) >> { >> @@ -9265,6 +9307,9 @@ finish_struct (location_t loc, tree t, tree >> fieldlist, tree attributes, >> "members"); >> TREE_TYPE (x) = error_mark_node; >> } >> + /* if there is a counted_by attribute attached to this field, >> + verify it. */ >> + verify_counted_by_attribute (fieldlist, x); >> } >> if (pedantic && TREE_CODE (t) == RECORD_TYPE >> @@ -9279,7 +9324,7 @@ finish_struct (location_t loc, tree t, tree >> fieldlist, tree attributes, >> when x is an array and is the last field. */ >> if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE) >> TYPE_INCLUDES_FLEXARRAY (t) >> - = is_last_field && flexible_array_member_type_p (TREE_TYPE (x)); >> + = is_last_field && c_flexible_array_member_type_p (TREE_TYPE (x)); >> /* Recursively set TYPE_INCLUDES_FLEXARRAY for the context of >> x, t >> when x is an union or record and is the last field. */ >> else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (x))) >> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi >> index 97eaacf8a7ec..ea6240646936 100644 >> --- a/gcc/doc/extend.texi >> +++ b/gcc/doc/extend.texi >> @@ -7617,6 +7617,83 @@ When both the attribute and the option present >> at the same time, the level of >> the strictness for the specific trailing array field is determined >> by the >> attribute. >> +@cindex @code{counted_by} variable attribute >> +@item counted_by (@var{count}) >> +The @code{counted_by} attribute may be attached to the flexible array >> +member of a structure. It indicates that the number of the elements >> of the >> +array is given by the field named "@var{count}" in the same structure >> as the >> +flexible array member. GCC uses this information to improve the >> results of >> +the array bound sanitizer and the @code{__builtin_dynamic_object_size}. > > Maybe specify somehow that this only applies to C99 flexible arrays? Like: > > The @code{counted_by} attribute may be attached to the C99 flexible > array member of a structure... > >> + >> +For instance, the following code: >> + >> +@smallexample >> +struct P @{ >> + size_t count; >> + char other; >> + char array[] __attribute__ ((counted_by (count))); >> +@} *p; >> +@end smallexample >> + >> +@noindent >> +specifies that the @code{array} is a flexible array member whose >> number of >> +elements is given by the field @code{count} in the same structure. >> + >> +The field that represents the number of the elements should have an >> integer >> +type. An explicit @code{counted_by} annotation defines a >> relationship between >> +two objects, @code{p->array} and @code{p->count}, that >> @code{p->array} has >> +@emph{at least} @code{p->count} number of elements available. This >> relationship >> +must hold even after any of these related objects are updated. It's >> the user's >> +responsibility to make sure this relationship to be kept all the time. >> +Otherwise the results of the array bound sanitizer and the >> +@code{__builtin_dynamic_object_size} might be incorrect. > > Suggest: > > The field that represents the number of the elements must have an > integer type. An explicit @code{counted_by} annotation defines a > relationship between two objects, @code{p->array} and @code{p->count}, > that @code{p->array} has @emph{at least} @code{p->count} number of > elements available. The user is responsible to ensure that this > relationship is consistently maintained during the lifetime of the > object. Failure to do so may result in results of the array bound > sanitizer and the @code{__builtin_dynamic_object_size} being > undefined. > >> + >> +For instance, in the following example, the allocated array has less >> elements >> +than what's specified by the @code{sbuf->count}, this is an user >> error. As a >> +result, out-of-bounds access to the array might not be detected. >> + >> +@smallexample >> +#define SIZE_BUMP 10 >> +struct P *sbuf; >> +void alloc_buf (size_t nelems) >> +@{ >> + sbuf = (struct P *) malloc (MAX (sizeof (struct P), >> + (offsetof (struct P, array[0]) >> + + nelems * sizeof (char)))); >> + sbuf->count = nelems + SIZE_BUMP; >> + /* This is invalid when the sbuf->array has less than sbuf->count >> + elements. */ >> +@} >> +@end smallexample >> + >> +In the following example, the 2nd update to the field >> @code{sbuf->count} of >> +the above structure will permit out-of-bounds access to the array >> +@code{sbuf>array} as well. >> + >> +@smallexample >> +#define SIZE_BUMP 10 >> +struct P *sbuf; >> +void alloc_buf (size_t nelems) >> +@{ >> + sbuf = (struct P *) malloc (MAX (sizeof (struct P), >> + (offsetof (struct P, array[0]) >> + + (nelems + SIZE_BUMP) * sizeof (char)))); >> + sbuf->count = nelems; >> + /* This is valid when the sbuf->array has at least sbuf->count >> + elements. */ >> +@} >> +void use_buf (int index) >> +@{ >> + sbuf->count = sbuf->count + SIZE_BUMP + 1; >> + /* Now the value of sbuf->count is larger than the number >> + of elements of sbuf->array. */ >> + sbuf->array[index] = 0; >> + /* then the out-of-bound access to this array >> + might not be detected. */ >> +@} >> +@end smallexample >> + >> + > > I'm unsure if we should be demonstrating example code with undefined > behaviour. Maybe the documentation is better off without it. > >> @cindex @code{alloc_size} variable attribute >> @item alloc_size (@var{position}) >> @itemx alloc_size (@var{position-1}, @var{position-2}) >> diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by.c >> b/gcc/testsuite/gcc.dg/flex-array-counted-by.c >> new file mode 100644 >> index 000000000000..f8ce9776bf86 >> --- /dev/null >> +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by.c >> @@ -0,0 +1,40 @@ >> +/* testing the correct usage of attribute counted_by. */ >> +/* { dg-do compile } */ >> +/* { dg-options "-O2" } */ >> + >> +#include <wchar.h> >> + >> +int size; >> +int x __attribute ((counted_by (size))); /* { dg-error "attribute may >> not be specified for non-field declaration" } */ >> + >> +struct trailing { >> + int count; >> + int field __attribute ((counted_by (count))); /* { dg-error >> "attribute may not be specified for a non-array field" } */ >> +}; >> + >> +struct trailing_1 { >> + int count; >> + int array_1[0] __attribute ((counted_by (count))); /* { dg-error >> "attribute may not be specified for a non flexible array member field" >> } */ >> +}; >> + >> +int count; >> +struct trailing_array_2 { >> + int count; >> + int array_2[] __attribute ((counted_by ("count"))); /* { dg-error >> "argument not an identifier" } */ >> +}; >> + >> +struct trailing_array_3 { >> + int other; >> + int array_3[] __attribute ((counted_by (L"count"))); /* { dg-error >> "argument not an identifier" } */ >> +}; >> + >> +struct trailing_array_4 { >> + int other; >> + int array_4[] __attribute ((counted_by (count))); /* { dg-error >> "attribute argument not a field declaration in the same structure, >> ignore it" } */ >> +}; >> + >> +int count; >> +struct trailing_array_5 { >> + float count; >> + int array_5[] __attribute ((counted_by (count))); /* { dg-error >> "attribute argument not a field declaration with integer type, ignore >> it" } */ >> +}; > > Tests look OK in principle, but may need updating for the error message. > >> diff --git a/gcc/tree.cc b/gcc/tree.cc >> index 420857b110c4..fcd36ae0cd74 100644 >> --- a/gcc/tree.cc >> +++ b/gcc/tree.cc >> @@ -12745,6 +12745,46 @@ array_ref_element_size (tree exp) >> return SUBSTITUTE_PLACEHOLDER_IN_EXPR (TYPE_SIZE_UNIT >> (elmt_type), exp); >> } >> +/* Given a field list, FIELDLIST, of a structure/union, return a >> TREE_LIST, >> + with each TREE_VALUE a FIELD_DECL stepping down the chain to the >> FIELD >> + whose name is FIELDNAME, which is the last TREE_VALUE of the list. >> + return NULL_TREE if such field is not found. Normally this list >> is of >> + length one, but if the field is embedded with (nested) anonymous >> structures >> + or unions, this list steps down the chain to the field. */ >> +tree >> +get_named_field (tree fieldlist, const char *fieldname) >> +{ >> + tree named_field = NULL_TREE; >> + for (tree field = fieldlist; field; field = DECL_CHAIN (field)) >> + { >> + if (TREE_CODE (field) != FIELD_DECL) >> + continue; >> + if (DECL_NAME (field) != NULL) >> + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (field)), fieldname) == 0) >> + { >> + named_field = tree_cons (NULL_TREE, field, named_field); >> + break; >> + } >> + else >> + continue; >> + /* if the field is an anonymous struct/union, we will check the >> nested >> + fields inside it recursively. */ >> + else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))) >> + if ((named_field = get_named_field (TYPE_FIELDS (TREE_TYPE >> (field)), >> + fieldname)) != NULL_TREE) >> + { >> + named_field = tree_cons (NULL_TREE, field, named_field); >> + break; >> + } >> + else >> + continue; >> + else >> + continue; >> + } >> + return named_field; >> +} > > Descending recursively into the field list of a struct to find an > identifier with a matching name. OK. > >> + >> + >> /* Return a tree representing the lower bound of the array mentioned in >> EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ >> diff --git a/gcc/tree.h b/gcc/tree.h >> index 4c04245e2b1b..4859becaa1e7 100644 >> --- a/gcc/tree.h >> +++ b/gcc/tree.h >> @@ -5619,6 +5619,11 @@ extern tree get_base_address (tree t); >> of EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ >> extern tree array_ref_element_size (tree); >> +/* Given a field list, FIELDLIST, of a structure/union, return the >> FIELD whose >> + name is FIELDNAME, return NULL_TREE if such field is not found. >> + searching nested anonymous structure/union recursively. */ >> +extern tree get_named_field (tree, const char *); >> + >> /* Return a typenode for the "standard" C type with a given name. */ >> extern tree get_typenode_from_name (const char *); >
Hi, Sid, Thanks a lot for your time and effort to review this patch set! And sorry for my late reply due to a long vacation immediately after Cauldron, just came back this Monday.. See my reply embedded below: > On Oct 5, 2023, at 2:51 PM, Siddhesh Poyarekar <siddhesh@gotplt.org> wrote: > > On 2023-08-25 11:24, Qing Zhao wrote: >> Provide a new counted_by attribute to flexible array member field. > > The obligatory "I can't ack the patch but here's a review" disclaimer :) > >> 'counted_by (COUNT)' >> The 'counted_by' attribute may be attached to the flexible array >> member of a structure. It indicates that the number of the >> elements of the array is given by the field named "COUNT" in the >> same structure as the flexible array member. GCC uses this >> information to improve the results of the array bound sanitizer and >> the '__builtin_dynamic_object_size'. >> For instance, the following code: >> struct P { >> size_t count; >> char other; >> char array[] __attribute__ ((counted_by (count))); >> } *p; >> specifies that the 'array' is a flexible array member whose number >> of elements is given by the field 'count' in the same structure. >> The field that represents the number of the elements should have an >> integer type. An explicit 'counted_by' annotation defines a >> relationship between two objects, 'p->array' and 'p->count', that >> 'p->array' has _at least_ 'p->count' number of elements available. >> This relationship must hold even after any of these related objects >> are updated. It's the user's responsibility to make sure this >> relationship to be kept all the time. Otherwise the results of the >> array bound sanitizer and the '__builtin_dynamic_object_size' might >> be incorrect. >> For instance, in the following example, the allocated array has >> less elements than what's specified by the 'sbuf->count', this is >> an user error. As a result, out-of-bounds access to the array >> might not be detected. >> #define SIZE_BUMP 10 >> struct P *sbuf; >> void alloc_buf (size_t nelems) >> { >> sbuf = (struct P *) malloc (MAX (sizeof (struct P), >> (offsetof (struct P, array[0]) >> + nelems * sizeof (char)))); >> sbuf->count = nelems + SIZE_BUMP; >> /* This is invalid when the sbuf->array has less than sbuf->count >> elements. */ >> } >> In the following example, the 2nd update to the field 'sbuf->count' >> of the above structure will permit out-of-bounds access to the >> array 'sbuf>array' as well. >> #define SIZE_BUMP 10 >> struct P *sbuf; >> void alloc_buf (size_t nelems) >> { >> sbuf = (struct P *) malloc (MAX (sizeof (struct P), >> (offsetof (struct P, array[0]) >> + (nelems + SIZE_BUMP) * sizeof (char)))); >> sbuf->count = nelems; >> /* This is valid when the sbuf->array has at least sbuf->count >> elements. */ >> } >> void use_buf (int index) >> { >> sbuf->count = sbuf->count + SIZE_BUMP + 1; >> /* Now the value of sbuf->count is larger than the number >> of elements of sbuf->array. */ >> sbuf->array[index] = 0; >> /* then the out-of-bound access to this array >> might not be detected. */ >> } >> gcc/c-family/ChangeLog: >> PR C/108896 >> * c-attribs.cc (handle_counted_by_attribute): New function. >> (attribute_takes_identifier_p): Add counted_by attribute to the list. >> * c-common.cc (c_flexible_array_member_type_p): ...To this. >> * c-common.h (c_flexible_array_member_type_p): New prototype. >> gcc/c/ChangeLog: >> PR C/108896 >> * c-decl.cc (flexible_array_member_type_p): Renamed and moved to... >> (add_flexible_array_elts_to_size): Use renamed function. >> (is_flexible_array_member_p): Use renamed function. >> (verify_counted_by_attribute): New function. >> (finish_struct): Use renamed function and verify counted_by >> attribute. >> gcc/ChangeLog: >> PR C/108896 >> * doc/extend.texi: Document attribute counted_by. >> * tree.cc (get_named_field): New function. >> * tree.h (get_named_field): New prototype. >> gcc/testsuite/ChangeLog: >> PR C/108896 >> * gcc.dg/flex-array-counted-by.c: New test. >> --- >> gcc/c-family/c-attribs.cc | 54 ++++++++++++- >> gcc/c-family/c-common.cc | 13 ++++ >> gcc/c-family/c-common.h | 1 + >> gcc/c/c-decl.cc | 79 +++++++++++++++----- >> gcc/doc/extend.texi | 77 +++++++++++++++++++ >> gcc/testsuite/gcc.dg/flex-array-counted-by.c | 40 ++++++++++ >> gcc/tree.cc | 40 ++++++++++ >> gcc/tree.h | 5 ++ >> 8 files changed, 291 insertions(+), 18 deletions(-) >> create mode 100644 gcc/testsuite/gcc.dg/flex-array-counted-by.c >> diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc >> index e2792ca6898b..65e4f6639109 100644 >> --- a/gcc/c-family/c-attribs.cc >> +++ b/gcc/c-family/c-attribs.cc >> @@ -103,6 +103,8 @@ static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree, >> int, bool *); >> static tree handle_strict_flex_array_attribute (tree *, tree, tree, >> int, bool *); >> +static tree handle_counted_by_attribute (tree *, tree, tree, >> + int, bool *); >> static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; >> static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ; >> static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *); >> @@ -373,6 +375,8 @@ const struct attribute_spec c_common_attribute_table[] = >> handle_warn_if_not_aligned_attribute, NULL }, >> { "strict_flex_array", 1, 1, true, false, false, false, >> handle_strict_flex_array_attribute, NULL }, >> + { "counted_by", 1, 1, true, false, false, false, >> + handle_counted_by_attribute, NULL }, >> { "weak", 0, 0, true, false, false, false, >> handle_weak_attribute, NULL }, >> { "noplt", 0, 0, true, false, false, false, >> @@ -601,7 +605,8 @@ attribute_takes_identifier_p (const_tree attr_id) >> else if (!strcmp ("mode", spec->name) >> || !strcmp ("format", spec->name) >> || !strcmp ("cleanup", spec->name) >> - || !strcmp ("access", spec->name)) >> + || !strcmp ("access", spec->name) >> + || !strcmp ("counted_by", spec->name)) >> return true; >> else >> return targetm.attribute_takes_identifier_p (attr_id); >> @@ -2555,6 +2560,53 @@ handle_strict_flex_array_attribute (tree *node, tree name, >> return NULL_TREE; >> } >> +/* Handle a "counted_by" attribute; arguments as in >> + struct attribute_spec.handler. */ >> + >> +static tree >> +handle_counted_by_attribute (tree *node, tree name, >> + tree args, int ARG_UNUSED (flags), >> + bool *no_add_attrs) >> +{ >> + tree decl = *node; >> + tree argval = TREE_VALUE (args); >> + >> + /* This attribute only applies to field decls of a structure. */ >> + if (TREE_CODE (decl) != FIELD_DECL) >> + { >> + error_at (DECL_SOURCE_LOCATION (decl), >> + "%qE attribute may not be specified for non-field" >> + " declaration %q+D", name, decl); >> + *no_add_attrs = true; >> + } > > Applies only to struct fields. OK. > >> + /* This attribute only applies to field with array type. */ >> + else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE) >> + { >> + error_at (DECL_SOURCE_LOCATION (decl), >> + "%qE attribute may not be specified for a non-array field", >> + name); >> + *no_add_attrs = true; >> + } > > The struct field should also be an array. OK. > >> + /* This attribute only applies to a C99 flexible array member type. */ >> + else if (! c_flexible_array_member_type_p (TREE_TYPE (decl))) >> + { >> + error_at (DECL_SOURCE_LOCATION (decl), >> + "%qE attribute may not be specified for a non" >> + " flexible array member field", >> + name); >> + *no_add_attrs = true; >> + } > > Additionally, the field should be a *flex* array. OK. Could this be reworded to: > > %qE attribute only applies to C99 flexible array members > > That would make it clear that the GNU extension flex arrays (i.e. any last array member of a struct) don't support this attribute. Yes, make sense, will update this. > >> + /* The argument should be an identifier. */ >> + else if (TREE_CODE (argval) != IDENTIFIER_NODE) >> + { >> + error_at (DECL_SOURCE_LOCATION (decl), >> + "%<counted_by%> argument not an identifier"); >> + *no_add_attrs = true; >> + } > > Argument should be an identifier, and the check for validity of the identifier comes later in finish_struct. OK. > >> + >> + return NULL_TREE; >> +} >> + >> /* Handle a "weak" attribute; arguments as in >> struct attribute_spec.handler. */ >> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc >> index 9fbaeb437a12..a18937245c2a 100644 >> --- a/gcc/c-family/c-common.cc >> +++ b/gcc/c-family/c-common.cc >> @@ -9521,6 +9521,19 @@ c_common_finalize_early_debug (void) >> (*debug_hooks->early_global_decl) (cnode->decl); >> } >> +/* Determine whether TYPE is a ISO C99 flexible array memeber type "[]". */ >> +bool >> +c_flexible_array_member_type_p (const_tree type) >> +{ >> + if (TREE_CODE (type) == ARRAY_TYPE >> + && TYPE_SIZE (type) == NULL_TREE >> + && TYPE_DOMAIN (type) != NULL_TREE >> + && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) >> + return true; >> + >> + return false; >> +} >> + > > Hoist flexible_array_member_type_p to use more widely. OK. > >> /* Get the LEVEL of the strict_flex_array for the ARRAY_FIELD based on the >> values of attribute strict_flex_array and the flag_strict_flex_arrays. */ >> unsigned int >> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h >> index 78fc5248ba68..c29bb429062b 100644 >> --- a/gcc/c-family/c-common.h >> +++ b/gcc/c-family/c-common.h >> @@ -909,6 +909,7 @@ extern tree fold_for_warn (tree); >> extern tree c_common_get_narrower (tree, int *); >> extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *); >> extern void c_common_finalize_early_debug (void); >> +extern bool c_flexible_array_member_type_p (const_tree); >> extern unsigned int c_strict_flex_array_level_of (tree); >> extern bool c_option_is_from_cpp_diagnostics (int); >> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc >> index 1f9eb44dbaa2..e943b49b5230 100644 >> --- a/gcc/c/c-decl.cc >> +++ b/gcc/c/c-decl.cc >> @@ -5173,19 +5173,6 @@ set_array_declarator_inner (struct c_declarator *decl, >> return decl; >> } >> -/* Determine whether TYPE is a ISO C99 flexible array memeber type "[]". */ >> -static bool >> -flexible_array_member_type_p (const_tree type) >> -{ >> - if (TREE_CODE (type) == ARRAY_TYPE >> - && TYPE_SIZE (type) == NULL_TREE >> - && TYPE_DOMAIN (type) != NULL_TREE >> - && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) >> - return true; >> - >> - return false; >> -} >> - >> /* Determine whether TYPE is a one-element array type "[1]". */ >> static bool >> one_element_array_type_p (const_tree type) >> @@ -5222,7 +5209,7 @@ add_flexible_array_elts_to_size (tree decl, tree init) >> elt = CONSTRUCTOR_ELTS (init)->last ().value; >> type = TREE_TYPE (elt); >> - if (flexible_array_member_type_p (type)) >> + if (c_flexible_array_member_type_p (type)) >> { >> complete_array_type (&type, elt, false); >> DECL_SIZE (decl) >> @@ -9094,7 +9081,7 @@ is_flexible_array_member_p (bool is_last_field, >> bool is_zero_length_array = zero_length_array_type_p (TREE_TYPE (x)); >> bool is_one_element_array = one_element_array_type_p (TREE_TYPE (x)); >> - bool is_flexible_array = flexible_array_member_type_p (TREE_TYPE (x)); >> + bool is_flexible_array = c_flexible_array_member_type_p (TREE_TYPE (x)); >> unsigned int strict_flex_array_level = c_strict_flex_array_level_of (x); >> > > Simple refactoring. OK. > >> @@ -9124,6 +9111,61 @@ is_flexible_array_member_p (bool is_last_field, >> return false; >> } >> +/* Verify the argument of the counted_by attribute of the flexible array > > Verify *that* the argument... Sure, will update this. > >> + member FIELD_DECL is a valid field of the containing structure's fieldlist, >> + FIELDLIST, Report error and remove this attribute when it's not. */ >> +static void >> +verify_counted_by_attribute (tree fieldlist, tree field_decl) >> +{ >> + tree attr_counted_by = lookup_attribute ("counted_by", >> + DECL_ATTRIBUTES (field_decl)); >> + >> + if (!attr_counted_by) >> + return; >> + >> + /* If there is an counted_by attribute attached to the field, >> + verify it. */ >> + >> + const char *fieldname >> + = IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (attr_counted_by))); >> + >> + /* Verify the argument of the attrbute is a valid field of the > > s/attrbute/attribute/ Okay. > >> + containing structure. */ >> + >> + tree counted_by_field = get_named_field (fieldlist, fieldname); >> + >> + /* Error when the field is not found in the containing structure. */ >> + if (!counted_by_field) >> + { >> + error_at (DECL_SOURCE_LOCATION (field_decl), >> + "%qE attribute argument not a field declaration" >> + " in the same structure, ignore it", >> + (get_attribute_name (attr_counted_by))); > > Probably someone with English as a first language would make a better suggestion, but how about: > > Argument specified in %qE attribute is not a field declaration in the > same structure, ignoring it. This is better, will update it. > >> + >> + DECL_ATTRIBUTES (field_decl) >> + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); >> + } >> + else >> + /* Error when the field is not with an integer type. */ > > Suggest: Flag an error when the field is not of an integer type. Okay. > >> + { >> + while (TREE_CHAIN (counted_by_field)) >> + counted_by_field = TREE_CHAIN (counted_by_field); >> + tree real_field = TREE_VALUE (counted_by_field); >> + >> + if (TREE_CODE (TREE_TYPE (real_field)) != INTEGER_TYPE) >> + { >> + error_at (DECL_SOURCE_LOCATION (field_decl), >> + "%qE attribute argument not a field declaration" >> + " with integer type, ignore it", >> + (get_attribute_name (attr_counted_by))); > > Suggest: > > Argument specified in %qE attribute is not of an integer type, > ignoring it. Will update this. > >> + >> + DECL_ATTRIBUTES (field_decl) >> + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); >> + } >> + } >> + >> + return; >> +} >> /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T. >> LOC is the location of the RECORD_TYPE or UNION_TYPE's definition. >> @@ -9244,7 +9286,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, >> DECL_PACKED (x) = 1; >> /* Detect flexible array member in an invalid context. */ >> - if (flexible_array_member_type_p (TREE_TYPE (x))) >> + if (c_flexible_array_member_type_p (TREE_TYPE (x))) >> { >> if (TREE_CODE (t) == UNION_TYPE) >> { >> @@ -9265,6 +9307,9 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, >> "members"); >> TREE_TYPE (x) = error_mark_node; >> } >> + /* if there is a counted_by attribute attached to this field, >> + verify it. */ >> + verify_counted_by_attribute (fieldlist, x); >> } >> if (pedantic && TREE_CODE (t) == RECORD_TYPE >> @@ -9279,7 +9324,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, >> when x is an array and is the last field. */ >> if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE) >> TYPE_INCLUDES_FLEXARRAY (t) >> - = is_last_field && flexible_array_member_type_p (TREE_TYPE (x)); >> + = is_last_field && c_flexible_array_member_type_p (TREE_TYPE (x)); >> /* Recursively set TYPE_INCLUDES_FLEXARRAY for the context of x, t >> when x is an union or record and is the last field. */ >> else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (x))) >> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi >> index 97eaacf8a7ec..ea6240646936 100644 >> --- a/gcc/doc/extend.texi >> +++ b/gcc/doc/extend.texi >> @@ -7617,6 +7617,83 @@ When both the attribute and the option present at the same time, the level of >> the strictness for the specific trailing array field is determined by the >> attribute. >> +@cindex @code{counted_by} variable attribute >> +@item counted_by (@var{count}) >> +The @code{counted_by} attribute may be attached to the flexible array >> +member of a structure. It indicates that the number of the elements of the >> +array is given by the field named "@var{count}" in the same structure as the >> +flexible array member. GCC uses this information to improve the results of >> +the array bound sanitizer and the @code{__builtin_dynamic_object_size}. > > Maybe specify somehow that this only applies to C99 flexible arrays? Like: > > The @code{counted_by} attribute may be attached to the C99 flexible > array member of a structure... Yes, specify that the attribute will be only applied to C99 FMA is necessary, I will update this. > >> + >> +For instance, the following code: >> + >> +@smallexample >> +struct P @{ >> + size_t count; >> + char other; >> + char array[] __attribute__ ((counted_by (count))); >> +@} *p; >> +@end smallexample >> + >> +@noindent >> +specifies that the @code{array} is a flexible array member whose number of >> +elements is given by the field @code{count} in the same structure. >> + >> +The field that represents the number of the elements should have an integer >> +type. An explicit @code{counted_by} annotation defines a relationship between >> +two objects, @code{p->array} and @code{p->count}, that @code{p->array} has >> +@emph{at least} @code{p->count} number of elements available. This relationship >> +must hold even after any of these related objects are updated. It's the user's >> +responsibility to make sure this relationship to be kept all the time. >> +Otherwise the results of the array bound sanitizer and the >> +@code{__builtin_dynamic_object_size} might be incorrect. > > Suggest: > > The field that represents the number of the elements must have an > integer type. An explicit @code{counted_by} annotation defines a > relationship between two objects, @code{p->array} and @code{p->count}, > that @code{p->array} has @emph{at least} @code{p->count} number of > elements available. The user is responsible to ensure that this > relationship is consistently maintained during the lifetime of the > object. Failure to do so may result in results of the array bound > sanitizer and the @code{__builtin_dynamic_object_size} being > undefined. Thanks for the suggestion, will update accordingly. > >> + >> +For instance, in the following example, the allocated array has less elements >> +than what's specified by the @code{sbuf->count}, this is an user error. As a >> +result, out-of-bounds access to the array might not be detected. >> + >> +@smallexample >> +#define SIZE_BUMP 10 >> +struct P *sbuf; >> +void alloc_buf (size_t nelems) >> +@{ >> + sbuf = (struct P *) malloc (MAX (sizeof (struct P), >> + (offsetof (struct P, array[0]) >> + + nelems * sizeof (char)))); >> + sbuf->count = nelems + SIZE_BUMP; >> + /* This is invalid when the sbuf->array has less than sbuf->count >> + elements. */ >> +@} >> +@end smallexample >> + >> +In the following example, the 2nd update to the field @code{sbuf->count} of >> +the above structure will permit out-of-bounds access to the array >> +@code{sbuf>array} as well. >> + >> +@smallexample >> +#define SIZE_BUMP 10 >> +struct P *sbuf; >> +void alloc_buf (size_t nelems) >> +@{ >> + sbuf = (struct P *) malloc (MAX (sizeof (struct P), >> + (offsetof (struct P, array[0]) >> + + (nelems + SIZE_BUMP) * sizeof (char)))); >> + sbuf->count = nelems; >> + /* This is valid when the sbuf->array has at least sbuf->count >> + elements. */ >> +@} >> +void use_buf (int index) >> +@{ >> + sbuf->count = sbuf->count + SIZE_BUMP + 1; >> + /* Now the value of sbuf->count is larger than the number >> + of elements of sbuf->array. */ >> + sbuf->array[index] = 0; >> + /* then the out-of-bound access to this array >> + might not be detected. */ >> +@} >> +@end smallexample >> + >> + > > I'm unsure if we should be demonstrating example code with undefined behaviour. Maybe the documentation is better off without it. Good point, will delete the example with undefined behavior from the doc. > >> @cindex @code{alloc_size} variable attribute >> @item alloc_size (@var{position}) >> @itemx alloc_size (@var{position-1}, @var{position-2}) >> diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by.c b/gcc/testsuite/gcc.dg/flex-array-counted-by.c >> new file mode 100644 >> index 000000000000..f8ce9776bf86 >> --- /dev/null >> +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by.c >> @@ -0,0 +1,40 @@ >> +/* testing the correct usage of attribute counted_by. */ >> +/* { dg-do compile } */ >> +/* { dg-options "-O2" } */ >> + >> +#include <wchar.h> >> + >> +int size; >> +int x __attribute ((counted_by (size))); /* { dg-error "attribute may not be specified for non-field declaration" } */ >> + >> +struct trailing { >> + int count; >> + int field __attribute ((counted_by (count))); /* { dg-error "attribute may not be specified for a non-array field" } */ >> +}; >> + >> +struct trailing_1 { >> + int count; >> + int array_1[0] __attribute ((counted_by (count))); /* { dg-error "attribute may not be specified for a non flexible array member field" } */ >> +}; >> + >> +int count; >> +struct trailing_array_2 { >> + int count; >> + int array_2[] __attribute ((counted_by ("count"))); /* { dg-error "argument not an identifier" } */ >> +}; >> + >> +struct trailing_array_3 { >> + int other; >> + int array_3[] __attribute ((counted_by (L"count"))); /* { dg-error "argument not an identifier" } */ >> +}; >> + >> +struct trailing_array_4 { >> + int other; >> + int array_4[] __attribute ((counted_by (count))); /* { dg-error "attribute argument not a field declaration in the same structure, ignore it" } */ >> +}; >> + >> +int count; >> +struct trailing_array_5 { >> + float count; >> + int array_5[] __attribute ((counted_by (count))); /* { dg-error "attribute argument not a field declaration with integer type, ignore it" } */ >> +}; > > Tests look OK in principle, but may need updating for the error message. Okay. thanks. Qing > >> diff --git a/gcc/tree.cc b/gcc/tree.cc >> index 420857b110c4..fcd36ae0cd74 100644 >> --- a/gcc/tree.cc >> +++ b/gcc/tree.cc >> @@ -12745,6 +12745,46 @@ array_ref_element_size (tree exp) >> return SUBSTITUTE_PLACEHOLDER_IN_EXPR (TYPE_SIZE_UNIT (elmt_type), exp); >> } >> +/* Given a field list, FIELDLIST, of a structure/union, return a TREE_LIST, >> + with each TREE_VALUE a FIELD_DECL stepping down the chain to the FIELD >> + whose name is FIELDNAME, which is the last TREE_VALUE of the list. >> + return NULL_TREE if such field is not found. Normally this list is of >> + length one, but if the field is embedded with (nested) anonymous structures >> + or unions, this list steps down the chain to the field. */ >> +tree >> +get_named_field (tree fieldlist, const char *fieldname) >> +{ >> + tree named_field = NULL_TREE; >> + for (tree field = fieldlist; field; field = DECL_CHAIN (field)) >> + { >> + if (TREE_CODE (field) != FIELD_DECL) >> + continue; >> + if (DECL_NAME (field) != NULL) >> + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (field)), fieldname) == 0) >> + { >> + named_field = tree_cons (NULL_TREE, field, named_field); >> + break; >> + } >> + else >> + continue; >> + /* if the field is an anonymous struct/union, we will check the nested >> + fields inside it recursively. */ >> + else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))) >> + if ((named_field = get_named_field (TYPE_FIELDS (TREE_TYPE (field)), >> + fieldname)) != NULL_TREE) >> + { >> + named_field = tree_cons (NULL_TREE, field, named_field); >> + break; >> + } >> + else >> + continue; >> + else >> + continue; >> + } >> + return named_field; >> +} > > Descending recursively into the field list of a struct to find an identifier with a matching name. OK. > >> + >> + >> /* Return a tree representing the lower bound of the array mentioned in >> EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ >> diff --git a/gcc/tree.h b/gcc/tree.h >> index 4c04245e2b1b..4859becaa1e7 100644 >> --- a/gcc/tree.h >> +++ b/gcc/tree.h >> @@ -5619,6 +5619,11 @@ extern tree get_base_address (tree t); >> of EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ >> extern tree array_ref_element_size (tree); >> +/* Given a field list, FIELDLIST, of a structure/union, return the FIELD whose >> + name is FIELDNAME, return NULL_TREE if such field is not found. >> + searching nested anonymous structure/union recursively. */ >> +extern tree get_named_field (tree, const char *); >> + >> /* Return a typenode for the "standard" C type with a given name. */ >> extern tree get_typenode_from_name (const char *); >>
>>> + member FIELD_DECL is a valid field of the containing structure's fieldlist, >>> + FIELDLIST, Report error and remove this attribute when it's not. */ >>> +static void >>> +verify_counted_by_attribute (tree fieldlist, tree field_decl) >>> +{ >>> + tree attr_counted_by = lookup_attribute ("counted_by", >>> + DECL_ATTRIBUTES (field_decl)); >>> + >>> + if (!attr_counted_by) >>> + return; >>> + >>> + /* If there is an counted_by attribute attached to the field, >>> + verify it. */ >>> + >>> + const char *fieldname >>> + = IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (attr_counted_by))); >>> + >>> + /* Verify the argument of the attrbute is a valid field of the >> s/attrbute/attribute/ >>> + containing structure. */ >>> + >>> + tree counted_by_field = get_named_field (fieldlist, fieldname); >>> + >>> + /* Error when the field is not found in the containing structure. */ >>> + if (!counted_by_field) >>> + { >>> + error_at (DECL_SOURCE_LOCATION (field_decl), >>> + "%qE attribute argument not a field declaration" >>> + " in the same structure, ignore it", >>> + (get_attribute_name (attr_counted_by))); >> Probably someone with English as a first language would make a better suggestion, but how about: >> Argument specified in %qE attribute is not a field declaration in the >> same structure, ignoring it. >>> + >>> + DECL_ATTRIBUTES (field_decl) >>> + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); >>> + } >>> + else >>> + /* Error when the field is not with an integer type. */ >> Suggest: Flag an error when the field is not of 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 (TREE_CODE (TREE_TYPE (real_field)) != INTEGER_TYPE) >>> + { >>> + error_at (DECL_SOURCE_LOCATION (field_decl), >>> + "%qE attribute argument not a field declaration" >>> + " with integer type, ignore it", >>> + (get_attribute_name (attr_counted_by))); >> Suggest: >> Argument specified in %qE attribute is not of an integer type, >> ignoring it. >>> + >>> + DECL_ATTRIBUTES (field_decl) >>> + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); >>> + } >>> + } >>> + >>> + return; > > I forgot to mention the redundant return here. Could you please clarify a little bit here, why the return here is redundant? > >>> +} >>> /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T.
On 2023-10-18 10:51, Qing Zhao wrote: > >>>> + member FIELD_DECL is a valid field of the containing structure's fieldlist, >>>> + FIELDLIST, Report error and remove this attribute when it's not. */ >>>> +static void >>>> +verify_counted_by_attribute (tree fieldlist, tree field_decl) >>>> +{ >>>> + tree attr_counted_by = lookup_attribute ("counted_by", >>>> + DECL_ATTRIBUTES (field_decl)); >>>> + >>>> + if (!attr_counted_by) >>>> + return; >>>> + >>>> + /* If there is an counted_by attribute attached to the field, >>>> + verify it. */ >>>> + >>>> + const char *fieldname >>>> + = IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (attr_counted_by))); >>>> + >>>> + /* Verify the argument of the attrbute is a valid field of the >>> s/attrbute/attribute/ >>>> + containing structure. */ >>>> + >>>> + tree counted_by_field = get_named_field (fieldlist, fieldname); >>>> + >>>> + /* Error when the field is not found in the containing structure. */ >>>> + if (!counted_by_field) >>>> + { >>>> + error_at (DECL_SOURCE_LOCATION (field_decl), >>>> + "%qE attribute argument not a field declaration" >>>> + " in the same structure, ignore it", >>>> + (get_attribute_name (attr_counted_by))); >>> Probably someone with English as a first language would make a better suggestion, but how about: >>> Argument specified in %qE attribute is not a field declaration in the >>> same structure, ignoring it. >>>> + >>>> + DECL_ATTRIBUTES (field_decl) >>>> + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); >>>> + } >>>> + else >>>> + /* Error when the field is not with an integer type. */ >>> Suggest: Flag an error when the field is not of 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 (TREE_CODE (TREE_TYPE (real_field)) != INTEGER_TYPE) >>>> + { >>>> + error_at (DECL_SOURCE_LOCATION (field_decl), >>>> + "%qE attribute argument not a field declaration" >>>> + " with integer type, ignore it", >>>> + (get_attribute_name (attr_counted_by))); >>> Suggest: >>> Argument specified in %qE attribute is not of an integer type, >>> ignoring it. >>>> + >>>> + DECL_ATTRIBUTES (field_decl) >>>> + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); >>>> + } >>>> + } >>>> + >>>> + return; >> >> I forgot to mention the redundant return here. > > Could you please clarify a little bit here, why the return here is redundant? It's the last line in the function, so even without that statement the function will return. Thanks, Sid
> On Oct 18, 2023, at 11:18 AM, Siddhesh Poyarekar <siddhesh@gotplt.org> wrote: > > On 2023-10-18 10:51, Qing Zhao wrote: >>>>> + member FIELD_DECL is a valid field of the containing structure's fieldlist, >>>>> + FIELDLIST, Report error and remove this attribute when it's not. */ >>>>> +static void >>>>> +verify_counted_by_attribute (tree fieldlist, tree field_decl) >>>>> +{ >>>>> + tree attr_counted_by = lookup_attribute ("counted_by", >>>>> + DECL_ATTRIBUTES (field_decl)); >>>>> + >>>>> + if (!attr_counted_by) >>>>> + return; >>>>> + >>>>> + /* If there is an counted_by attribute attached to the field, >>>>> + verify it. */ >>>>> + >>>>> + const char *fieldname >>>>> + = IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (attr_counted_by))); >>>>> + >>>>> + /* Verify the argument of the attrbute is a valid field of the >>>> s/attrbute/attribute/ >>>>> + containing structure. */ >>>>> + >>>>> + tree counted_by_field = get_named_field (fieldlist, fieldname); >>>>> + >>>>> + /* Error when the field is not found in the containing structure. */ >>>>> + if (!counted_by_field) >>>>> + { >>>>> + error_at (DECL_SOURCE_LOCATION (field_decl), >>>>> + "%qE attribute argument not a field declaration" >>>>> + " in the same structure, ignore it", >>>>> + (get_attribute_name (attr_counted_by))); >>>> Probably someone with English as a first language would make a better suggestion, but how about: >>>> Argument specified in %qE attribute is not a field declaration in the >>>> same structure, ignoring it. >>>>> + >>>>> + DECL_ATTRIBUTES (field_decl) >>>>> + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); >>>>> + } >>>>> + else >>>>> + /* Error when the field is not with an integer type. */ >>>> Suggest: Flag an error when the field is not of 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 (TREE_CODE (TREE_TYPE (real_field)) != INTEGER_TYPE) >>>>> + { >>>>> + error_at (DECL_SOURCE_LOCATION (field_decl), >>>>> + "%qE attribute argument not a field declaration" >>>>> + " with integer type, ignore it", >>>>> + (get_attribute_name (attr_counted_by))); >>>> Suggest: >>>> Argument specified in %qE attribute is not of an integer type, >>>> ignoring it. >>>>> + >>>>> + DECL_ATTRIBUTES (field_decl) >>>>> + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); >>>>> + } >>>>> + } >>>>> + >>>>> + return; >>> >>> I forgot to mention the redundant return here. >> Could you please clarify a little bit here, why the return here is redundant? > > It's the last line in the function, so even without that statement the function will return. Oh, I see. -:) Actually,I always put an explicit return there even though it’s the last line and return implicitly. Qing > > Thanks, > Sid
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index e2792ca6898b..65e4f6639109 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -103,6 +103,8 @@ static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree, int, bool *); static tree handle_strict_flex_array_attribute (tree *, tree, tree, int, bool *); +static tree handle_counted_by_attribute (tree *, tree, tree, + int, bool *); static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ; static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *); @@ -373,6 +375,8 @@ const struct attribute_spec c_common_attribute_table[] = handle_warn_if_not_aligned_attribute, NULL }, { "strict_flex_array", 1, 1, true, false, false, false, handle_strict_flex_array_attribute, NULL }, + { "counted_by", 1, 1, true, false, false, false, + handle_counted_by_attribute, NULL }, { "weak", 0, 0, true, false, false, false, handle_weak_attribute, NULL }, { "noplt", 0, 0, true, false, false, false, @@ -601,7 +605,8 @@ attribute_takes_identifier_p (const_tree attr_id) else if (!strcmp ("mode", spec->name) || !strcmp ("format", spec->name) || !strcmp ("cleanup", spec->name) - || !strcmp ("access", spec->name)) + || !strcmp ("access", spec->name) + || !strcmp ("counted_by", spec->name)) return true; else return targetm.attribute_takes_identifier_p (attr_id); @@ -2555,6 +2560,53 @@ handle_strict_flex_array_attribute (tree *node, tree name, return NULL_TREE; } +/* Handle a "counted_by" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_counted_by_attribute (tree *node, tree name, + tree args, int ARG_UNUSED (flags), + bool *no_add_attrs) +{ + tree decl = *node; + tree argval = TREE_VALUE (args); + + /* This attribute only applies to field decls of a structure. */ + if (TREE_CODE (decl) != FIELD_DECL) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%qE attribute may not be specified for non-field" + " declaration %q+D", name, decl); + *no_add_attrs = true; + } + /* This attribute only applies to field with array type. */ + else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%qE attribute may not be specified for a non-array field", + name); + *no_add_attrs = true; + } + /* This attribute only applies to a C99 flexible array member type. */ + else if (! c_flexible_array_member_type_p (TREE_TYPE (decl))) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%qE attribute may not be specified for a non" + " flexible array member field", + name); + *no_add_attrs = true; + } + /* The argument should be an identifier. */ + else if (TREE_CODE (argval) != IDENTIFIER_NODE) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%<counted_by%> argument not an identifier"); + *no_add_attrs = true; + } + + return NULL_TREE; +} + /* Handle a "weak" attribute; arguments as in struct attribute_spec.handler. */ diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index 9fbaeb437a12..a18937245c2a 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -9521,6 +9521,19 @@ c_common_finalize_early_debug (void) (*debug_hooks->early_global_decl) (cnode->decl); } +/* Determine whether TYPE is a ISO C99 flexible array memeber type "[]". */ +bool +c_flexible_array_member_type_p (const_tree type) +{ + if (TREE_CODE (type) == ARRAY_TYPE + && TYPE_SIZE (type) == NULL_TREE + && TYPE_DOMAIN (type) != NULL_TREE + && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) + return true; + + return false; +} + /* Get the LEVEL of the strict_flex_array for the ARRAY_FIELD based on the values of attribute strict_flex_array and the flag_strict_flex_arrays. */ unsigned int diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 78fc5248ba68..c29bb429062b 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -909,6 +909,7 @@ extern tree fold_for_warn (tree); extern tree c_common_get_narrower (tree, int *); extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *); extern void c_common_finalize_early_debug (void); +extern bool c_flexible_array_member_type_p (const_tree); extern unsigned int c_strict_flex_array_level_of (tree); extern bool c_option_is_from_cpp_diagnostics (int); diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 1f9eb44dbaa2..e943b49b5230 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -5173,19 +5173,6 @@ set_array_declarator_inner (struct c_declarator *decl, return decl; } -/* Determine whether TYPE is a ISO C99 flexible array memeber type "[]". */ -static bool -flexible_array_member_type_p (const_tree type) -{ - if (TREE_CODE (type) == ARRAY_TYPE - && TYPE_SIZE (type) == NULL_TREE - && TYPE_DOMAIN (type) != NULL_TREE - && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE) - return true; - - return false; -} - /* Determine whether TYPE is a one-element array type "[1]". */ static bool one_element_array_type_p (const_tree type) @@ -5222,7 +5209,7 @@ add_flexible_array_elts_to_size (tree decl, tree init) elt = CONSTRUCTOR_ELTS (init)->last ().value; type = TREE_TYPE (elt); - if (flexible_array_member_type_p (type)) + if (c_flexible_array_member_type_p (type)) { complete_array_type (&type, elt, false); DECL_SIZE (decl) @@ -9094,7 +9081,7 @@ is_flexible_array_member_p (bool is_last_field, bool is_zero_length_array = zero_length_array_type_p (TREE_TYPE (x)); bool is_one_element_array = one_element_array_type_p (TREE_TYPE (x)); - bool is_flexible_array = flexible_array_member_type_p (TREE_TYPE (x)); + bool is_flexible_array = c_flexible_array_member_type_p (TREE_TYPE (x)); unsigned int strict_flex_array_level = c_strict_flex_array_level_of (x); @@ -9124,6 +9111,61 @@ is_flexible_array_member_p (bool is_last_field, return false; } +/* Verify the argument of the counted_by attribute of the flexible array + member FIELD_DECL is a valid field of the containing structure's fieldlist, + FIELDLIST, Report error and remove this attribute when it's not. */ +static void +verify_counted_by_attribute (tree fieldlist, tree field_decl) +{ + tree attr_counted_by = lookup_attribute ("counted_by", + DECL_ATTRIBUTES (field_decl)); + + if (!attr_counted_by) + return; + + /* If there is an counted_by attribute attached to the field, + verify it. */ + + const char *fieldname + = IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (attr_counted_by))); + + /* Verify the argument of the attrbute is a valid field of the + containing structure. */ + + tree counted_by_field = get_named_field (fieldlist, fieldname); + + /* Error when the field is not found in the containing structure. */ + if (!counted_by_field) + { + error_at (DECL_SOURCE_LOCATION (field_decl), + "%qE attribute argument not a field declaration" + " in the same structure, ignore it", + (get_attribute_name (attr_counted_by))); + + 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 (TREE_CODE (TREE_TYPE (real_field)) != INTEGER_TYPE) + { + error_at (DECL_SOURCE_LOCATION (field_decl), + "%qE attribute argument not a field declaration" + " with integer type, ignore it", + (get_attribute_name (attr_counted_by))); + + DECL_ATTRIBUTES (field_decl) + = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); + } + } + + return; +} /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T. LOC is the location of the RECORD_TYPE or UNION_TYPE's definition. @@ -9244,7 +9286,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, DECL_PACKED (x) = 1; /* Detect flexible array member in an invalid context. */ - if (flexible_array_member_type_p (TREE_TYPE (x))) + if (c_flexible_array_member_type_p (TREE_TYPE (x))) { if (TREE_CODE (t) == UNION_TYPE) { @@ -9265,6 +9307,9 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, "members"); TREE_TYPE (x) = error_mark_node; } + /* if there is a counted_by attribute attached to this field, + verify it. */ + verify_counted_by_attribute (fieldlist, x); } if (pedantic && TREE_CODE (t) == RECORD_TYPE @@ -9279,7 +9324,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, when x is an array and is the last field. */ if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE) TYPE_INCLUDES_FLEXARRAY (t) - = is_last_field && flexible_array_member_type_p (TREE_TYPE (x)); + = is_last_field && c_flexible_array_member_type_p (TREE_TYPE (x)); /* Recursively set TYPE_INCLUDES_FLEXARRAY for the context of x, t when x is an union or record and is the last field. */ else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (x))) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 97eaacf8a7ec..ea6240646936 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -7617,6 +7617,83 @@ When both the attribute and the option present at the same time, the level of the strictness for the specific trailing array field is determined by the attribute. +@cindex @code{counted_by} variable attribute +@item counted_by (@var{count}) +The @code{counted_by} attribute may be attached to the flexible array +member of a structure. It indicates that the number of the elements of the +array is given by the field named "@var{count}" in the same structure as the +flexible array member. GCC uses this information to improve the results of +the array bound sanitizer and the @code{__builtin_dynamic_object_size}. + +For instance, the following code: + +@smallexample +struct P @{ + size_t count; + char other; + char array[] __attribute__ ((counted_by (count))); +@} *p; +@end smallexample + +@noindent +specifies that the @code{array} is a flexible array member whose number of +elements is given by the field @code{count} in the same structure. + +The field that represents the number of the elements should have an integer +type. An explicit @code{counted_by} annotation defines a relationship between +two objects, @code{p->array} and @code{p->count}, that @code{p->array} has +@emph{at least} @code{p->count} number of elements available. This relationship +must hold even after any of these related objects are updated. It's the user's +responsibility to make sure this relationship to be kept all the time. +Otherwise the results of the array bound sanitizer and the +@code{__builtin_dynamic_object_size} might be incorrect. + +For instance, in the following example, the allocated array has less elements +than what's specified by the @code{sbuf->count}, this is an user error. As a +result, out-of-bounds access to the array might not be detected. + +@smallexample +#define SIZE_BUMP 10 +struct P *sbuf; +void alloc_buf (size_t nelems) +@{ + sbuf = (struct P *) malloc (MAX (sizeof (struct P), + (offsetof (struct P, array[0]) + + nelems * sizeof (char)))); + sbuf->count = nelems + SIZE_BUMP; + /* This is invalid when the sbuf->array has less than sbuf->count + elements. */ +@} +@end smallexample + +In the following example, the 2nd update to the field @code{sbuf->count} of +the above structure will permit out-of-bounds access to the array +@code{sbuf>array} as well. + +@smallexample +#define SIZE_BUMP 10 +struct P *sbuf; +void alloc_buf (size_t nelems) +@{ + sbuf = (struct P *) malloc (MAX (sizeof (struct P), + (offsetof (struct P, array[0]) + + (nelems + SIZE_BUMP) * sizeof (char)))); + sbuf->count = nelems; + /* This is valid when the sbuf->array has at least sbuf->count + elements. */ +@} +void use_buf (int index) +@{ + sbuf->count = sbuf->count + SIZE_BUMP + 1; + /* Now the value of sbuf->count is larger than the number + of elements of sbuf->array. */ + sbuf->array[index] = 0; + /* then the out-of-bound access to this array + might not be detected. */ +@} +@end smallexample + + @cindex @code{alloc_size} variable attribute @item alloc_size (@var{position}) @itemx alloc_size (@var{position-1}, @var{position-2}) diff --git a/gcc/testsuite/gcc.dg/flex-array-counted-by.c b/gcc/testsuite/gcc.dg/flex-array-counted-by.c new file mode 100644 index 000000000000..f8ce9776bf86 --- /dev/null +++ b/gcc/testsuite/gcc.dg/flex-array-counted-by.c @@ -0,0 +1,40 @@ +/* testing the correct usage of attribute counted_by. */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +#include <wchar.h> + +int size; +int x __attribute ((counted_by (size))); /* { dg-error "attribute may not be specified for non-field declaration" } */ + +struct trailing { + int count; + int field __attribute ((counted_by (count))); /* { dg-error "attribute may not be specified for a non-array field" } */ +}; + +struct trailing_1 { + int count; + int array_1[0] __attribute ((counted_by (count))); /* { dg-error "attribute may not be specified for a non flexible array member field" } */ +}; + +int count; +struct trailing_array_2 { + int count; + int array_2[] __attribute ((counted_by ("count"))); /* { dg-error "argument not an identifier" } */ +}; + +struct trailing_array_3 { + int other; + int array_3[] __attribute ((counted_by (L"count"))); /* { dg-error "argument not an identifier" } */ +}; + +struct trailing_array_4 { + int other; + int array_4[] __attribute ((counted_by (count))); /* { dg-error "attribute argument not a field declaration in the same structure, ignore it" } */ +}; + +int count; +struct trailing_array_5 { + float count; + int array_5[] __attribute ((counted_by (count))); /* { dg-error "attribute argument not a field declaration with integer type, ignore it" } */ +}; diff --git a/gcc/tree.cc b/gcc/tree.cc index 420857b110c4..fcd36ae0cd74 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -12745,6 +12745,46 @@ array_ref_element_size (tree exp) return SUBSTITUTE_PLACEHOLDER_IN_EXPR (TYPE_SIZE_UNIT (elmt_type), exp); } +/* Given a field list, FIELDLIST, of a structure/union, return a TREE_LIST, + with each TREE_VALUE a FIELD_DECL stepping down the chain to the FIELD + whose name is FIELDNAME, which is the last TREE_VALUE of the list. + return NULL_TREE if such field is not found. Normally this list is of + length one, but if the field is embedded with (nested) anonymous structures + or unions, this list steps down the chain to the field. */ +tree +get_named_field (tree fieldlist, const char *fieldname) +{ + tree named_field = NULL_TREE; + for (tree field = fieldlist; field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) != FIELD_DECL) + continue; + if (DECL_NAME (field) != NULL) + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (field)), fieldname) == 0) + { + named_field = tree_cons (NULL_TREE, field, named_field); + break; + } + else + continue; + /* if the field is an anonymous struct/union, we will check the nested + fields inside it recursively. */ + else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))) + if ((named_field = get_named_field (TYPE_FIELDS (TREE_TYPE (field)), + fieldname)) != NULL_TREE) + { + named_field = tree_cons (NULL_TREE, field, named_field); + break; + } + else + continue; + else + continue; + } + return named_field; +} + + /* Return a tree representing the lower bound of the array mentioned in EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ diff --git a/gcc/tree.h b/gcc/tree.h index 4c04245e2b1b..4859becaa1e7 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5619,6 +5619,11 @@ extern tree get_base_address (tree t); of EXP, an ARRAY_REF or an ARRAY_RANGE_REF. */ extern tree array_ref_element_size (tree); +/* Given a field list, FIELDLIST, of a structure/union, return the FIELD whose + name is FIELDNAME, return NULL_TREE if such field is not found. + searching nested anonymous structure/union recursively. */ +extern tree get_named_field (tree, const char *); + /* Return a typenode for the "standard" C type with a given name. */ extern tree get_typenode_from_name (const char *);