Skip to content

Commit da945ea

Browse files
authored
Merge pull request #9822 from hnrklssn/bounds-safety-apinotes
This adds support for annotating function parameters with __counted_by, __sized_by, __counted_by_or_null, __sized_by_or_null, and __ended_by, using API notes. The main content of handlePtrCountedByEndedByAttr is extracted to applyPtrCountedByEndedByAttr and decoupled from ParsedAttr. The helper function ParseBoundsAttributeArgFromString is added to make it possible to parse count expressions from SemaAPINotes. The current implementation of __terminated_by/__null_terminated makes it harder to extract from the iterative type processing, but since it doesn't require any extra context to parse the attribute, it can be applied using the normal Type override instead. rdar://139830881
2 parents 3491751 + 32c4fa5 commit da945ea

20 files changed

+1081
-102
lines changed

clang/include/clang/APINotes/Types.h

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,72 @@ inline bool operator!=(const ContextInfo &LHS, const ContextInfo &RHS) {
300300
return !(LHS == RHS);
301301
}
302302

303+
/* TO_UPSTREAM(BoundsSafety) ON */
304+
class BoundsSafetyInfo {
305+
public:
306+
enum class BoundsSafetyKind {
307+
CountedBy = 0,
308+
CountedByOrNull,
309+
SizedBy,
310+
SizedByOrNull,
311+
EndedBy,
312+
};
313+
314+
private:
315+
/// Whether this property has been audited for nullability.
316+
LLVM_PREFERRED_TYPE(bool)
317+
unsigned KindAudited : 1;
318+
319+
/// The kind of nullability for this property. Only valid if the nullability
320+
/// has been audited.
321+
LLVM_PREFERRED_TYPE(BoundsSafetyKind)
322+
unsigned Kind : 3;
323+
324+
LLVM_PREFERRED_TYPE(bool)
325+
unsigned LevelAudited : 1;
326+
327+
unsigned Level : 3;
328+
329+
public:
330+
std::string ExternalBounds;
331+
332+
BoundsSafetyInfo()
333+
: KindAudited(false), Kind(0), LevelAudited(false), Level(0),
334+
ExternalBounds("") {}
335+
336+
std::optional<BoundsSafetyKind> getKind() const {
337+
return KindAudited ? std::optional<BoundsSafetyKind>(
338+
static_cast<BoundsSafetyKind>(Kind))
339+
: std::nullopt;
340+
}
341+
342+
void setKindAudited(BoundsSafetyKind kind) {
343+
KindAudited = true;
344+
Kind = static_cast<unsigned>(kind);
345+
}
346+
347+
std::optional<unsigned> getLevel() const {
348+
return LevelAudited ? std::optional<unsigned>(Level) : std::nullopt;
349+
}
350+
351+
void setLevelAudited(unsigned level) {
352+
LevelAudited = true;
353+
Level = level;
354+
}
355+
356+
friend bool operator==(const BoundsSafetyInfo &, const BoundsSafetyInfo &);
357+
358+
LLVM_DUMP_METHOD void dump(llvm::raw_ostream &OS) const;
359+
};
360+
361+
inline bool operator==(const BoundsSafetyInfo &LHS,
362+
const BoundsSafetyInfo &RHS) {
363+
return LHS.KindAudited == RHS.KindAudited && LHS.Kind == RHS.Kind &&
364+
LHS.LevelAudited == RHS.LevelAudited && LHS.Level == RHS.Level &&
365+
LHS.ExternalBounds == RHS.ExternalBounds;
366+
}
367+
/* TO_UPSTREAM(BoundsSafety) OFF */
368+
303369
/// API notes for a variable/property.
304370
class VariableInfo : public CommonEntityInfo {
305371
/// Whether this property has been audited for nullability.
@@ -439,10 +505,14 @@ class ParamInfo : public VariableInfo {
439505
unsigned RawRetainCountConvention : 3;
440506

441507
public:
508+
/* TO_UPSTREAM(BoundsSafety) ON */
509+
std::optional<BoundsSafetyInfo> BoundsSafety;
510+
/* TO_UPSTREAM(BoundsSafety) OFF */
511+
442512
ParamInfo()
443513
: NoEscapeSpecified(false), NoEscape(false),
444514
LifetimeboundSpecified(false), Lifetimebound(false),
445-
RawRetainCountConvention() {}
515+
RawRetainCountConvention(), BoundsSafety(std::nullopt) {}
446516

447517
std::optional<bool> isNoEscape() const {
448518
return NoEscapeSpecified ? std::optional<bool>(NoEscape) : std::nullopt;
@@ -488,6 +558,11 @@ class ParamInfo : public VariableInfo {
488558
if (!RawRetainCountConvention)
489559
RawRetainCountConvention = RHS.RawRetainCountConvention;
490560

561+
/* TO_UPSTREAM(BoundsSafety) ON */
562+
if (!BoundsSafety)
563+
BoundsSafety = RHS.BoundsSafety;
564+
/* TO_UPSTREAM(BoundsSafety) OFF */
565+
491566
return *this;
492567
}
493568

@@ -502,7 +577,10 @@ inline bool operator==(const ParamInfo &LHS, const ParamInfo &RHS) {
502577
LHS.NoEscape == RHS.NoEscape &&
503578
LHS.LifetimeboundSpecified == RHS.LifetimeboundSpecified &&
504579
LHS.Lifetimebound == RHS.Lifetimebound &&
505-
LHS.RawRetainCountConvention == RHS.RawRetainCountConvention;
580+
LHS.RawRetainCountConvention == RHS.RawRetainCountConvention &&
581+
/* TO_UPSTREAM(BoundsSafety) ON */
582+
LHS.BoundsSafety == RHS.BoundsSafety;
583+
/* TO_UPSTREAM(BoundsSafety) OFF */
506584
}
507585

508586
inline bool operator!=(const ParamInfo &LHS, const ParamInfo &RHS) {

clang/include/clang/Parse/Parser.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2021,7 +2021,7 @@ class Parser : public CodeCompletionHandler {
20212021
/// Parse a __builtin_bit_cast(T, E), used to implement C++2a std::bit_cast.
20222022
ExprResult ParseBuiltinBitCast();
20232023

2024-
/* TO_UPSTREAM(BoundsSafety) ON*/
2024+
/* TO_UPSTREAM(BoundsSafety) ON */
20252025
//===--------------------------------------------------------------------===//
20262026
/// BoundsSafety: __builtin_unsafe_forge_bidi_indexable(expr, size)
20272027
ExprResult ParseUnsafeForgeBidiIndexable();
@@ -3924,6 +3924,25 @@ class Parser : public CodeCompletionHandler {
39243924
/// \param IncludeLoc The location at which this parse was triggered.
39253925
TypeResult ParseTypeFromString(StringRef TypeStr, StringRef Context,
39263926
SourceLocation IncludeLoc);
3927+
/* TO_UPSTREAM(BoundsSafety) ON */
3928+
/// Parse the given string as an expression in the argument position for a
3929+
/// bounds safety attribute.
3930+
///
3931+
/// This is a dangerous utility function currently employed only by API notes.
3932+
/// It is not a general entry-point for safely parsing expressions from
3933+
/// strings.
3934+
///
3935+
/// \param ExprStr The string to be parsed as an expression.
3936+
/// \param Context The name of the context in which this string is being
3937+
/// parsed, which will be used in diagnostics.
3938+
/// \param ParentDecl If a function or method is provided, the parameters are
3939+
/// added to the current parsing context.
3940+
/// \param IncludeLoc The location at which this parse was triggered.
3941+
ExprResult ParseBoundsAttributeArgFromString(StringRef ExprStr,
3942+
StringRef Context,
3943+
Decl *ParentDecl,
3944+
SourceLocation IncludeLoc);
3945+
/* TO_UPSTREAM(BoundsSafety) OFF */
39273946

39283947
//===--------------------------------------------------------------------===//
39293948
// Modules

clang/include/clang/Sema/Sema.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,12 @@ class Sema final : public SemaBase {
11311131
std::function<TypeResult(StringRef, StringRef, SourceLocation)>
11321132
ParseTypeFromStringCallback;
11331133

1134+
/* TO_UPSTREAM(BoundsSafety) ON */
1135+
/// Callback to the parser to parse a type expressed as a string.
1136+
std::function<ExprResult(StringRef, StringRef, Decl *, SourceLocation)>
1137+
ParseBoundsAttributeArgFromStringCallback;
1138+
/* TO_UPSTREAM(BoundsSafety) OFF */
1139+
11341140
/// VAListTagName - The declaration name corresponding to __va_list_tag.
11351141
/// This is used as part of a hack to omit that class from ADL results.
11361142
DeclarationName VAListTagName;
@@ -15498,7 +15504,13 @@ class Sema final : public SemaBase {
1549815504
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls, bool CountInBytes,
1549915505
bool OrNull);
1550015506

15501-
/* TO_UPSTREAM(BoundsSafety) ON*/
15507+
/* TO_UPSTREAM(BoundsSafety) ON */
15508+
void applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
15509+
AttributeCommonInfo::Kind Kind,
15510+
Expr *AttrArg, SourceLocation Loc,
15511+
SourceRange Range, StringRef DiagName,
15512+
bool OriginatesInAPINotes = false);
15513+
1550215514
/// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or
1550315515
/// `__counted_by_or_null` pointer type \param LHSTy.
1550415516
///

clang/lib/APINotes/APINotesFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0;
2424
/// API notes file minor version number.
2525
///
2626
/// When the format changes IN ANY WAY, this number should be incremented.
27-
const uint16_t VERSION_MINOR = 34; // SwiftReturnOwnership
27+
const uint16_t VERSION_MINOR = 35; // BoundsSafety
2828

2929
const uint8_t kSwiftConforms = 1;
3030
const uint8_t kSwiftDoesNotConform = 2;

clang/lib/APINotes/APINotesReader.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,31 @@ class FieldTableInfo
322322
}
323323
};
324324

325+
/* TO_UPSTREAM(BoundsSafety) ON */
326+
/// Read serialized BoundsSafetyInfo.
327+
void ReadBoundsSafetyInfo(const uint8_t *&Data, BoundsSafetyInfo &Info) {
328+
uint8_t Payload = endian::readNext<uint8_t, llvm::endianness::little>(Data);
329+
330+
if (Payload & 0x01) {
331+
uint8_t Level = (Payload >> 1) & 0x7;
332+
Info.setLevelAudited(Level);
333+
}
334+
Payload >>= 4;
335+
336+
if (Payload & 0x01) {
337+
uint8_t Kind = (Payload >> 1) & 0x7;
338+
assert(Kind >= (uint8_t)BoundsSafetyInfo::BoundsSafetyKind::CountedBy);
339+
assert(Kind <= (uint8_t)BoundsSafetyInfo::BoundsSafetyKind::EndedBy);
340+
Info.setKindAudited((BoundsSafetyInfo::BoundsSafetyKind)Kind);
341+
}
342+
343+
uint16_t ExternalBoundsLen =
344+
endian::readNext<uint16_t, llvm::endianness::little>(Data);
345+
Info.ExternalBounds = std::string(Data, Data + ExternalBoundsLen);
346+
Data += ExternalBoundsLen;
347+
}
348+
/* TO_UPSTREAM(BoundsSafety) OFF */
349+
325350
/// Read serialized ParamInfo.
326351
void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
327352
ReadVariableInfo(Data, Info);
@@ -338,7 +363,13 @@ void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
338363
if (Payload & 0x01)
339364
Info.setNoEscape(Payload & 0x02);
340365
Payload >>= 2;
341-
assert(Payload == 0 && "Bad API notes");
366+
/* TO_UPSTREAM(BoundsSafety) ON */
367+
if (Payload & 0x01) {
368+
BoundsSafetyInfo BSI;
369+
ReadBoundsSafetyInfo(Data, BSI);
370+
Info.BoundsSafety = BSI;
371+
}
372+
/* TO_UPSTREAM(BoundsSafety) OFF */
342373
}
343374

344375
/// Read serialized FunctionInfo.

clang/lib/APINotes/APINotesTypes.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,34 @@ LLVM_DUMP_METHOD void ObjCPropertyInfo::dump(llvm::raw_ostream &OS) const {
6161
OS << '\n';
6262
}
6363

64+
LLVM_DUMP_METHOD void BoundsSafetyInfo::dump(llvm::raw_ostream &OS) const {
65+
if (KindAudited) {
66+
assert((BoundsSafetyKind)Kind >= BoundsSafetyKind::CountedBy);
67+
assert((BoundsSafetyKind)Kind <= BoundsSafetyKind::EndedBy);
68+
switch ((BoundsSafetyKind)Kind) {
69+
case BoundsSafetyKind::CountedBy:
70+
OS << "[counted_by] ";
71+
break;
72+
case BoundsSafetyKind::CountedByOrNull:
73+
OS << "[counted_by_or_null] ";
74+
break;
75+
case BoundsSafetyKind::SizedBy:
76+
OS << "[sized_by] ";
77+
break;
78+
case BoundsSafetyKind::SizedByOrNull:
79+
OS << "[sized_by_or_null] ";
80+
break;
81+
case BoundsSafetyKind::EndedBy:
82+
OS << "[ended_by] ";
83+
break;
84+
}
85+
}
86+
if (LevelAudited)
87+
OS << "Level: " << Level << " ";
88+
OS << "ExternalBounds: "
89+
<< (ExternalBounds.empty() ? "<missing>" : ExternalBounds) << '\n';
90+
}
91+
6492
LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const {
6593
static_cast<const VariableInfo &>(*this).dump(OS);
6694
if (NoEscapeSpecified)
@@ -69,6 +97,8 @@ LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const {
6997
OS << (Lifetimebound ? "[Lifetimebound] " : "");
7098
OS << "RawRetainCountConvention: " << RawRetainCountConvention << ' ';
7199
OS << '\n';
100+
if (BoundsSafety)
101+
BoundsSafety->dump(OS);
72102
}
73103

74104
LLVM_DUMP_METHOD void FunctionInfo::dump(llvm::raw_ostream &OS) const {

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1074,15 +1074,48 @@ void APINotesWriter::Implementation::writeGlobalVariableBlock(
10741074
}
10751075
}
10761076

1077+
/* TO_UPSTREAM(BoundsSafety) ON */
10771078
namespace {
1079+
void emitBoundsSafetyInfo(raw_ostream &OS, const BoundsSafetyInfo &BSI) {
1080+
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
1081+
uint8_t flags = 0;
1082+
if (auto kind = BSI.getKind()) {
1083+
flags |= 0x01; // 1 bit
1084+
flags |= (uint8_t)*kind << 1; // 3 bits
1085+
}
1086+
flags <<= 4;
1087+
if (auto level = BSI.getLevel()) {
1088+
flags |= 0x01; // 1 bit
1089+
flags |= *level << 1; // 3 bits
1090+
}
1091+
1092+
writer.write<uint8_t>(flags);
1093+
writer.write<uint16_t>(BSI.ExternalBounds.size());
1094+
writer.write(
1095+
ArrayRef<char>{BSI.ExternalBounds.data(), BSI.ExternalBounds.size()});
1096+
}
1097+
1098+
unsigned getBoundsSafetyInfoSize(const BoundsSafetyInfo &BSI) {
1099+
return 1 + sizeof(uint16_t) + BSI.ExternalBounds.size();
1100+
}
1101+
/* TO_UPSTREAM(BoundsSafety) OFF */
1102+
10781103
unsigned getParamInfoSize(const ParamInfo &PI) {
1079-
return getVariableInfoSize(PI) + 1;
1104+
unsigned BSISize = 0;
1105+
/* TO_UPSTREAM(BoundsSafety) ON */
1106+
if (auto BSI = PI.BoundsSafety)
1107+
BSISize = getBoundsSafetyInfoSize(*BSI);
1108+
/* TO_UPSTREAM(BoundsSafety) OFF */
1109+
return getVariableInfoSize(PI) + 1 + BSISize;
10801110
}
10811111

10821112
void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
10831113
emitVariableInfo(OS, PI);
10841114

10851115
uint8_t flags = 0;
1116+
if (PI.BoundsSafety)
1117+
flags |= 0x01;
1118+
flags <<= 2;
10861119
if (auto noescape = PI.isNoEscape()) {
10871120
flags |= 0x01;
10881121
if (*noescape)
@@ -1100,6 +1133,10 @@ void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
11001133

11011134
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
11021135
writer.write<uint8_t>(flags);
1136+
/* TO_UPSTREAM(BoundsSafety) ON */
1137+
if (auto BSI = PI.BoundsSafety)
1138+
emitBoundsSafetyInfo(OS, *PI.BoundsSafety);
1139+
/* TO_UPSTREAM(BoundsSafety) OFF */
11031140
}
11041141

11051142
/// Retrieve the serialized size of the given FunctionInfo, for use in on-disk

0 commit comments

Comments
 (0)