Skip to content

Commit fa82a97

Browse files
committed
[Bounds-Safety] Add sized_by, counted_by_or_null & sized_by_or_null
The attributes `sized_by`, `counted_by_or_null` and `sized_by_or_null` have been added as variants on `counted_by`, each with slightly different semantics. `sized_by` takes a byte size parameter instead of an element count, allowing pointees with unknown size. The `counted_by_or_null` and `sized_by_or_null` variants are equivalent to their base variants, except the pointer can be null regardless of count/size value. If the pointer is null the size is effectively 0. rdar://125400354
1 parent 112eadd commit fa82a97

30 files changed

+2153
-31
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,12 @@ Attribute Changes in Clang
425425
size_t count;
426426
};
427427
428+
- The attributes ``sized_by``, ``counted_by_or_null`` and ``sized_by_or_null```
429+
have been added as variants on ``counted_by``, each with slightly different semantics.
430+
``sized_by`` takes a byte size parameter instead of an element count, allowing pointees
431+
with unknown size. The ``counted_by_or_null`` and ``sized_by_or_null`` variants are equivalent
432+
to their base variants, except the pointer can be null regardless of count/size value.
433+
If the pointer is null the size is effectively 0.
428434

429435
Improvements to Clang's diagnostics
430436
-----------------------------------

clang/include/clang/Basic/Attr.td

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,6 +2236,36 @@ def CountedBy : DeclOrTypeAttr {
22362236
let LangOpts = [COnly];
22372237
}
22382238

2239+
def CountedByOrNull : DeclOrTypeAttr {
2240+
let Spellings = [Clang<"counted_by_or_null">];
2241+
let Subjects = SubjectList<[Field], ErrorDiag>;
2242+
let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>];
2243+
let LateParsed = LateAttrParseExperimentalExt;
2244+
let ParseArgumentsAsUnevaluated = 1;
2245+
let Documentation = [CountedByDocs];
2246+
let LangOpts = [COnly];
2247+
}
2248+
2249+
def SizedBy : DeclOrTypeAttr {
2250+
let Spellings = [Clang<"sized_by">];
2251+
let Subjects = SubjectList<[Field], ErrorDiag>;
2252+
let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>];
2253+
let LateParsed = LateAttrParseExperimentalExt;
2254+
let ParseArgumentsAsUnevaluated = 1;
2255+
let Documentation = [CountedByDocs];
2256+
let LangOpts = [COnly];
2257+
}
2258+
2259+
def SizedByOrNull : DeclOrTypeAttr {
2260+
let Spellings = [Clang<"sized_by_or_null">];
2261+
let Subjects = SubjectList<[Field], ErrorDiag>;
2262+
let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>];
2263+
let LateParsed = LateAttrParseExperimentalExt;
2264+
let ParseArgumentsAsUnevaluated = 1;
2265+
let Documentation = [CountedByDocs];
2266+
let LangOpts = [COnly];
2267+
}
2268+
22392269
// This is a marker used to indicate that an __unsafe_unretained qualifier was
22402270
// ignored because ARC is not enabled. The usual representation for this
22412271
// qualifier is as an ObjCOwnership attribute with Kind == "none".

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6532,27 +6532,27 @@ def warn_superclass_variable_sized_type_not_at_end : Warning<
65326532
" in superclass %3">, InGroup<ObjCFlexibleArray>;
65336533

65346534
def err_flexible_array_count_not_in_same_struct : Error<
6535-
"'counted_by' field %0 isn't within the same struct as the flexible array">;
6535+
"'%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">;
65366536
def err_counted_by_attr_not_on_ptr_or_flexible_array_member : Error<
6537-
"'counted_by' only applies to pointers or C99 flexible array members">;
6537+
"'%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">;
65386538
def err_counted_by_attr_on_array_not_flexible_array_member : Error<
65396539
"'counted_by' on arrays only applies to C99 flexible array members">;
65406540
def err_counted_by_attr_refer_to_itself : Error<
65416541
"'counted_by' cannot refer to the flexible array member %0">;
65426542
def err_counted_by_must_be_in_structure : Error<
6543-
"field %0 in 'counted_by' not inside structure">;
6543+
"field %0 in '%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' not inside structure">;
65446544
def err_counted_by_attr_argument_not_integer : Error<
6545-
"'counted_by' requires a non-boolean integer type argument">;
6545+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' requires a non-boolean integer type argument">;
65466546
def err_counted_by_attr_only_support_simple_decl_reference : Error<
6547-
"'counted_by' argument must be a simple declaration reference">;
6547+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' argument must be a simple declaration reference">;
65486548
def err_counted_by_attr_in_union : Error<
6549-
"'counted_by' cannot be applied to a union member">;
6549+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' cannot be applied to a union member">;
65506550
def err_counted_by_attr_refer_to_union : Error<
6551-
"'counted_by' argument cannot refer to a union member">;
6551+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' argument cannot refer to a union member">;
65526552
def note_flexible_array_counted_by_attr_field : Note<
65536553
"field %0 declared here">;
65546554
def err_counted_by_attr_pointee_unknown_size : Error<
6555-
"'counted_by' cannot be applied to %select{"
6555+
"'%select{counted_by|counted_by_or_null}3' cannot be applied to %select{"
65566556
"a pointer with pointee|" // pointer
65576557
"an array with element}0" // array
65586558
" of unknown size because %1 is %select{"

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11397,7 +11397,9 @@ class Sema final : public SemaBase {
1139711397
SourceLocation AttrLoc);
1139811398

1139911399
QualType BuildCountAttributedArrayOrPointerType(QualType WrappedTy,
11400-
Expr *CountExpr);
11400+
Expr *CountExpr,
11401+
bool CountInBytes,
11402+
bool OrNull);
1140111403

1140211404
QualType BuildAddressSpaceAttr(QualType &T, LangAS ASIdx, Expr *AddrSpace,
1140311405
SourceLocation AttrLoc);

clang/lib/AST/TypePrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
19051905
break;
19061906

19071907
case attr::CountedBy:
1908+
case attr::CountedByOrNull:
1909+
case attr::SizedBy:
1910+
case attr::SizedByOrNull:
19081911
case attr::LifetimeBound:
19091912
case attr::TypeNonNull:
19101913
case attr::TypeNullable:

clang/lib/Parse/ParseDecl.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,10 @@ void Parser::ParseGNUAttributeArgs(
662662
ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, ScopeName,
663663
ScopeLoc, Form);
664664
return;
665-
} else if (AttrKind == ParsedAttr::AT_CountedBy) {
665+
} else if (AttrKind == ParsedAttr::AT_CountedBy ||
666+
AttrKind == ParsedAttr::AT_CountedByOrNull ||
667+
AttrKind == ParsedAttr::AT_SizedBy ||
668+
AttrKind == ParsedAttr::AT_SizedByOrNull) {
666669
ParseBoundsAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc,
667670
Form);
668671
return;
@@ -4806,7 +4809,7 @@ static void DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS,
48064809
if (!RD->containsDecl(DD.getDecl())) {
48074810
P.Diag(VD->getBeginLoc(),
48084811
diag::err_flexible_array_count_not_in_same_struct)
4809-
<< DD.getDecl();
4812+
<< DD.getDecl() << CAT->getKind();
48104813
P.Diag(DD.getDecl()->getBeginLoc(),
48114814
diag::note_flexible_array_counted_by_attr_field)
48124815
<< DD.getDecl();
@@ -4964,6 +4967,9 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute &LA,
49644967
/*SyntaxUsed=*/ParsedForm.getSyntax());
49654968
switch (AttrKind) {
49664969
case ParsedAttr::Kind::AT_CountedBy:
4970+
case ParsedAttr::Kind::AT_CountedByOrNull:
4971+
case ParsedAttr::Kind::AT_SizedBy:
4972+
case ParsedAttr::Kind::AT_SizedByOrNull:
49674973
ParseBoundsAttribute(LA.AttrName, LA.AttrNameLoc, Attrs,
49684974
/*ScopeName=*/ScopeName, SourceLocation(),
49694975
/*Form=*/ParsedForm);

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8641,22 +8641,33 @@ enum class CountedByInvalidPointeeTypeKind {
86418641
VALID,
86428642
};
86438643

8644-
static bool CheckCountedByAttrOnField(
8645-
Sema &S, FieldDecl *FD, Expr *E,
8646-
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls) {
8644+
static bool
8645+
CheckCountedByAttrOnField(Sema &S, FieldDecl *FD, Expr *E,
8646+
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls,
8647+
bool CountInBytes, bool OrNull) {
86478648
// Check the context the attribute is used in
86488649

8650+
unsigned Kind = CountInBytes;
8651+
if (OrNull)
8652+
Kind += 2;
8653+
86498654
if (FD->getParent()->isUnion()) {
86508655
S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_in_union)
8651-
<< FD->getSourceRange();
8656+
<< Kind << FD->getSourceRange();
86528657
return true;
86538658
}
86548659

86558660
const auto FieldTy = FD->getType();
8661+
if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
8662+
S.Diag(FD->getBeginLoc(),
8663+
diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member)
8664+
<< Kind << FD->getLocation();
8665+
return true;
8666+
}
86568667
if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {
86578668
S.Diag(FD->getBeginLoc(),
86588669
diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member)
8659-
<< FD->getLocation();
8670+
<< Kind << FD->getLocation();
86608671
return true;
86618672
}
86628673

@@ -8667,7 +8678,7 @@ static bool CheckCountedByAttrOnField(
86678678
StrictFlexArraysLevel, true)) {
86688679
S.Diag(FD->getBeginLoc(),
86698680
diag::err_counted_by_attr_on_array_not_flexible_array_member)
8670-
<< FD->getLocation();
8681+
<< Kind << FD->getLocation();
86718682
return true;
86728683
}
86738684

@@ -8697,9 +8708,10 @@ static bool CheckCountedByAttrOnField(
86978708
InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;
86988709
}
86998710

8700-
if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
8711+
if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID &&
8712+
!CountInBytes) {
87018713
S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_pointee_unknown_size)
8702-
<< SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
8714+
<< SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind << OrNull
87038715
<< FD->getSourceRange();
87048716
return true;
87058717
}
@@ -8708,15 +8720,15 @@ static bool CheckCountedByAttrOnField(
87088720

87098721
if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
87108722
S.Diag(E->getBeginLoc(), diag::err_counted_by_attr_argument_not_integer)
8711-
<< E->getSourceRange();
8723+
<< Kind << E->getSourceRange();
87128724
return true;
87138725
}
87148726

87158727
auto *DRE = dyn_cast<DeclRefExpr>(E);
87168728
if (!DRE) {
87178729
S.Diag(E->getBeginLoc(),
87188730
diag::err_counted_by_attr_only_support_simple_decl_reference)
8719-
<< E->getSourceRange();
8731+
<< Kind << E->getSourceRange();
87208732
return true;
87218733
}
87228734

@@ -8727,7 +8739,7 @@ static bool CheckCountedByAttrOnField(
87278739
}
87288740
if (!CountFD) {
87298741
S.Diag(E->getBeginLoc(), diag::err_counted_by_must_be_in_structure)
8730-
<< CountDecl << E->getSourceRange();
8742+
<< CountDecl << Kind << E->getSourceRange();
87318743

87328744
S.Diag(CountDecl->getBeginLoc(),
87338745
diag::note_flexible_array_counted_by_attr_field)
@@ -8738,7 +8750,7 @@ static bool CheckCountedByAttrOnField(
87388750
if (FD->getParent() != CountFD->getParent()) {
87398751
if (CountFD->getParent()->isUnion()) {
87408752
S.Diag(CountFD->getBeginLoc(), diag::err_counted_by_attr_refer_to_union)
8741-
<< CountFD->getSourceRange();
8753+
<< Kind << CountFD->getSourceRange();
87428754
return true;
87438755
}
87448756
// Whether CountRD is an anonymous struct is not determined at this
@@ -8750,7 +8762,7 @@ static bool CheckCountedByAttrOnField(
87508762
if (RD != CountRD) {
87518763
S.Diag(E->getBeginLoc(),
87528764
diag::err_flexible_array_count_not_in_same_struct)
8753-
<< CountFD << E->getSourceRange();
8765+
<< CountFD << Kind << E->getSourceRange();
87548766
S.Diag(CountFD->getBeginLoc(),
87558767
diag::note_flexible_array_counted_by_attr_field)
87568768
<< CountFD << CountFD->getSourceRange();
@@ -8770,12 +8782,35 @@ static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) {
87708782
if (!CountExpr)
87718783
return;
87728784

8785+
bool CountInBytes;
8786+
bool OrNull;
8787+
switch (AL.getKind()) {
8788+
case ParsedAttr::AT_CountedBy:
8789+
CountInBytes = false;
8790+
OrNull = false;
8791+
break;
8792+
case ParsedAttr::AT_CountedByOrNull:
8793+
CountInBytes = false;
8794+
OrNull = true;
8795+
break;
8796+
case ParsedAttr::AT_SizedBy:
8797+
CountInBytes = true;
8798+
OrNull = false;
8799+
break;
8800+
case ParsedAttr::AT_SizedByOrNull:
8801+
CountInBytes = true;
8802+
OrNull = true;
8803+
break;
8804+
default:
8805+
llvm_unreachable("unexpected counted_by family attribute");
8806+
}
8807+
87738808
llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
8774-
if (CheckCountedByAttrOnField(S, FD, CountExpr, Decls))
8809+
if (CheckCountedByAttrOnField(S, FD, CountExpr, Decls, CountInBytes, OrNull))
87758810
return;
87768811

8777-
QualType CAT =
8778-
S.BuildCountAttributedArrayOrPointerType(FD->getType(), CountExpr);
8812+
QualType CAT = S.BuildCountAttributedArrayOrPointerType(
8813+
FD->getType(), CountExpr, CountInBytes, OrNull);
87798814
FD->setType(CAT);
87808815
}
87818816

@@ -9802,6 +9837,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
98029837
break;
98039838

98049839
case ParsedAttr::AT_CountedBy:
9840+
case ParsedAttr::AT_CountedByOrNull:
9841+
case ParsedAttr::AT_SizedBy:
9842+
case ParsedAttr::AT_SizedByOrNull:
98059843
handleCountedByAttrField(S, D, AL);
98069844
break;
98079845

clang/lib/Sema/SemaType.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9346,15 +9346,17 @@ BuildTypeCoupledDecls(Expr *E,
93469346
}
93479347

93489348
QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy,
9349-
Expr *CountExpr) {
9349+
Expr *CountExpr,
9350+
bool CountInBytes,
9351+
bool OrNull) {
93509352
assert(WrappedTy->isIncompleteArrayType() || WrappedTy->isPointerType());
93519353

93529354
llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
93539355
BuildTypeCoupledDecls(CountExpr, Decls);
93549356
/// When the resulting expression is invalid, we still create the AST using
93559357
/// the original count expression for the sake of AST dump.
9356-
return Context.getCountAttributedType(
9357-
WrappedTy, CountExpr, /*CountInBytes*/ false, /*OrNull*/ false, Decls);
9358+
return Context.getCountAttributedType(WrappedTy, CountExpr, CountInBytes,
9359+
OrNull, Decls);
93589360
}
93599361

93609362
/// getDecltypeForExpr - Given an expr, will return the decltype for

clang/lib/Sema/TreeTransform.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7344,7 +7344,8 @@ QualType TreeTransform<Derived>::TransformCountAttributedType(
73447344
if (getDerived().AlwaysRebuild() || InnerTy != OldTy->desugar() ||
73457345
OldCount != NewCount) {
73467346
// Currently, CountAttributedType can only wrap incomplete array types.
7347-
Result = SemaRef.BuildCountAttributedArrayOrPointerType(InnerTy, NewCount);
7347+
Result = SemaRef.BuildCountAttributedArrayOrPointerType(
7348+
InnerTy, NewCount, OldTy->isCountInBytes(), OldTy->isOrNull());
73487349
}
73497350

73507351
TLB.push<CountAttributedTypeLoc>(Result);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %clang_cc1 -fexperimental-late-parse-attributes %s -ast-dump | FileCheck %s
2+
3+
#define __counted_by_or_null(f) __attribute__((counted_by_or_null(f)))
4+
5+
struct size_known {
6+
int field;
7+
};
8+
9+
//==============================================================================
10+
// __counted_by_or_null on struct member pointer in decl attribute position
11+
//==============================================================================
12+
13+
struct on_member_pointer_complete_ty {
14+
struct size_known *buf __counted_by_or_null(count);
15+
int count;
16+
};
17+
// CHECK-LABEL: struct on_member_pointer_complete_ty definition
18+
// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by_or_null(count)':'struct size_known *'
19+
// CHECK-NEXT: `-FieldDecl {{.*}} referenced count 'int'
20+
21+
struct on_pointer_anon_count {
22+
struct size_known *buf __counted_by_or_null(count);
23+
struct {
24+
int count;
25+
};
26+
};
27+
28+
// CHECK-LABEL: struct on_pointer_anon_count definition
29+
// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by_or_null(count)':'struct size_known *'
30+
// CHECK-NEXT: |-RecordDecl {{.*}} struct definition
31+
// CHECK-NEXT: | `-FieldDecl {{.*}} count 'int'
32+
// CHECK-NEXT: |-FieldDecl {{.*}} implicit 'struct on_pointer_anon_count::(anonymous at {{.*}})'
33+
// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit referenced count 'int'
34+
// CHECK-NEXT: |-Field {{.*}} '' 'struct on_pointer_anon_count::(anonymous at {{.*}})'
35+
// CHECK-NEXT: `-Field {{.*}} 'count' 'int'
36+
37+
//==============================================================================
38+
// __counted_by_or_null on struct member pointer in type attribute position
39+
//==============================================================================
40+
// TODO: Correctly parse counted_by_or_null as a type attribute. Currently it is parsed
41+
// as a declaration attribute and is **not** late parsed resulting in the `count`
42+
// field being unavailable.
43+
//
44+
// See `clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c` for test
45+
// cases.

0 commit comments

Comments
 (0)