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