Skip to content

Commit 634eafb

Browse files
hnrklssnaaryanshukla
authored andcommitted
[Bounds-Safety] Add sized_by, counted_by_or_null & sized_by_or_null (llvm#93231)
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 2afbb98 commit 634eafb

34 files changed

+2628
-51
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,15 @@ Attribute Changes in Clang
554554
size_t count;
555555
};
556556
557+
- The attributes ``sized_by``, ``counted_by_or_null`` and ``sized_by_or_null```
558+
have been added as variants on ``counted_by``, each with slightly different semantics.
559+
``sized_by`` takes a byte size parameter instead of an element count, allowing pointees
560+
with unknown size. The ``counted_by_or_null`` and ``sized_by_or_null`` variants are equivalent
561+
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. ``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.
565+
557566
- The ``guarded_by``, ``pt_guarded_by``, ``acquired_after``, ``acquired_before``
558567
attributes now support referencing struct members in C. The arguments are also
559568
now late parsed when ``-fexperimental-late-parse-attributes`` is passed like

clang/include/clang/Basic/Attr.td

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2292,6 +2292,36 @@ def CountedBy : DeclOrTypeAttr {
22922292
let LangOpts = [COnly];
22932293
}
22942294

2295+
def CountedByOrNull : DeclOrTypeAttr {
2296+
let Spellings = [Clang<"counted_by_or_null">];
2297+
let Subjects = SubjectList<[Field], ErrorDiag>;
2298+
let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>];
2299+
let LateParsed = LateAttrParseExperimentalExt;
2300+
let ParseArgumentsAsUnevaluated = 1;
2301+
let Documentation = [CountedByDocs];
2302+
let LangOpts = [COnly];
2303+
}
2304+
2305+
def SizedBy : DeclOrTypeAttr {
2306+
let Spellings = [Clang<"sized_by">];
2307+
let Subjects = SubjectList<[Field], ErrorDiag>;
2308+
let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>];
2309+
let LateParsed = LateAttrParseExperimentalExt;
2310+
let ParseArgumentsAsUnevaluated = 1;
2311+
let Documentation = [CountedByDocs];
2312+
let LangOpts = [COnly];
2313+
}
2314+
2315+
def SizedByOrNull : DeclOrTypeAttr {
2316+
let Spellings = [Clang<"sized_by_or_null">];
2317+
let Subjects = SubjectList<[Field], ErrorDiag>;
2318+
let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>];
2319+
let LateParsed = LateAttrParseExperimentalExt;
2320+
let ParseArgumentsAsUnevaluated = 1;
2321+
let Documentation = [CountedByDocs];
2322+
let LangOpts = [COnly];
2323+
}
2324+
22952325
// This is a marker used to indicate that an __unsafe_unretained qualifier was
22962326
// ignored because ARC is not enabled. The usual representation for this
22972327
// qualifier is as an ObjCOwnership attribute with Kind == "none".

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6567,28 +6567,28 @@ 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-
"'counted_by' 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-
"'counted_by' only applies to pointers or C99 flexible array members">;
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<
6579-
"field %0 in 'counted_by' not inside structure">;
6580-
def err_counted_by_attr_argument_not_integer : Error<
6581-
"'counted_by' requires a non-boolean integer type argument">;
6582-
def err_counted_by_attr_only_support_simple_decl_reference : Error<
6583-
"'counted_by' argument must be a simple declaration reference">;
6584-
def err_counted_by_attr_in_union : Error<
6585-
"'counted_by' cannot be applied to a union member">;
6586-
def err_counted_by_attr_refer_to_union : Error<
6587-
"'counted_by' argument cannot refer to a union member">;
6578+
def err_count_attr_must_be_in_structure : Error<
6579+
"field %0 in '%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' not inside structure">;
6580+
def err_count_attr_argument_not_integer : Error<
6581+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' requires a non-boolean integer type argument">;
6582+
def err_count_attr_only_support_simple_decl_reference : Error<
6583+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' argument must be a simple declaration reference">;
6584+
def err_count_attr_in_union : Error<
6585+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' cannot be applied to a union member">;
6586+
def err_count_attr_refer_to_union : Error<
6587+
"'%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">;
65906590
def err_counted_by_attr_pointee_unknown_size : Error<
6591-
"'counted_by' %select{cannot|should not}3 be applied to %select{"
6591+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}4' %select{cannot|should not}3 be applied to %select{"
65926592
"a pointer with pointee|" // pointer
65936593
"an array with element}0" // array
65946594
" 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
@@ -14603,7 +14603,9 @@ class Sema final : public SemaBase {
1460314603
SourceLocation AttrLoc);
1460414604

1460514605
QualType BuildCountAttributedArrayOrPointerType(QualType WrappedTy,
14606-
Expr *CountExpr);
14606+
Expr *CountExpr,
14607+
bool CountInBytes,
14608+
bool OrNull);
1460714609

1460814610
/// BuildAddressSpaceAttr - Builds a DependentAddressSpaceType if an
1460914611
/// expression is uninstantiated. If instantiated it will apply the

clang/lib/AST/TypePrinter.cpp

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

19361936
case attr::CountedBy:
1937+
case attr::CountedByOrNull:
1938+
case attr::SizedBy:
1939+
case attr::SizedByOrNull:
19371940
case attr::LifetimeBound:
19381941
case attr::TypeNonNull:
19391942
case attr::TypeNullable:

clang/lib/Parse/ParseDecl.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,10 @@ void Parser::ParseGNUAttributeArgs(
700700
ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, ScopeName,
701701
ScopeLoc, Form);
702702
return;
703-
} else if (AttrKind == ParsedAttr::AT_CountedBy) {
703+
} else if (AttrKind == ParsedAttr::AT_CountedBy ||
704+
AttrKind == ParsedAttr::AT_CountedByOrNull ||
705+
AttrKind == ParsedAttr::AT_SizedBy ||
706+
AttrKind == ParsedAttr::AT_SizedByOrNull) {
704707
ParseBoundsAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc,
705708
Form);
706709
return;
@@ -4866,9 +4869,8 @@ static void DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS,
48664869

48674870
for (const auto &DD : CAT->dependent_decls()) {
48684871
if (!RD->containsDecl(DD.getDecl())) {
4869-
P.Diag(VD->getBeginLoc(),
4870-
diag::err_flexible_array_count_not_in_same_struct)
4871-
<< DD.getDecl();
4872+
P.Diag(VD->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
4873+
<< DD.getDecl() << CAT->getKind() << CAT->isArrayType();
48724874
P.Diag(DD.getDecl()->getBeginLoc(),
48734875
diag::note_flexible_array_counted_by_attr_field)
48744876
<< DD.getDecl();

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 67 additions & 24 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,
@@ -5876,22 +5885,31 @@ enum class CountedByInvalidPointeeTypeKind {
58765885
VALID,
58775886
};
58785887

5879-
static bool CheckCountedByAttrOnField(
5880-
Sema &S, FieldDecl *FD, Expr *E,
5881-
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls) {
5888+
static bool
5889+
CheckCountedByAttrOnField(Sema &S, FieldDecl *FD, Expr *E,
5890+
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls,
5891+
bool CountInBytes, bool OrNull) {
58825892
// Check the context the attribute is used in
58835893

5894+
unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
5895+
58845896
if (FD->getParent()->isUnion()) {
5885-
S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_in_union)
5886-
<< FD->getSourceRange();
5897+
S.Diag(FD->getBeginLoc(), diag::err_count_attr_in_union)
5898+
<< Kind << FD->getSourceRange();
58875899
return true;
58885900
}
58895901

58905902
const auto FieldTy = FD->getType();
5903+
if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
5904+
S.Diag(FD->getBeginLoc(),
5905+
diag::err_count_attr_not_on_ptr_or_flexible_array_member)
5906+
<< Kind << FD->getLocation() << /* suggest counted_by */ 1;
5907+
return true;
5908+
}
58915909
if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {
58925910
S.Diag(FD->getBeginLoc(),
5893-
diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member)
5894-
<< FD->getLocation();
5911+
diag::err_count_attr_not_on_ptr_or_flexible_array_member)
5912+
<< Kind << FD->getLocation() << /* do not suggest counted_by */ 0;
58955913
return true;
58965914
}
58975915

@@ -5902,7 +5920,7 @@ static bool CheckCountedByAttrOnField(
59025920
StrictFlexArraysLevel, true)) {
59035921
S.Diag(FD->getBeginLoc(),
59045922
diag::err_counted_by_attr_on_array_not_flexible_array_member)
5905-
<< FD->getLocation();
5923+
<< Kind << FD->getLocation();
59065924
return true;
59075925
}
59085926

@@ -5923,7 +5941,7 @@ static bool CheckCountedByAttrOnField(
59235941
// only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable
59245942
// when `FieldTy->isArrayType()`.
59255943
bool ShouldWarn = false;
5926-
if (PointeeTy->isIncompleteType()) {
5944+
if (PointeeTy->isIncompleteType() && !CountInBytes) {
59275945
InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
59285946
} else if (PointeeTy->isSizelessType()) {
59295947
InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
@@ -5948,23 +5966,23 @@ static bool CheckCountedByAttrOnField(
59485966
: diag::err_counted_by_attr_pointee_unknown_size;
59495967
S.Diag(FD->getBeginLoc(), DiagID)
59505968
<< SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
5951-
<< (ShouldWarn ? 1 : 0) << FD->getSourceRange();
5969+
<< (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange();
59525970
return true;
59535971
}
59545972

59555973
// Check the expression
59565974

59575975
if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
5958-
S.Diag(E->getBeginLoc(), diag::err_counted_by_attr_argument_not_integer)
5959-
<< E->getSourceRange();
5976+
S.Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)
5977+
<< Kind << E->getSourceRange();
59605978
return true;
59615979
}
59625980

59635981
auto *DRE = dyn_cast<DeclRefExpr>(E);
59645982
if (!DRE) {
59655983
S.Diag(E->getBeginLoc(),
5966-
diag::err_counted_by_attr_only_support_simple_decl_reference)
5967-
<< E->getSourceRange();
5984+
diag::err_count_attr_only_support_simple_decl_reference)
5985+
<< Kind << E->getSourceRange();
59685986
return true;
59695987
}
59705988

@@ -5974,8 +5992,8 @@ static bool CheckCountedByAttrOnField(
59745992
CountFD = IFD->getAnonField();
59755993
}
59765994
if (!CountFD) {
5977-
S.Diag(E->getBeginLoc(), diag::err_counted_by_must_be_in_structure)
5978-
<< CountDecl << E->getSourceRange();
5995+
S.Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)
5996+
<< CountDecl << Kind << E->getSourceRange();
59795997

59805998
S.Diag(CountDecl->getBeginLoc(),
59815999
diag::note_flexible_array_counted_by_attr_field)
@@ -5985,8 +6003,8 @@ static bool CheckCountedByAttrOnField(
59856003

59866004
if (FD->getParent() != CountFD->getParent()) {
59876005
if (CountFD->getParent()->isUnion()) {
5988-
S.Diag(CountFD->getBeginLoc(), diag::err_counted_by_attr_refer_to_union)
5989-
<< CountFD->getSourceRange();
6006+
S.Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union)
6007+
<< Kind << CountFD->getSourceRange();
59906008
return true;
59916009
}
59926010
// Whether CountRD is an anonymous struct is not determined at this
@@ -5996,9 +6014,8 @@ static bool CheckCountedByAttrOnField(
59966014
auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);
59976015

59986016
if (RD != CountRD) {
5999-
S.Diag(E->getBeginLoc(),
6000-
diag::err_flexible_array_count_not_in_same_struct)
6001-
<< CountFD << E->getSourceRange();
6017+
S.Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
6018+
<< CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();
60026019
S.Diag(CountFD->getBeginLoc(),
60036020
diag::note_flexible_array_counted_by_attr_field)
60046021
<< CountFD << CountFD->getSourceRange();
@@ -6018,12 +6035,35 @@ static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) {
60186035
if (!CountExpr)
60196036
return;
60206037

6038+
bool CountInBytes;
6039+
bool OrNull;
6040+
switch (AL.getKind()) {
6041+
case ParsedAttr::AT_CountedBy:
6042+
CountInBytes = false;
6043+
OrNull = false;
6044+
break;
6045+
case ParsedAttr::AT_CountedByOrNull:
6046+
CountInBytes = false;
6047+
OrNull = true;
6048+
break;
6049+
case ParsedAttr::AT_SizedBy:
6050+
CountInBytes = true;
6051+
OrNull = false;
6052+
break;
6053+
case ParsedAttr::AT_SizedByOrNull:
6054+
CountInBytes = true;
6055+
OrNull = true;
6056+
break;
6057+
default:
6058+
llvm_unreachable("unexpected counted_by family attribute");
6059+
}
6060+
60216061
llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
6022-
if (CheckCountedByAttrOnField(S, FD, CountExpr, Decls))
6062+
if (CheckCountedByAttrOnField(S, FD, CountExpr, Decls, CountInBytes, OrNull))
60236063
return;
60246064

6025-
QualType CAT =
6026-
S.BuildCountAttributedArrayOrPointerType(FD->getType(), CountExpr);
6065+
QualType CAT = S.BuildCountAttributedArrayOrPointerType(
6066+
FD->getType(), CountExpr, CountInBytes, OrNull);
60276067
FD->setType(CAT);
60286068
}
60296069

@@ -6971,6 +7011,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
69717011
break;
69727012

69737013
case ParsedAttr::AT_CountedBy:
7014+
case ParsedAttr::AT_CountedByOrNull:
7015+
case ParsedAttr::AT_SizedBy:
7016+
case ParsedAttr::AT_SizedByOrNull:
69747017
handleCountedByAttrField(S, D, AL);
69757018
break;
69767019

clang/lib/Sema/SemaType.cpp

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

93269326
QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy,
9327-
Expr *CountExpr) {
9327+
Expr *CountExpr,
9328+
bool CountInBytes,
9329+
bool OrNull) {
93289330
assert(WrappedTy->isIncompleteArrayType() || WrappedTy->isPointerType());
93299331

93309332
llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
93319333
BuildTypeCoupledDecls(CountExpr, Decls);
93329334
/// When the resulting expression is invalid, we still create the AST using
93339335
/// the original count expression for the sake of AST dump.
9334-
return Context.getCountAttributedType(
9335-
WrappedTy, CountExpr, /*CountInBytes*/ false, /*OrNull*/ false, Decls);
9336+
return Context.getCountAttributedType(WrappedTy, CountExpr, CountInBytes,
9337+
OrNull, Decls);
93369338
}
93379339

93389340
/// 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
@@ -7397,7 +7397,8 @@ QualType TreeTransform<Derived>::TransformCountAttributedType(
73977397
if (getDerived().AlwaysRebuild() || InnerTy != OldTy->desugar() ||
73987398
OldCount != NewCount) {
73997399
// Currently, CountAttributedType can only wrap incomplete array types.
7400-
Result = SemaRef.BuildCountAttributedArrayOrPointerType(InnerTy, NewCount);
7400+
Result = SemaRef.BuildCountAttributedArrayOrPointerType(
7401+
InnerTy, NewCount, OldTy->isCountInBytes(), OldTy->isOrNull());
74017402
}
74027403

74037404
TLB.push<CountAttributedTypeLoc>(Result);

0 commit comments

Comments
 (0)