Skip to content

Commit 5b75087

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 9e1f1cf commit 5b75087

30 files changed

+2151
-31
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,13 @@ 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.
563+
557564
- The ``guarded_by``, ``pt_guarded_by``, ``acquired_after``, ``acquired_before``
558565
attributes now support referencing struct members in C. The arguments are also
559566
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: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6568,27 +6568,27 @@ def warn_superclass_variable_sized_type_not_at_end : Warning<
65686568
" in superclass %3">, InGroup<ObjCFlexibleArray>;
65696569

65706570
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">;
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">;
65726572
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">;
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">;
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">;
65786578
def err_counted_by_must_be_in_structure : Error<
6579-
"field %0 in 'counted_by' not inside structure">;
6579+
"field %0 in '%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}1' not inside structure">;
65806580
def err_counted_by_attr_argument_not_integer : Error<
6581-
"'counted_by' requires a non-boolean integer type argument">;
6581+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' requires a non-boolean integer type argument">;
65826582
def err_counted_by_attr_only_support_simple_decl_reference : Error<
6583-
"'counted_by' argument must be a simple declaration reference">;
6583+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' argument must be a simple declaration reference">;
65846584
def err_counted_by_attr_in_union : Error<
6585-
"'counted_by' cannot be applied to a union member">;
6585+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' cannot be applied to a union member">;
65866586
def err_counted_by_attr_refer_to_union : Error<
6587-
"'counted_by' argument cannot refer to a union member">;
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|counted_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: 5 additions & 2 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;
@@ -4868,7 +4871,7 @@ static void DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS,
48684871
if (!RD->containsDecl(DD.getDecl())) {
48694872
P.Diag(VD->getBeginLoc(),
48704873
diag::err_flexible_array_count_not_in_same_struct)
4871-
<< DD.getDecl();
4874+
<< DD.getDecl() << CAT->getKind();
48724875
P.Diag(DD.getDecl()->getBeginLoc(),
48734876
diag::note_flexible_array_counted_by_attr_field)
48744877
<< DD.getDecl();

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5876,22 +5876,33 @@ enum class CountedByInvalidPointeeTypeKind {
58765876
VALID,
58775877
};
58785878

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

5885+
unsigned Kind = CountInBytes;
5886+
if (OrNull)
5887+
Kind += 2;
5888+
58845889
if (FD->getParent()->isUnion()) {
58855890
S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_in_union)
5886-
<< FD->getSourceRange();
5891+
<< Kind << FD->getSourceRange();
58875892
return true;
58885893
}
58895894

58905895
const auto FieldTy = FD->getType();
5896+
if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
5897+
S.Diag(FD->getBeginLoc(),
5898+
diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member)
5899+
<< Kind << FD->getLocation();
5900+
return true;
5901+
}
58915902
if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {
58925903
S.Diag(FD->getBeginLoc(),
58935904
diag::err_counted_by_attr_not_on_ptr_or_flexible_array_member)
5894-
<< FD->getLocation();
5905+
<< Kind << FD->getLocation();
58955906
return true;
58965907
}
58975908

@@ -5902,7 +5913,7 @@ static bool CheckCountedByAttrOnField(
59025913
StrictFlexArraysLevel, true)) {
59035914
S.Diag(FD->getBeginLoc(),
59045915
diag::err_counted_by_attr_on_array_not_flexible_array_member)
5905-
<< FD->getLocation();
5916+
<< Kind << FD->getLocation();
59065917
return true;
59075918
}
59085919

@@ -5942,29 +5953,30 @@ static bool CheckCountedByAttrOnField(
59425953
InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;
59435954
}
59445955

5945-
if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
5956+
if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID &&
5957+
!CountInBytes) {
59465958
unsigned DiagID = ShouldWarn
59475959
? diag::warn_counted_by_attr_elt_type_unknown_size
59485960
: diag::err_counted_by_attr_pointee_unknown_size;
59495961
S.Diag(FD->getBeginLoc(), DiagID)
59505962
<< SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
5951-
<< (ShouldWarn ? 1 : 0) << FD->getSourceRange();
5963+
<< (ShouldWarn ? 1 : 0) << OrNull << FD->getSourceRange();
59525964
return true;
59535965
}
59545966

59555967
// Check the expression
59565968

59575969
if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
59585970
S.Diag(E->getBeginLoc(), diag::err_counted_by_attr_argument_not_integer)
5959-
<< E->getSourceRange();
5971+
<< Kind << E->getSourceRange();
59605972
return true;
59615973
}
59625974

59635975
auto *DRE = dyn_cast<DeclRefExpr>(E);
59645976
if (!DRE) {
59655977
S.Diag(E->getBeginLoc(),
59665978
diag::err_counted_by_attr_only_support_simple_decl_reference)
5967-
<< E->getSourceRange();
5979+
<< Kind << E->getSourceRange();
59685980
return true;
59695981
}
59705982

@@ -5975,7 +5987,7 @@ static bool CheckCountedByAttrOnField(
59755987
}
59765988
if (!CountFD) {
59775989
S.Diag(E->getBeginLoc(), diag::err_counted_by_must_be_in_structure)
5978-
<< CountDecl << E->getSourceRange();
5990+
<< CountDecl << Kind << E->getSourceRange();
59795991

59805992
S.Diag(CountDecl->getBeginLoc(),
59815993
diag::note_flexible_array_counted_by_attr_field)
@@ -5986,7 +5998,7 @@ static bool CheckCountedByAttrOnField(
59865998
if (FD->getParent() != CountFD->getParent()) {
59875999
if (CountFD->getParent()->isUnion()) {
59886000
S.Diag(CountFD->getBeginLoc(), diag::err_counted_by_attr_refer_to_union)
5989-
<< CountFD->getSourceRange();
6001+
<< Kind << CountFD->getSourceRange();
59906002
return true;
59916003
}
59926004
// Whether CountRD is an anonymous struct is not determined at this
@@ -5998,7 +6010,7 @@ static bool CheckCountedByAttrOnField(
59986010
if (RD != CountRD) {
59996011
S.Diag(E->getBeginLoc(),
60006012
diag::err_flexible_array_count_not_in_same_struct)
6001-
<< CountFD << E->getSourceRange();
6013+
<< CountFD << Kind << E->getSourceRange();
60026014
S.Diag(CountFD->getBeginLoc(),
60036015
diag::note_flexible_array_counted_by_attr_field)
60046016
<< CountFD << CountFD->getSourceRange();
@@ -6018,12 +6030,35 @@ static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) {
60186030
if (!CountExpr)
60196031
return;
60206032

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

6025-
QualType CAT =
6026-
S.BuildCountAttributedArrayOrPointerType(FD->getType(), CountExpr);
6060+
QualType CAT = S.BuildCountAttributedArrayOrPointerType(
6061+
FD->getType(), CountExpr, CountInBytes, OrNull);
60276062
FD->setType(CAT);
60286063
}
60296064

@@ -6971,6 +7006,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
69717006
break;
69727007

69737008
case ParsedAttr::AT_CountedBy:
7009+
case ParsedAttr::AT_CountedByOrNull:
7010+
case ParsedAttr::AT_SizedBy:
7011+
case ParsedAttr::AT_SizedByOrNull:
69747012
handleCountedByAttrField(S, D, AL);
69757013
break;
69767014

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);
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)