Skip to content

Commit eb4e65b

Browse files
committed
[Bounds-Safety] Address review comments
- use enum to index into diagnostics - rename err_counted_by* diagnostics to err_count_attr* when the diagnostic applies to other attributes than only counted_by (sized_by, counted_by_or_null, sized_by_or_null) - suggest using counted_by when arrays are annotated with sized_by, counted_by_or_null or sized_by_or_null - update release notes with example use case for sized_by_or_null
1 parent 5b75087 commit eb4e65b

20 files changed

+483
-53
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,9 @@ Attribute Changes in Clang
559559
``sized_by`` takes a byte size parameter instead of an element count, allowing pointees
560560
with unknown size. The ``counted_by_or_null`` and ``sized_by_or_null`` variants are equivalent
561561
to their base variants, except the pointer can be null regardless of count/size value.
562-
If the pointer is null the size is effectively 0.
562+
If the pointer is null the size is effectively 0. ``sized_by_or_null`` is needed to properly
563+
annotate allocator functions like ``malloc`` that return a buffer of a given byte size, but can
564+
also return null.
563565

564566
- The ``guarded_by``, ``pt_guarded_by``, ``acquired_after``, ``acquired_before``
565567
attributes now support referencing struct members in C. The arguments are also

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6567,23 +6567,23 @@ def warn_superclass_variable_sized_type_not_at_end : Warning<
65676567
"field %0 can overwrite instance variable %1 with variable sized type %2"
65686568
" in superclass %3">, InGroup<ObjCFlexibleArray>;
65696569

6570-
def err_flexible_array_count_not_in_same_struct : Error<
6571-
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' field %0 isn't within the same struct as the flexible array">;
6572-
def err_counted_by_attr_not_on_ptr_or_flexible_array_member : Error<
6573-
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' only applies to pointers%select{ or C99 flexible array members|||}0">;
6570+
def err_count_attr_param_not_in_same_struct : Error<
6571+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' field %0 isn't within the same struct as the annotated %select{pointer|flexible array}2">;
6572+
def err_count_attr_not_on_ptr_or_flexible_array_member : Error<
6573+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' only applies to pointers%select{ or C99 flexible array members|||}0%select{|; did you mean to use 'counted_by'?}1">;
65746574
def err_counted_by_attr_on_array_not_flexible_array_member : Error<
65756575
"'counted_by' on arrays only applies to C99 flexible array members">;
65766576
def err_counted_by_attr_refer_to_itself : Error<
65776577
"'counted_by' cannot refer to the flexible array member %0">;
6578-
def err_counted_by_must_be_in_structure : Error<
6578+
def err_count_attr_must_be_in_structure : Error<
65796579
"field %0 in '%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' not inside structure">;
6580-
def err_counted_by_attr_argument_not_integer : Error<
6580+
def err_count_attr_argument_not_integer : Error<
65816581
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' requires a non-boolean integer type argument">;
6582-
def err_counted_by_attr_only_support_simple_decl_reference : Error<
6582+
def err_count_attr_only_support_simple_decl_reference : Error<
65836583
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' argument must be a simple declaration reference">;
6584-
def err_counted_by_attr_in_union : Error<
6584+
def err_count_attr_in_union : Error<
65856585
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' cannot be applied to a union member">;
6586-
def err_counted_by_attr_refer_to_union : Error<
6586+
def err_count_attr_refer_to_union : Error<
65876587
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' argument cannot refer to a union member">;
65886588
def note_flexible_array_counted_by_attr_field : Note<
65896589
"field %0 declared here">;

clang/lib/Parse/ParseDecl.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4869,9 +4869,8 @@ static void DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS,
48694869

48704870
for (const auto &DD : CAT->dependent_decls()) {
48714871
if (!RD->containsDecl(DD.getDecl())) {
4872-
P.Diag(VD->getBeginLoc(),
4873-
diag::err_flexible_array_count_not_in_same_struct)
4874-
<< DD.getDecl() << CAT->getKind();
4872+
P.Diag(VD->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
4873+
<< DD.getDecl() << CAT->getKind() << CAT->isArrayType();
48754874
P.Diag(DD.getDecl()->getBeginLoc(),
48764875
diag::note_flexible_array_counted_by_attr_field)
48774876
<< DD.getDecl();

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5868,6 +5868,15 @@ static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) {
58685868
return RD;
58695869
}
58705870

5871+
static CountAttributedType::DynamicCountPointerKind
5872+
getCountAttrKind(bool CountInBytes, bool OrNull) {
5873+
if (CountInBytes)
5874+
return OrNull ? CountAttributedType::SizedByOrNull
5875+
: CountAttributedType::SizedBy;
5876+
return OrNull ? CountAttributedType::CountedByOrNull
5877+
: CountAttributedType::CountedBy;
5878+
}
5879+
58715880
enum class CountedByInvalidPointeeTypeKind {
58725881
INCOMPLETE,
58735882
SIZELESS,
@@ -5882,27 +5891,25 @@ CheckCountedByAttrOnField(Sema &S, FieldDecl *FD, Expr *E,
58825891
bool CountInBytes, bool OrNull) {
58835892
// Check the context the attribute is used in
58845893

5885-
unsigned Kind = CountInBytes;
5886-
if (OrNull)
5887-
Kind += 2;
5894+
unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
58885895

58895896
if (FD->getParent()->isUnion()) {
5890-
S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_in_union)
5897+
S.Diag(FD->getBeginLoc(), diag::err_count_attr_in_union)
58915898
<< Kind << FD->getSourceRange();
58925899
return true;
58935900
}
58945901

58955902
const auto FieldTy = FD->getType();
58965903
if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
58975904
S.Diag(FD->getBeginLoc(),
5898-
diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member)
5899-
<< Kind << FD->getLocation();
5905+
diag::err_count_attr_not_on_ptr_or_flexible_array_member)
5906+
<< Kind << FD->getLocation() << /* suggest counted_by */ 1;
59005907
return true;
59015908
}
59025909
if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {
59035910
S.Diag(FD->getBeginLoc(),
5904-
diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member)
5905-
<< Kind << FD->getLocation();
5911+
diag::err_count_attr_not_on_ptr_or_flexible_array_member)
5912+
<< Kind << FD->getLocation() << /* do not suggest counted_by */ 0;
59065913
return true;
59075914
}
59085915

@@ -5967,15 +5974,15 @@ CheckCountedByAttrOnField(Sema &S, FieldDecl *FD, Expr *E,
59675974
// Check the expression
59685975

59695976
if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
5970-
S.Diag(E->getBeginLoc(), diag::err_counted_by_attr_argument_not_integer)
5977+
S.Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)
59715978
<< Kind << E->getSourceRange();
59725979
return true;
59735980
}
59745981

59755982
auto *DRE = dyn_cast<DeclRefExpr>(E);
59765983
if (!DRE) {
59775984
S.Diag(E->getBeginLoc(),
5978-
diag::err_counted_by_attr_only_support_simple_decl_reference)
5985+
diag::err_count_attr_only_support_simple_decl_reference)
59795986
<< Kind << E->getSourceRange();
59805987
return true;
59815988
}
@@ -5986,7 +5993,7 @@ CheckCountedByAttrOnField(Sema &S, FieldDecl *FD, Expr *E,
59865993
CountFD = IFD->getAnonField();
59875994
}
59885995
if (!CountFD) {
5989-
S.Diag(E->getBeginLoc(), diag::err_counted_by_must_be_in_structure)
5996+
S.Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)
59905997
<< CountDecl << Kind << E->getSourceRange();
59915998

59925999
S.Diag(CountDecl->getBeginLoc(),
@@ -5997,7 +6004,7 @@ CheckCountedByAttrOnField(Sema &S, FieldDecl *FD, Expr *E,
59976004

59986005
if (FD->getParent() != CountFD->getParent()) {
59996006
if (CountFD->getParent()->isUnion()) {
6000-
S.Diag(CountFD->getBeginLoc(), diag::err_counted_by_attr_refer_to_union)
6007+
S.Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union)
60016008
<< Kind << CountFD->getSourceRange();
60026009
return true;
60036010
}
@@ -6008,9 +6015,8 @@ CheckCountedByAttrOnField(Sema &S, FieldDecl *FD, Expr *E,
60086015
auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);
60096016

60106017
if (RD != CountRD) {
6011-
S.Diag(E->getBeginLoc(),
6012-
diag::err_flexible_array_count_not_in_same_struct)
6013-
<< CountFD << Kind << E->getSourceRange();
6018+
S.Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
6019+
<< CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();
60146020
S.Diag(CountFD->getBeginLoc(),
60156021
diag::note_flexible_array_counted_by_attr_field)
60166022
<< CountFD << CountFD->getSourceRange();
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
3+
#define __counted_by_or_null(f) __attribute__((counted_by_or_null(f)))
4+
5+
// This has been adapted from clang/test/Sema/attr-counted-by-vla.c, but with VLAs replaced with pointers
6+
7+
struct bar;
8+
9+
struct not_found {
10+
int count;
11+
struct bar *ptr __counted_by_or_null(bork); // expected-error {{use of undeclared identifier 'bork'}}
12+
};
13+
14+
struct no_found_count_not_in_substruct {
15+
unsigned long flags;
16+
unsigned char count; // expected-note {{'count' declared here}}
17+
struct A {
18+
int dummy;
19+
int * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' field 'count' isn't within the same struct as the annotated pointer}}
20+
} a;
21+
};
22+
23+
struct not_found_count_not_in_unnamed_substruct {
24+
unsigned char count; // expected-note {{'count' declared here}}
25+
struct {
26+
int dummy;
27+
int * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' field 'count' isn't within the same struct as the annotated pointer}}
28+
} a;
29+
};
30+
31+
struct not_found_count_not_in_unnamed_substruct_2 {
32+
struct {
33+
unsigned char count; // expected-note {{'count' declared here}}
34+
};
35+
struct {
36+
int dummy;
37+
int * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' field 'count' isn't within the same struct as the annotated pointer}}
38+
} a;
39+
};
40+
41+
struct not_found_count_in_other_unnamed_substruct {
42+
struct {
43+
unsigned char count;
44+
} a1;
45+
46+
struct {
47+
int dummy;
48+
int * ptr __counted_by_or_null(count); // expected-error {{use of undeclared identifier 'count'}}
49+
};
50+
};
51+
52+
struct not_found_count_in_other_substruct {
53+
struct _a1 {
54+
unsigned char count;
55+
} a1;
56+
57+
struct {
58+
int dummy;
59+
int * ptr __counted_by_or_null(count); // expected-error {{use of undeclared identifier 'count'}}
60+
};
61+
};
62+
63+
struct not_found_count_in_other_substruct_2 {
64+
struct _a2 {
65+
unsigned char count;
66+
} a2;
67+
68+
int * ptr __counted_by_or_null(count); // expected-error {{use of undeclared identifier 'count'}}
69+
};
70+
71+
struct not_found_suggest {
72+
int bork;
73+
struct bar **ptr __counted_by_or_null(blork); // expected-error {{use of undeclared identifier 'blork'}}
74+
};
75+
76+
int global; // expected-note {{'global' declared here}}
77+
78+
struct found_outside_of_struct {
79+
int bork;
80+
struct bar ** ptr __counted_by_or_null(global); // expected-error {{field 'global' in 'counted_by_or_null' not inside structure}}
81+
};
82+
83+
struct self_referrential {
84+
int bork;
85+
struct bar *self[] __counted_by_or_null(self); // expected-error {{use of undeclared identifier 'self'}}
86+
};
87+
88+
struct non_int_count {
89+
double dbl_count;
90+
struct bar ** ptr __counted_by_or_null(dbl_count); // expected-error {{'counted_by_or_null' requires a non-boolean integer type argument}}
91+
};
92+
93+
struct array_of_ints_count {
94+
int integers[2];
95+
struct bar ** ptr __counted_by_or_null(integers); // expected-error {{'counted_by_or_null' requires a non-boolean integer type argument}}
96+
};
97+
98+
struct not_a_c99_fam {
99+
int count;
100+
struct bar *non_c99_fam[0] __counted_by_or_null(count); // expected-error {{'counted_by_or_null' only applies to pointers; did you mean to use 'counted_by'?}}
101+
};
102+
103+
struct annotated_with_anon_struct {
104+
unsigned long flags;
105+
struct {
106+
unsigned char count;
107+
int * ptr __counted_by_or_null(crount); // expected-error {{use of undeclared identifier 'crount'}}
108+
};
109+
};
110+
111+
//==============================================================================
112+
// __counted_by_or_null on a struct ptr with element type that has unknown count
113+
//==============================================================================
114+
115+
struct count_unknown;
116+
struct on_member_ptr_incomplete_ty_ty_pos {
117+
int count;
118+
struct count_unknown * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'struct count_unknown' is an incomplete type}}
119+
};
120+
121+
struct on_member_ptr_incomplete_const_ty_ty_pos {
122+
int count;
123+
const struct count_unknown * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'const struct count_unknown' is an incomplete type}}
124+
};
125+
126+
struct on_member_ptr_void_ty_ty_pos {
127+
int count;
128+
void * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'void' is an incomplete type}}
129+
};
130+
131+
typedef void(fn_ty)(int);
132+
133+
struct on_member_ptr_fn_ptr_ty {
134+
int count;
135+
fn_ty* * ptr __counted_by_or_null(count);
136+
};
137+
138+
struct on_member_ptr_fn_ty {
139+
int count;
140+
fn_ty * ptr __counted_by_or_null(count); // expected-error {{'counted_by_or_null' cannot be applied to a pointer with pointee of unknown size because 'fn_ty' (aka 'void (int)') is a function type}}
141+
};

clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,13 @@ struct on_pointer_anon_count_ty_pos {
242242
//==============================================================================
243243

244244
struct on_pod_ty {
245-
// expected-error@+1{{'counted_by_or_null' only applies to pointers}}
245+
// expected-error-re@+1{{'counted_by_or_null' only applies to pointers{{$}}}}
246246
int wrong_ty __counted_by_or_null(count);
247247
int count;
248248
};
249249

250250
struct on_void_ty {
251-
// expected-error@+2{{'counted_by_or_null' only applies to pointers}}
251+
// expected-error-re@+2{{'counted_by_or_null' only applies to pointers{{$}}}}
252252
// expected-error@+1{{field has incomplete type 'void'}}
253253
void wrong_ty __counted_by_or_null(count);
254254
int count;

clang/test/Sema/attr-counted-by-or-null-struct-ptrs-sizeless-types.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct on_sizeless_pointee_ty {
1111

1212
struct on_sizeless_ty {
1313
int count;
14-
// expected-error@+2{{'counted_by_or_null' only applies to pointers}}
14+
// expected-error-re@+2{{'counted_by_or_null' only applies to pointers{{$}}}}
1515
// expected-error@+1{{field has sizeless type '__SVInt8_t'}}
1616
__SVInt8_t member __counted_by_or_null(count);
1717
};

clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,13 @@ struct on_pointer_anon_count_ty_pos {
213213

214214
struct on_pod_ty {
215215
int count;
216-
// expected-error@+1{{'counted_by_or_null' only applies to pointers}}
216+
// expected-error-re@+1{{'counted_by_or_null' only applies to pointers{{$}}}}
217217
int wrong_ty __counted_by_or_null(count);
218218
};
219219

220220
struct on_void_ty {
221221
int count;
222-
// expected-error@+2{{'counted_by_or_null' only applies to pointers}}
222+
// expected-error-re@+2{{'counted_by_or_null' only applies to pointers{{$}}}}
223223
// expected-error@+1{{field has incomplete type 'void'}}
224224
void wrong_ty __counted_by_or_null(count);
225225
};

clang/test/Sema/attr-counted-by-or-null-vla-sizeless-types.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
struct on_sizeless_elt_ty {
77
int count;
8-
// expected-error@+2{{'counted_by_or_null' only applies to pointers}}
8+
// expected-error-re@+2{{'counted_by_or_null' only applies to pointers{{$}}}}
99
// expected-error@+1{{array has sizeless element type '__SVInt8_t'}}
1010
__SVInt8_t arr[] __counted_by_or_null(count);
1111
};

clang/test/Sema/attr-counted-by-vla.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ struct no_found_count_not_in_substruct {
1414
unsigned char count; // expected-note {{'count' declared here}}
1515
struct A {
1616
int dummy;
17-
int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}}
17+
int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the annotated flexible array}}
1818
} a;
1919
};
2020

2121
struct not_found_count_not_in_unnamed_substruct {
2222
unsigned char count; // expected-note {{'count' declared here}}
2323
struct {
2424
int dummy;
25-
int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}}
25+
int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the annotated flexible array}}
2626
} a;
2727
};
2828

@@ -32,7 +32,7 @@ struct not_found_count_not_in_unnamed_substruct_2 {
3232
};
3333
struct {
3434
int dummy;
35-
int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the flexible array}}
35+
int array[] __counted_by(count); // expected-error {{'counted_by' field 'count' isn't within the same struct as the annotated flexible array}}
3636
} a;
3737
};
3838

0 commit comments

Comments
 (0)