[APINotes][unsafe-buffer-usage] Add [[clang::unsafe_buffer_usage]] support in APINotes (#189775)
Support the ``[[clang::unsafe_buffer_usage]]`` attribute in APINotes,
e.g.,
```
Functions:
- Name: myUnsafeFunction
UnsafeBufferUsage: true
```
rdar://171859135
This commit is contained in:
@@ -296,6 +296,15 @@ Attribute Changes in Clang
|
||||
sound because any writer must hold all capabilities, so holding any one
|
||||
prevents concurrent writes.
|
||||
|
||||
- The ``[[clang::unsafe_buffer_usage]]`` attribute is now supported in API
|
||||
notes. For example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Functions:
|
||||
- Name: myUnsafeFunction
|
||||
UnsafeBufferUsage: true
|
||||
|
||||
- Added support for ``[[msvc::forceinline]]`` for functions and
|
||||
``[[msvc::forceinline_calls]]`` for statements. Both are aliases to
|
||||
``[[clang::always_inline]]`` with additional checks to ensure that they
|
||||
|
||||
@@ -634,6 +634,10 @@ public:
|
||||
/// A biased RetainCountConventionKind, where 0 means "unspecified".
|
||||
unsigned RawRetainCountConvention : 3;
|
||||
|
||||
/// Whether the function has the [[clang::unsafe_buffer_usage]] attribute
|
||||
LLVM_PREFERRED_TYPE(bool)
|
||||
unsigned UnsafeBufferUsage : 1;
|
||||
|
||||
// NullabilityKindSize bits are used to encode the nullability. The info
|
||||
// about the return type is stored at position 0, followed by the nullability
|
||||
// of the parameters.
|
||||
@@ -652,7 +656,7 @@ public:
|
||||
|
||||
FunctionInfo()
|
||||
: NullabilityAudited(false), NumAdjustedNullable(0),
|
||||
RawRetainCountConvention() {}
|
||||
RawRetainCountConvention(), UnsafeBufferUsage(0) {}
|
||||
|
||||
static unsigned getMaxNullabilityIndex() {
|
||||
return ((sizeof(NullabilityPayload) * CHAR_BIT) / NullabilityKindSize);
|
||||
@@ -724,6 +728,7 @@ public:
|
||||
inline bool operator==(const FunctionInfo &LHS, const FunctionInfo &RHS) {
|
||||
return static_cast<const CommonEntityInfo &>(LHS) == RHS &&
|
||||
LHS.NullabilityAudited == RHS.NullabilityAudited &&
|
||||
LHS.UnsafeBufferUsage == RHS.UnsafeBufferUsage &&
|
||||
LHS.NumAdjustedNullable == RHS.NumAdjustedNullable &&
|
||||
LHS.NullabilityPayload == RHS.NullabilityPayload &&
|
||||
LHS.ResultType == RHS.ResultType && LHS.Params == RHS.Params &&
|
||||
|
||||
@@ -24,7 +24,8 @@ const uint16_t VERSION_MAJOR = 0;
|
||||
/// API notes file minor version number.
|
||||
///
|
||||
/// When the format changes IN ANY WAY, this number should be incremented.
|
||||
const uint16_t VERSION_MINOR = 39; // BoundsSafety
|
||||
const uint16_t VERSION_MINOR = 40; // 39 for BoundsSafety;
|
||||
// 40 for UnsafeBufferUsageAttr
|
||||
|
||||
const uint8_t kSwiftConforms = 1;
|
||||
const uint8_t kSwiftDoesNotConform = 2;
|
||||
|
||||
@@ -378,6 +378,9 @@ void ReadFunctionInfo(const uint8_t *&Data, FunctionInfo &Info) {
|
||||
ReadCommonEntityInfo(Data, Info);
|
||||
|
||||
uint8_t Payload = endian::readNext<uint8_t, llvm::endianness::little>(Data);
|
||||
if (Payload & 0x1)
|
||||
Info.UnsafeBufferUsage = 1;
|
||||
Payload >>= 0x1;
|
||||
if (auto RawConvention = Payload & 0x7) {
|
||||
auto Convention = static_cast<RetainCountConventionKind>(RawConvention - 1);
|
||||
Info.setRetainCountConvention(Convention);
|
||||
|
||||
@@ -117,6 +117,7 @@ LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const {
|
||||
LLVM_DUMP_METHOD void FunctionInfo::dump(llvm::raw_ostream &OS) const {
|
||||
static_cast<const CommonEntityInfo &>(*this).dump(OS);
|
||||
OS << (NullabilityAudited ? "[NullabilityAudited] " : "")
|
||||
<< (UnsafeBufferUsage ? "[UnsafeBufferUsage] " : "")
|
||||
<< "RawRetainCountConvention: " << RawRetainCountConvention << ' ';
|
||||
if (!ResultType.empty())
|
||||
OS << "Result Type: " << ResultType << ' ';
|
||||
|
||||
@@ -1155,6 +1155,8 @@ void emitFunctionInfo(raw_ostream &OS, const FunctionInfo &FI) {
|
||||
flags <<= 3;
|
||||
if (auto RCC = FI.getRetainCountConvention())
|
||||
flags |= static_cast<uint8_t>(RCC.value()) + 1;
|
||||
flags <<= 0x01;
|
||||
flags |= FI.UnsafeBufferUsage;
|
||||
|
||||
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
|
||||
|
||||
|
||||
@@ -351,6 +351,7 @@ struct Function {
|
||||
StringRef ResultType;
|
||||
StringRef SwiftReturnOwnership;
|
||||
SwiftSafetyKind SafetyKind = SwiftSafetyKind::None;
|
||||
bool UnsafeBufferUsage = false;
|
||||
};
|
||||
|
||||
typedef std::vector<Function> FunctionsSeq;
|
||||
@@ -376,6 +377,7 @@ template <> struct MappingTraits<Function> {
|
||||
IO.mapOptional("SwiftReturnOwnership", F.SwiftReturnOwnership,
|
||||
StringRef(""));
|
||||
IO.mapOptional("SwiftSafety", F.SafetyKind, SwiftSafetyKind::None);
|
||||
IO.mapOptional("UnsafeBufferUsage", F.UnsafeBufferUsage, false);
|
||||
}
|
||||
};
|
||||
} // namespace yaml
|
||||
@@ -1046,6 +1048,7 @@ public:
|
||||
FI.ResultType = std::string(Function.ResultType);
|
||||
FI.SwiftReturnOwnership = std::string(Function.SwiftReturnOwnership);
|
||||
FI.setRetainCountConvention(Function.RetainCountConvention);
|
||||
FI.UnsafeBufferUsage = Function.UnsafeBufferUsage;
|
||||
}
|
||||
|
||||
void convertTagContext(std::optional<Context> ParentContext, const Tag &T,
|
||||
|
||||
@@ -577,6 +577,14 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
|
||||
if (Info.NullabilityAudited)
|
||||
applyNullability(S, D, Info.getReturnTypeInfo(), Metadata);
|
||||
|
||||
// Add [[clang::unsafe_buffer_usage]]
|
||||
if (Info.UnsafeBufferUsage && !D->getAttr<UnsafeBufferUsageAttr>()) {
|
||||
handleAPINotedAttribute<UnsafeBufferUsageAttr>(S, D, true, Metadata, [&]() {
|
||||
return UnsafeBufferUsageAttr::Create(S.getASTContext(),
|
||||
getPlaceholderAttrInfo());
|
||||
});
|
||||
}
|
||||
|
||||
// Parameters.
|
||||
unsigned NumParams = FD ? FD->getNumParams() : MD->param_size();
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
Name: UnsafeBufferUsage
|
||||
Functions:
|
||||
- Name: unsafeFunc
|
||||
UnsafeBufferUsage: true
|
||||
- Name: annotatedUnsafeFunc
|
||||
UnsafeBufferUsage: true
|
||||
- Name: falseAPINotesButAnnotatedUnsafeFunc
|
||||
UnsafeBufferUsage: false
|
||||
...
|
||||
3
clang/test/APINotes/Inputs/Headers/UnsafeBufferUsage.h
Normal file
3
clang/test/APINotes/Inputs/Headers/UnsafeBufferUsage.h
Normal file
@@ -0,0 +1,3 @@
|
||||
void unsafeFunc(int *p, int n);
|
||||
[[clang::unsafe_buffer_usage]] void annotatedUnsafeFunc(int *p, int n);
|
||||
[[clang::unsafe_buffer_usage]] void falseAPINotesButAnnotatedUnsafeFunc(int *p, int n);
|
||||
@@ -65,3 +65,8 @@ module SwiftImportAs {
|
||||
module SwiftReturnOwnershipForObjC {
|
||||
header "SwiftReturnOwnershipForObjC.h"
|
||||
}
|
||||
|
||||
module UnsafeBufferUsage {
|
||||
header "UnsafeBufferUsage.h"
|
||||
export *
|
||||
}
|
||||
|
||||
54
clang/test/APINotes/unsafe-buffer-usage.cpp
Normal file
54
clang/test/APINotes/unsafe-buffer-usage.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules\
|
||||
// RUN: -I %S/Inputs/Headers %s -x c++ -verify -Wunsafe-buffer-usage -Wno-unused-value
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules\
|
||||
// RUN: -I %S/Inputs/Headers %s -x c++ -Wunused-value -ast-dump -ast-dump-filter unsafeFunc | FileCheck %s --check-prefixes=CHECK-UNSAFE
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules\
|
||||
// RUN: -I %S/Inputs/Headers %s -x c++ -Wunused-value -ast-dump -ast-dump-filter annotatedUnsafeFunc | FileCheck %s --check-prefixes=CHECK-ANNO
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules\
|
||||
// RUN: -I %S/Inputs/Headers %s -x c++ -Wunused-value -ast-dump -ast-dump-filter falseAPINotesButAnnotatedUnsafeFunc | FileCheck %s --check-prefixes=CHECK-ANNO-FALSE
|
||||
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules\
|
||||
// RUN: -I %S/Inputs/Headers %s -x c++ -Wunused-value -ast-dump -ast-dump-filter funcWithoutAnnotation | FileCheck %s --check-prefixes=CHECK-UNANNO
|
||||
|
||||
#include "UnsafeBufferUsage.h"
|
||||
|
||||
// CHECK-UNSAFE: FunctionDecl {{.+}} unsafeFunc
|
||||
// CHECK-UNSAFE: UnsafeBufferUsageAttr
|
||||
|
||||
// If the function already has the attribute, the APINotes does
|
||||
// nothing no matter how the attribute is specified in the .apinotes
|
||||
// file:
|
||||
|
||||
// CHECK-ANNO: FunctionDecl {{.+}} annotatedUnsafeFunc
|
||||
// CHECK-ANNO: UnsafeBufferUsageAttr
|
||||
// CHECK-ANNO-NOT: UnsafeBufferUsageAttr
|
||||
// CHECK-ANNO: FunctionDecl {{.+}} annotatedUnsafeFunc
|
||||
|
||||
// CHECK-ANNO-FALSE: FunctionDecl {{.+}} falseAPINotesButAnnotatedUnsafeFunc
|
||||
// CHECK-ANNO-FALSE: UnsafeBufferUsageAttr
|
||||
|
||||
// CHECK-UNANNO: FunctionDecl {{.+}} funcWithoutAnnotation
|
||||
// CHECK-UNANNO-NOT: UnsafeBufferUsageAttr
|
||||
|
||||
void unsafeFunc(int *p, int n) {
|
||||
p[n]; // no warning
|
||||
}
|
||||
|
||||
void annotatedUnsafeFunc(int *p, int n) {
|
||||
p[n]; // no warning
|
||||
}
|
||||
|
||||
void falseAPINotesButAnnotatedUnsafeFunc(int *p, int n) {
|
||||
p[n]; // no warning
|
||||
}
|
||||
|
||||
void funcWithoutAnnotation(int *p, int n) {
|
||||
p[n]; // expected-warning{{unsafe buffer access}}
|
||||
}
|
||||
|
||||
void caller(int *p, int n) {
|
||||
unsafeFunc(p, n); // expected-warning{{function introduces unsafe buffer manipulation}}
|
||||
annotatedUnsafeFunc(p, n); // expected-warning{{function introduces unsafe buffer manipulation}}
|
||||
falseAPINotesButAnnotatedUnsafeFunc(p, n); // expected-warning{{function introduces unsafe buffer manipulation}}
|
||||
funcWithoutAnnotation(p, n); // no warning
|
||||
}
|
||||
Reference in New Issue
Block a user