[clang] fe17b30 - [attributes][analyzer] Add annotations for handles.
Gabor Horvath via cfe-commits
cfe-commits at lists.llvm.org
Fri Dec 20 11:48:35 PST 2019
Author: Gabor Horvath
Date: 2019-12-20T11:47:55-08:00
New Revision: fe17b30a79572f0d50fe78f6a07d5194cbf90860
URL: https://github.com/llvm/llvm-project/commit/fe17b30a79572f0d50fe78f6a07d5194cbf90860
DIFF: https://github.com/llvm/llvm-project/commit/fe17b30a79572f0d50fe78f6a07d5194cbf90860.diff
LOG: [attributes][analyzer] Add annotations for handles.
These annotations will be used in an upcomming static analyzer check
that finds handle leaks, use after releases, and double releases.
Differential Revision: https://reviews.llvm.org/D70469
Added:
clang/test/Sema/attr-handles.cpp
Modified:
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/AST/TypePrinter.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaType.cpp
clang/test/Misc/pragma-attribute-supported-attributes-list.test
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 286807ec779f..d9ca121b6510 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3471,3 +3471,27 @@ def NoBuiltin : Attr {
let Subjects = SubjectList<[Function]>;
let Documentation = [NoBuiltinDocs];
}
+
+// FIXME: This attribute is not inheritable, it will not be propagated to
+// redecls. [[clang::lifetimebound]] has the same problems. This should be
+// fixed in TableGen (by probably adding a new inheritable flag).
+def AcquireHandle : DeclOrTypeAttr {
+ let Spellings = [Clang<"acquire_handle">];
+ let Args = [StringArgument<"HandleType">];
+ let Subjects = SubjectList<[Function, TypedefName, ParmVar]>;
+ let Documentation = [AcquireHandleDocs];
+}
+
+def UseHandle : InheritableParamAttr {
+ let Spellings = [Clang<"use_handle">];
+ let Args = [StringArgument<"HandleType">];
+ let Subjects = SubjectList<[ParmVar]>;
+ let Documentation = [UseHandleDocs];
+}
+
+def ReleaseHandle : InheritableParamAttr {
+ let Spellings = [Clang<"release_handle">];
+ let Args = [StringArgument<"HandleType">];
+ let Subjects = SubjectList<[ParmVar]>;
+ let Documentation = [ReleaseHandleDocs];
+}
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 3fa6993a5fd0..9b4afa8f128e 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -4696,3 +4696,64 @@ once.
}
}];
}
+
+def HandleDocs : DocumentationCategory<"Handle Attributes"> {
+ let Content = [{
+Handles are a way to identify resources like files, sockets, and processes.
+They are more opaque than pointers and widely used in system programming. They
+have similar risks such as never releasing a resource associated with a handle,
+attempting to use a handle that was already released, or trying to release a
+handle twice. Using the annotations below it is possible to make the ownership
+of the handles clear: whose responsibility is to release them. They can also
+aid static analysis tools to find bugs.
+ }];
+}
+
+def AcquireHandleDocs : Documentation {
+ let Category = HandleDocs;
+ let Content = [{
+If this annotation is on a function or a function type it is assumed to return
+a new handle. In case this annotation is on an output parameter,
+the function is assumed to fill the corresponding argument with a new
+handle.
+
+.. code-block:: c++
+
+ // Output arguments from Zircon.
+ zx_status_t zx_socket_create(uint32_t options,
+ zx_handle_t __attribute__((acquire_handle)) * out0,
+ zx_handle_t* out1 [[clang::acquire_handle]]);
+
+
+ // Returned handle.
+ [[clang::acquire_handle]] int open(const char *path, int oflag, ... );
+ int open(const char *path, int oflag, ... ) __attribute__((acquire_handle));
+ }];
+}
+
+def UseHandleDocs : Documentation {
+ let Category = HandleDocs;
+ let Content = [{
+A function taking a handle by value might close the handle. If a function
+parameter is annotated with `use_handle` it is assumed to not to change
+the state of the handle. It is also assumed to require an open handle to work with.
+
+.. code-block:: c++
+
+ zx_status_t zx_port_wait(zx_handle_t handle [[clang::use_handle]],
+ zx_time_t deadline,
+ zx_port_packet_t* packet);
+ }];
+}
+
+def ReleaseHandleDocs : Documentation {
+ let Category = HandleDocs;
+ let Content = [{
+If a function parameter is annotated with `release_handle` it is assumed to
+close the handle. It is also assumed to require an open handle to work with.
+
+.. code-block:: c++
+
+ zx_status_t zx_handle_close(zx_handle_t handle [[clang::release_handle]]);
+ }];
+}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b714cb81f0ca..1d81f69a7bd2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3131,6 +3131,8 @@ def warn_declspec_allocator_nonpointer : Warning<
def err_cconv_incomplete_param_type : Error<
"parameter %0 must have a complete type to use function %1 with the %2 "
"calling convention">;
+def err_attribute_output_parameter : Error<
+ "attribute only applies to output parameters">;
def ext_cannot_use_trivial_abi : ExtWarn<
"'trivial_abi' cannot be applied to %0">, InGroup<IgnoredAttributes>;
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 0228d637d018..be23a497577e 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1555,6 +1555,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::NoDeref:
OS << "noderef";
break;
+ case attr::AcquireHandle:
+ OS << "acquire_handle";
+ break;
}
OS << "))";
}
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 678320487453..ac83a9b156e8 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6601,6 +6601,31 @@ static void handleMSAllocatorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
handleSimpleAttribute<MSAllocatorAttr>(S, D, AL);
}
+static void handeAcquireHandleAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ if (AL.isUsedAsTypeAttr())
+ return;
+ // Warn if the parameter is definitely not an output parameter.
+ if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
+ if (PVD->getType()->isIntegerType()) {
+ S.Diag(AL.getLoc(), diag::err_attribute_output_parameter)
+ << AL.getRange();
+ return;
+ }
+ }
+ StringRef Argument;
+ if (!S.checkStringLiteralArgumentAttr(AL, 0, Argument))
+ return;
+ D->addAttr(AcquireHandleAttr::Create(S.Context, Argument, AL));
+}
+
+template<typename Attr>
+static void handleHandleAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ StringRef Argument;
+ if (!S.checkStringLiteralArgumentAttr(AL, 0, Argument))
+ return;
+ D->addAttr(Attr::Create(S.Context, Argument, AL));
+}
+
//===----------------------------------------------------------------------===//
// Top Level Sema Entry Points
//===----------------------------------------------------------------------===//
@@ -7378,6 +7403,18 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_ArmMveAlias:
handleArmMveAliasAttr(S, D, AL);
break;
+
+ case ParsedAttr::AT_AcquireHandle:
+ handeAcquireHandleAttr(S, D, AL);
+ break;
+
+ case ParsedAttr::AT_ReleaseHandle:
+ handleHandleAttr<ReleaseHandleAttr>(S, D, AL);
+ break;
+
+ case ParsedAttr::AT_UseHandle:
+ handleHandleAttr<UseHandleAttr>(S, D, AL);
+ break;
}
}
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 4dc10ffdd64e..d77542be53fb 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -7634,6 +7634,18 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
else if (!handleFunctionTypeAttr(state, attr, type))
distributeFunctionTypeAttr(state, attr, type);
break;
+ case ParsedAttr::AT_AcquireHandle: {
+ if (!type->isFunctionType())
+ return;
+ StringRef HandleType;
+ if (!state.getSema().checkStringLiteralArgumentAttr(attr, 0, HandleType))
+ return;
+ type = state.getAttributedType(
+ AcquireHandleAttr::Create(state.getSema().Context, HandleType, attr),
+ type, type);
+ attr.setUsedAsTypeAttr();
+ break;
+ }
}
// Handle attributes that are defined in a macro. We do not want this to be
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index dc2d668aaa16..ef518cdd1bef 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -9,6 +9,7 @@
// CHECK-NEXT: AMDGPUWavesPerEU (SubjectMatchRule_function)
// CHECK-NEXT: AVRSignal (SubjectMatchRule_function)
// CHECK-NEXT: AbiTag (SubjectMatchRule_record_not_is_union, SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_namespace)
+// CHECK-NEXT: AcquireHandle (SubjectMatchRule_function, SubjectMatchRule_type_alias, SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: Alias (SubjectMatchRule_function, SubjectMatchRule_variable_is_global)
// CHECK-NEXT: AlignValue (SubjectMatchRule_variable, SubjectMatchRule_type_alias)
// CHECK-NEXT: AllocSize (SubjectMatchRule_function)
@@ -128,6 +129,7 @@
// CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union)
+// CHECK-NEXT: ReleaseHandle (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function)
// CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function)
// CHECK-NEXT: Restrict (SubjectMatchRule_function)
@@ -145,6 +147,7 @@
// CHECK-NEXT: Target (SubjectMatchRule_function)
// CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member)
// CHECK-NEXT: TrivialABI (SubjectMatchRule_record)
+// CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: VecReturn (SubjectMatchRule_record)
// CHECK-NEXT: VecTypeHint (SubjectMatchRule_function)
// CHECK-NEXT: WarnUnused (SubjectMatchRule_record)
diff --git a/clang/test/Sema/attr-handles.cpp b/clang/test/Sema/attr-handles.cpp
new file mode 100644
index 000000000000..5abb1e8d00bc
--- /dev/null
+++ b/clang/test/Sema/attr-handles.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// Decl annotations.
+void f(int *a __attribute__((acquire_handle("Fuchsia"))));
+void (*fp)(int handle [[clang::use_handle("Fuchsia")]]);
+auto lambda = [](int handle [[clang::use_handle("Fuchsia")]]){};
+void g(int a __attribute__((acquire_handle("Fuchsia")))); // expected-error {{attribute only applies to output parameters}}
+void h(int *a __attribute__((acquire_handle))); // expected-error {{'acquire_handle' attribute takes one argument}}
+void h(int *a __attribute__((acquire_handle(1)))); // expected-error {{attribute requires a string}}
+void h(int *a __attribute__((acquire_handle("RandomString", "AndAnother")))); // expected-error {{'acquire_handle' attribute takes one argument}}
+__attribute__((release_handle("Fuchsia"))) int i(); // expected-warning {{'release_handle' attribute only applies to parameters}}
+__attribute__((use_handle("Fuchsia"))) int j(); // expected-warning {{'use_handle' attribute only applies to parameters}}
+int a __attribute__((acquire_handle("Fuchsia"))); // expected-warning {{'acquire_handle' attribute only applies to functions, typedefs, and parameters}}
+int (* __attribute__((acquire_handle("Fuchsia"))) fpt)(char *); // expected-warning {{'acquire_handle' attribute only applies to functions, typedefs, and parameters}}
+
+// Type annotations.
+auto lambdat = [](int handle __attribute__((use_handle("Fuchsia"))))
+ __attribute__((acquire_handle("Fuchsia"))) -> int { return 0; };
+int __attribute((acquire_handle("Fuchsia"))) ta; // expected-warning {{'acquire_handle' attribute only applies to functions, typedefs, and parameters}}
+
+// Typedefs.
+typedef int callback(char *) __attribute__((acquire_handle("Fuchsia")));
More information about the cfe-commits
mailing list