clang  7.0.0
ObjCAutoreleaseWriteChecker.cpp
Go to the documentation of this file.
1 //===- ObjCAutoreleaseWriteChecker.cpp ----------------------------*- C++ -*-==//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines ObjCAutoreleaseWriteChecker which warns against writes
11 // into autoreleased out parameters which cause crashes.
12 // An example of a problematic write is a write to {@code error} in the example
13 // below:
14 //
15 // - (BOOL) mymethod:(NSError *__autoreleasing *)error list:(NSArray*) list {
16 // [list enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
17 // NSString *myString = obj;
18 // if ([myString isEqualToString:@"error"] && error)
19 // *error = [NSError errorWithDomain:@"MyDomain" code:-1];
20 // }];
21 // return false;
22 // }
23 //
24 // Such code will crash on read from `*error` due to the autorelease pool
25 // in `enumerateObjectsUsingBlock` implementation freeing the error object
26 // on exit from the function.
27 //
28 //===----------------------------------------------------------------------===//
29 
30 #include "ClangSACheckers.h"
36 #include "llvm/ADT/Twine.h"
37 
38 using namespace clang;
39 using namespace ento;
40 using namespace ast_matchers;
41 
42 namespace {
43 
44 const char *ProblematicWriteBind = "problematicwrite";
45 const char *CapturedBind = "capturedbind";
46 const char *ParamBind = "parambind";
47 const char *IsMethodBind = "ismethodbind";
48 
49 class ObjCAutoreleaseWriteChecker : public Checker<check::ASTCodeBody> {
50 public:
51  void checkASTCodeBody(const Decl *D,
52  AnalysisManager &AM,
53  BugReporter &BR) const;
54 private:
55  std::vector<std::string> SelectorsWithAutoreleasingPool = {
56  // Common to NSArray, NSSet, NSOrderedSet
57  "enumerateObjectsUsingBlock:",
58  "enumerateObjectsWithOptions:usingBlock:",
59 
60  // Common to NSArray and NSOrderedSet
61  "enumerateObjectsAtIndexes:options:usingBlock:",
62  "indexOfObjectAtIndexes:options:passingTest:",
63  "indexesOfObjectsAtIndexes:options:passingTest:",
64  "indexOfObjectPassingTest:",
65  "indexOfObjectWithOptions:passingTest:",
66  "indexesOfObjectsPassingTest:",
67  "indexesOfObjectsWithOptions:passingTest:",
68 
69  // NSDictionary
70  "enumerateKeysAndObjectsUsingBlock:",
71  "enumerateKeysAndObjectsWithOptions:usingBlock:",
72  "keysOfEntriesPassingTest:",
73  "keysOfEntriesWithOptions:passingTest:",
74 
75  // NSSet
76  "objectsPassingTest:",
77  "objectsWithOptions:passingTest:",
78  "enumerateIndexPathsWithOptions:usingBlock:",
79 
80  // NSIndexSet
81  "enumerateIndexesWithOptions:usingBlock:",
82  "enumerateIndexesUsingBlock:",
83  "enumerateIndexesInRange:options:usingBlock:",
84  "enumerateRangesUsingBlock:",
85  "enumerateRangesWithOptions:usingBlock:",
86  "enumerateRangesInRange:options:usingBlock:",
87  "indexPassingTest:",
88  "indexesPassingTest:",
89  "indexWithOptions:passingTest:",
90  "indexesWithOptions:passingTest:",
91  "indexInRange:options:passingTest:",
92  "indexesInRange:options:passingTest:"
93  };
94 
95  std::vector<std::string> FunctionsWithAutoreleasingPool = {
96  "dispatch_async", "dispatch_group_async", "dispatch_barrier_async"};
97 };
98 }
99 
100 static inline std::vector<llvm::StringRef> toRefs(std::vector<std::string> V) {
101  return std::vector<llvm::StringRef>(V.begin(), V.end());
102 }
103 
104 static auto callsNames(std::vector<std::string> FunctionNames)
105  -> decltype(callee(functionDecl())) {
106  return callee(functionDecl(hasAnyName(toRefs(FunctionNames))));
107 }
108 
109 static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR,
110  AnalysisManager &AM,
111  const ObjCAutoreleaseWriteChecker *Checker) {
113 
114  const auto *PVD = Match.getNodeAs<ParmVarDecl>(ParamBind);
115  QualType Ty = PVD->getType();
116  if (Ty->getPointeeType().getObjCLifetime() != Qualifiers::OCL_Autoreleasing)
117  return;
118  const char *ActionMsg = "Write to";
119  const auto *MarkedStmt = Match.getNodeAs<Expr>(ProblematicWriteBind);
120  bool IsCapture = false;
121 
122  // Prefer to warn on write, but if not available, warn on capture.
123  if (!MarkedStmt) {
124  MarkedStmt = Match.getNodeAs<Expr>(CapturedBind);
125  assert(MarkedStmt);
126  ActionMsg = "Capture of";
127  IsCapture = true;
128  }
129 
130  SourceRange Range = MarkedStmt->getSourceRange();
132  MarkedStmt, BR.getSourceManager(), ADC);
133  bool IsMethod = Match.getNodeAs<ObjCMethodDecl>(IsMethodBind) != nullptr;
134  const char *Name = IsMethod ? "method" : "function";
135 
136  BR.EmitBasicReport(
137  ADC->getDecl(), Checker,
138  /*Name=*/(llvm::Twine(ActionMsg)
139  + " autoreleasing out parameter inside autorelease pool").str(),
140  /*Category=*/"Memory",
141  (llvm::Twine(ActionMsg) + " autoreleasing out parameter " +
142  (IsCapture ? "'" + PVD->getName() + "'" + " " : "") + "inside " +
143  "autorelease pool that may exit before " + Name + " returns; consider "
144  "writing first to a strong local variable declared outside of the block")
145  .str(),
146  Location,
147  Range);
148 }
149 
150 void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D,
151  AnalysisManager &AM,
152  BugReporter &BR) const {
153 
154  auto DoublePointerParamM =
155  parmVarDecl(hasType(hasCanonicalType(pointerType(
156  pointee(hasCanonicalType(objcObjectPointerType()))))))
157  .bind(ParamBind);
158 
159  auto ReferencedParamM =
160  declRefExpr(to(parmVarDecl(DoublePointerParamM))).bind(CapturedBind);
161 
162  // Write into a binded object, e.g. *ParamBind = X.
163  auto WritesIntoM = binaryOperator(
164  hasLHS(unaryOperator(
165  hasOperatorName("*"),
166  hasUnaryOperand(
167  ignoringParenImpCasts(ReferencedParamM))
168  )),
169  hasOperatorName("=")
170  ).bind(ProblematicWriteBind);
171 
172  auto ArgumentCaptureM = hasAnyArgument(
173  ignoringParenImpCasts(ReferencedParamM));
174  auto CapturedInParamM = stmt(anyOf(
175  callExpr(ArgumentCaptureM),
176  objcMessageExpr(ArgumentCaptureM)));
177 
178  // WritesIntoM happens inside a block passed as an argument.
179  auto WritesOrCapturesInBlockM = hasAnyArgument(allOf(
180  hasType(hasCanonicalType(blockPointerType())),
182  stmt(anyOf(WritesIntoM, CapturedInParamM))
183  )));
184 
185  auto BlockPassedToMarkedFuncM = stmt(anyOf(
186  callExpr(allOf(
187  callsNames(FunctionsWithAutoreleasingPool), WritesOrCapturesInBlockM)),
189  hasAnySelector(toRefs(SelectorsWithAutoreleasingPool)),
190  WritesOrCapturesInBlockM))
191  ));
192 
193  auto HasParamAndWritesInMarkedFuncM = allOf(
194  hasAnyParameter(DoublePointerParamM),
195  forEachDescendant(BlockPassedToMarkedFuncM));
196 
197  auto MatcherM = decl(anyOf(
198  objcMethodDecl(HasParamAndWritesInMarkedFuncM).bind(IsMethodBind),
199  functionDecl(HasParamAndWritesInMarkedFuncM),
200  blockDecl(HasParamAndWritesInMarkedFuncM)));
201 
202  auto Matches = match(MatcherM, *D, AM.getASTContext());
203  for (BoundNodes Match : Matches)
204  emitDiagnostics(Match, D, BR, AM, this);
205 }
206 
207 void ento::registerAutoreleaseWriteChecker(CheckerManager &Mgr) {
208  Mgr.registerChecker<ObjCAutoreleaseWriteChecker>();
209 }
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
A (possibly-)qualified type.
Definition: Type.h:655
const internal::VariadicAllOfMatcher< Stmt > stmt
Matches statements.
const internal::VariadicFunction< internal::Matcher< ObjCMessageExpr >, StringRef, internal::hasAnySelectorFunc > hasAnySelector
Matches when at least one of the supplied string equals to the Selector.getAsString() ...
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf
Matches if any of the given matchers matches.
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
A Range represents the closed range [from, to].
const AstTypeMatcher< PointerType > pointerType
Matches pointer types, but does not match Objective-C object pointer types.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> allOf
Matches if all given matchers match.
const internal::VariadicDynCastAllOfMatcher< Stmt, BinaryOperator > binaryOperator
Matches binary operator expressions.
ObjCMethodDecl - Represents an instance or class method declaration.
Definition: DeclObjC.h:139
const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl
Matches function declarations.
Represents a parameter to a function.
Definition: Decl.h:1535
AnalysisDeclContext contains the context data for the function or method under analysis.
const internal::VariadicDynCastAllOfMatcher< Stmt, DeclRefExpr > declRefExpr
Matches expressions that refer to declarations.
const internal::VariadicFunction< internal::Matcher< NamedDecl >, StringRef, internal::hasAnyNameFunc > hasAnyName
Matches NamedDecl nodes that have any of the specified names.
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
ASTContext & getASTContext() override
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
static auto callsNames(std::vector< std::string > FunctionNames) -> decltype(callee(functionDecl()))
const internal::ArgumentAdaptingMatcherFunc< internal::ForEachDescendantMatcher > forEachDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher. ...
Expr - This represents one expression.
Definition: Expr.h:106
const internal::VariadicAllOfMatcher< Decl > decl
Matches declarations.
CHECKER * registerChecker(AT... Args)
Used to register checkers.
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:412
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
const internal::VariadicDynCastAllOfMatcher< Decl, ParmVarDecl > parmVarDecl
Matches parameter variable declarations.
const internal::VariadicDynCastAllOfMatcher< Decl, ObjCMethodDecl > objcMethodDecl
Matches Objective-C method declarations.
const Decl * getDecl() const
static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR, AnalysisManager &AM, const ObjCAutoreleaseWriteChecker *Checker)
Assigning into this object requires a lifetime extension.
Definition: Type.h:188
BoundNodesTreeBuilder BoundNodes
Dataflow Directional Tag Classes.
const internal::VariadicDynCastAllOfMatcher< Stmt, UnaryOperator > unaryOperator
Matches unary operator expressions.
SourceManager & getSourceManager()
Definition: BugReporter.h:474
static std::vector< llvm::StringRef > toRefs(std::vector< std::string > V)
const AstTypeMatcher< ObjCObjectPointerType > objcObjectPointerType
Matches an Objective-C object pointer type, which is different from a pointer type, despite being syntactically similar.
const internal::VariadicDynCastAllOfMatcher< Decl, BlockDecl > blockDecl
Matches block declarations.
A trivial tuple used to represent a source range.
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=None)
const internal::VariadicDynCastAllOfMatcher< Stmt, ObjCMessageExpr > objcMessageExpr
Matches ObjectiveC Message invocation expressions.
const AstTypeMatcher< BlockPointerType > blockPointerType
Matches block pointer types, i.e.