Skip to content

Commit

Permalink
[attributes][analyzer] Add annotations for handles.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Xazax-hun committed Dec 20, 2019
1 parent 304d130 commit fe17b30
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 0 deletions.
24 changes: 24 additions & 0 deletions clang/include/clang/Basic/Attr.td
Expand Up @@ -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];
}
61 changes: 61 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Expand Up @@ -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]]);
}];
}
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -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>;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/TypePrinter.cpp
Expand Up @@ -1555,6 +1555,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::NoDeref:
OS << "noderef";
break;
case attr::AcquireHandle:
OS << "acquire_handle";
break;
}
OS << "))";
}
Expand Down
37 changes: 37 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -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;
}
}

Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Sema/SemaType.cpp
Expand Up @@ -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
Expand Down
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
22 changes: 22 additions & 0 deletions 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")));

0 comments on commit fe17b30

Please sign in to comment.