clang  10.0.0svn
ObjCAutoreleaseWriteChecker.cpp
Go to the documentation of this file.
1 //===- ObjCAutoreleaseWriteChecker.cpp ----------------------------*- C++ -*-==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines ObjCAutoreleaseWriteChecker which warns against writes
10 // into autoreleased out parameters which cause crashes.
11 // An example of a problematic write is a write to {@code error} in the example
12 // below:
13 //
14 // - (BOOL) mymethod:(NSError *__autoreleasing *)error list:(NSArray*) list {
15 // [list enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
16 // NSString *myString = obj;
17 // if ([myString isEqualToString:@"error"] && error)
18 // *error = [NSError errorWithDomain:@"MyDomain" code:-1];
19 // }];
20 // return false;
21 // }
22 //
23 // Such code will crash on read from `*error` due to the autorelease pool
24 // in `enumerateObjectsUsingBlock` implementation freeing the error object
25 // on exit from the function.
26 //
27 //===----------------------------------------------------------------------===//
28 
35 #include "llvm/ADT/Twine.h"
36 
37 using namespace clang;
38 using namespace ento;
39 using namespace ast_matchers;
40 
41 namespace {
42 
43 const char *ProblematicWriteBind = "problematicwrite";
44 const char *CapturedBind = "capturedbind";
45 const char *ParamBind = "parambind";
46 const char *IsMethodBind = "ismethodbind";
47 
48 class ObjCAutoreleaseWriteChecker : public Checker<check::ASTCodeBody> {
49 public:
50  void checkASTCodeBody(const Decl *D,
51  AnalysisManager &AM,
52  BugReporter &BR) const;
53 private:
54  std::vector<std::string> SelectorsWithAutoreleasingPool = {
55  // Common to NSArray, NSSet, NSOrderedSet
56  "enumerateObjectsUsingBlock:",
57  "enumerateObjectsWithOptions:usingBlock:",
58 
59  // Common to NSArray and NSOrderedSet
60  "enumerateObjectsAtIndexes:options:usingBlock:",
61  "indexOfObjectAtIndexes:options:passingTest:",
62  "indexesOfObjectsAtIndexes:options:passingTest:",
63  "indexOfObjectPassingTest:",
64  "indexOfObjectWithOptions:passingTest:",
65  "indexesOfObjectsPassingTest:",
66  "indexesOfObjectsWithOptions:passingTest:",
67 
68  // NSDictionary
69  "enumerateKeysAndObjectsUsingBlock:",
70  "enumerateKeysAndObjectsWithOptions:usingBlock:",
71  "keysOfEntriesPassingTest:",
72  "keysOfEntriesWithOptions:passingTest:",
73 
74  // NSSet
75  "objectsPassingTest:",
76  "objectsWithOptions:passingTest:",
77  "enumerateIndexPathsWithOptions:usingBlock:",
78 
79  // NSIndexSet
80  "enumerateIndexesWithOptions:usingBlock:",
81  "enumerateIndexesUsingBlock:",
82  "enumerateIndexesInRange:options:usingBlock:",
83  "enumerateRangesUsingBlock:",
84  "enumerateRangesWithOptions:usingBlock:",
85  "enumerateRangesInRange:options:usingBlock:",
86  "indexPassingTest:",
87  "indexesPassingTest:",
88  "indexWithOptions:passingTest:",
89  "indexesWithOptions:passingTest:",
90  "indexInRange:options:passingTest:",
91  "indexesInRange:options:passingTest:"
92  };
93 
94  std::vector<std::string> FunctionsWithAutoreleasingPool = {
95  "dispatch_async", "dispatch_group_async", "dispatch_barrier_async"};
96 };
97 }
98 
99 static inline std::vector<llvm::StringRef> toRefs(std::vector<std::string> V) {
100  return std::vector<llvm::StringRef>(V.begin(), V.end());
101 }
102 
103 static auto callsNames(std::vector<std::string> FunctionNames)
104  -> decltype(callee(functionDecl())) {
105  return callee(functionDecl(hasAnyName(toRefs(FunctionNames))));
106 }
107 
108 static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR,
109  AnalysisManager &AM,
110  const ObjCAutoreleaseWriteChecker *Checker) {
111  AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
112 
113  const auto *PVD = Match.getNodeAs<ParmVarDecl>(ParamBind);
114  QualType Ty = PVD->getType();
115  if (Ty->getPointeeType().getObjCLifetime() != Qualifiers::OCL_Autoreleasing)
116  return;
117  const char *ActionMsg = "Write to";
118  const auto *MarkedStmt = Match.getNodeAs<Expr>(ProblematicWriteBind);
119  bool IsCapture = false;
120 
121  // Prefer to warn on write, but if not available, warn on capture.
122  if (!MarkedStmt) {
123  MarkedStmt = Match.getNodeAs<Expr>(CapturedBind);
124  assert(MarkedStmt);
125  ActionMsg = "Capture of";
126  IsCapture = true;
127  }
128 
129  SourceRange Range = MarkedStmt->getSourceRange();
130  PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin(
131  MarkedStmt, BR.getSourceManager(), ADC);
132  bool IsMethod = Match.getNodeAs<ObjCMethodDecl>(IsMethodBind) != nullptr;
133  const char *Name = IsMethod ? "method" : "function";
134 
135  BR.EmitBasicReport(
136  ADC->getDecl(), Checker,
137  /*Name=*/(llvm::Twine(ActionMsg)
138  + " autoreleasing out parameter inside autorelease pool").str(),
139  /*BugCategory=*/"Memory",
140  (llvm::Twine(ActionMsg) + " autoreleasing out parameter " +
141  (IsCapture ? "'" + PVD->getName() + "'" + " " : "") + "inside " +
142  "autorelease pool that may exit before " + Name + " returns; consider "
143  "writing first to a strong local variable declared outside of the block")
144  .str(),
145  Location,
146  Range);
147 }
148 
149 void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D,
150  AnalysisManager &AM,
151  BugReporter &BR) const {
152 
153  auto DoublePointerParamM =
154  parmVarDecl(hasType(hasCanonicalType(pointerType(
155  pointee(hasCanonicalType(objcObjectPointerType()))))))
156  .bind(ParamBind);
157 
158  auto ReferencedParamM =
159  declRefExpr(to(parmVarDecl(DoublePointerParamM))).bind(CapturedBind);
160 
161  // Write into a binded object, e.g. *ParamBind = X.
162  auto WritesIntoM = binaryOperator(
163  hasLHS(unaryOperator(
164  hasOperatorName("*"),
165  hasUnaryOperand(
166  ignoringParenImpCasts(ReferencedParamM))
167  )),
168  hasOperatorName("=")
169  ).bind(ProblematicWriteBind);
170 
171  auto ArgumentCaptureM = hasAnyArgument(
172  ignoringParenImpCasts(ReferencedParamM));
173  auto CapturedInParamM = stmt(anyOf(
174  callExpr(ArgumentCaptureM),
175  objcMessageExpr(ArgumentCaptureM)));
176 
177  // WritesIntoM happens inside a block passed as an argument.
178  auto WritesOrCapturesInBlockM = hasAnyArgument(allOf(
179  hasType(hasCanonicalType(blockPointerType())),
181  stmt(anyOf(WritesIntoM, CapturedInParamM))
182  )));
183 
184  auto BlockPassedToMarkedFuncM = stmt(anyOf(
185  callExpr(allOf(
186  callsNames(FunctionsWithAutoreleasingPool), WritesOrCapturesInBlockM)),
188  hasAnySelector(toRefs(SelectorsWithAutoreleasingPool)),
189  WritesOrCapturesInBlockM))
190  ));
191 
192  auto HasParamAndWritesInMarkedFuncM = allOf(
193  hasAnyParameter(DoublePointerParamM),
194  forEachDescendant(BlockPassedToMarkedFuncM));
195 
196  auto MatcherM = decl(anyOf(
197  objcMethodDecl(HasParamAndWritesInMarkedFuncM).bind(IsMethodBind),
198  functionDecl(HasParamAndWritesInMarkedFuncM),
199  blockDecl(HasParamAndWritesInMarkedFuncM)));
200 
201  auto Matches = match(MatcherM, *D, AM.getASTContext());
202  for (BoundNodes Match : Matches)
203  emitDiagnostics(Match, D, BR, AM, this);
204 }
205 
206 void ento::registerAutoreleaseWriteChecker(CheckerManager &Mgr) {
207  Mgr.registerChecker<ObjCAutoreleaseWriteChecker>();
208 }
209 
210 bool ento::shouldRegisterAutoreleaseWriteChecker(const LangOptions &LO) {
211  return true;
212 }
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
A (possibly-)qualified type.
Definition: Type.h:643
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:88
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:138
const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl
Matches function declarations.
Represents a parameter to a function.
Definition: Decl.h:1600
AnalysisDeclContext contains the context data for the function or method under analysis.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
const internal::VariadicDynCastAllOfMatcher< Stmt, DeclRefExpr > declRefExpr
Matches expressions that refer to declarations.
const T * getNodeAs(StringRef ID) const
Returns the AST node bound to ID.
Definition: ASTMatchers.h:110
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.
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. ...
This represents one expression.
Definition: Expr.h:108
#define V(N, I)
Definition: ASTContext.h:2921
const internal::VariadicAllOfMatcher< Decl > decl
Matches declarations.
Maps string IDs to AST nodes matched by parts of a matcher.
Definition: ASTMatchers.h:103
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.
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:377
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:171
Dataflow Directional Tag Classes.
const internal::VariadicDynCastAllOfMatcher< Stmt, UnaryOperator > unaryOperator
Matches unary operator expressions.
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.
const internal::VariadicDynCastAllOfMatcher< Stmt, ObjCMessageExpr > objcMessageExpr
Matches ObjectiveC Message invocation expressions.
const AstTypeMatcher< BlockPointerType > blockPointerType
Matches block pointer types, i.e.