clang  12.0.0git
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 
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 const char *IsARPBind = "isautoreleasepoolbind";
49 
50 class ObjCAutoreleaseWriteChecker : public Checker<check::ASTCodeBody> {
51 public:
52  void checkASTCodeBody(const Decl *D,
53  AnalysisManager &AM,
54  BugReporter &BR) const;
55 private:
56  std::vector<std::string> SelectorsWithAutoreleasingPool = {
57  // Common to NSArray, NSSet, NSOrderedSet
58  "enumerateObjectsUsingBlock:",
59  "enumerateObjectsWithOptions:usingBlock:",
60 
61  // Common to NSArray and NSOrderedSet
62  "enumerateObjectsAtIndexes:options:usingBlock:",
63  "indexOfObjectAtIndexes:options:passingTest:",
64  "indexesOfObjectsAtIndexes:options:passingTest:",
65  "indexOfObjectPassingTest:",
66  "indexOfObjectWithOptions:passingTest:",
67  "indexesOfObjectsPassingTest:",
68  "indexesOfObjectsWithOptions:passingTest:",
69 
70  // NSDictionary
71  "enumerateKeysAndObjectsUsingBlock:",
72  "enumerateKeysAndObjectsWithOptions:usingBlock:",
73  "keysOfEntriesPassingTest:",
74  "keysOfEntriesWithOptions:passingTest:",
75 
76  // NSSet
77  "objectsPassingTest:",
78  "objectsWithOptions:passingTest:",
79  "enumerateIndexPathsWithOptions:usingBlock:",
80 
81  // NSIndexSet
82  "enumerateIndexesWithOptions:usingBlock:",
83  "enumerateIndexesUsingBlock:",
84  "enumerateIndexesInRange:options:usingBlock:",
85  "enumerateRangesUsingBlock:",
86  "enumerateRangesWithOptions:usingBlock:",
87  "enumerateRangesInRange:options:usingBlock:",
88  "indexPassingTest:",
89  "indexesPassingTest:",
90  "indexWithOptions:passingTest:",
91  "indexesWithOptions:passingTest:",
92  "indexInRange:options:passingTest:",
93  "indexesInRange:options:passingTest:"
94  };
95 
96  std::vector<std::string> FunctionsWithAutoreleasingPool = {
97  "dispatch_async", "dispatch_group_async", "dispatch_barrier_async"};
98 };
99 }
100 
101 static inline std::vector<llvm::StringRef> toRefs(std::vector<std::string> V) {
102  return std::vector<llvm::StringRef>(V.begin(), V.end());
103 }
104 
105 static decltype(auto) callsNames(std::vector<std::string> FunctionNames) {
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) {
112  AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
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();
131  PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin(
132  MarkedStmt, BR.getSourceManager(), ADC);
133 
134  bool IsMethod = Match.getNodeAs<ObjCMethodDecl>(IsMethodBind) != nullptr;
135  const char *FunctionDescription = IsMethod ? "method" : "function";
136  bool IsARP = Match.getNodeAs<ObjCAutoreleasePoolStmt>(IsARPBind) != nullptr;
137 
138  llvm::SmallString<128> BugNameBuf;
139  llvm::raw_svector_ostream BugName(BugNameBuf);
140  BugName << ActionMsg
141  << " autoreleasing out parameter inside autorelease pool";
142 
143  llvm::SmallString<128> BugMessageBuf;
144  llvm::raw_svector_ostream BugMessage(BugMessageBuf);
145  BugMessage << ActionMsg << " autoreleasing out parameter ";
146  if (IsCapture)
147  BugMessage << "'" + PVD->getName() + "' ";
148 
149  BugMessage << "inside ";
150  if (IsARP)
151  BugMessage << "locally-scoped autorelease pool;";
152  else
153  BugMessage << "autorelease pool that may exit before "
154  << FunctionDescription << " returns;";
155 
156  BugMessage << " consider writing first to a strong local variable"
157  " declared outside ";
158  if (IsARP)
159  BugMessage << "of the autorelease pool";
160  else
161  BugMessage << "of the block";
162 
163  BR.EmitBasicReport(ADC->getDecl(), Checker, BugName.str(),
164  categories::MemoryRefCount, BugMessage.str(), Location,
165  Range);
166 }
167 
168 void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D,
169  AnalysisManager &AM,
170  BugReporter &BR) const {
171 
172  auto DoublePointerParamM =
173  parmVarDecl(hasType(hasCanonicalType(pointerType(
174  pointee(hasCanonicalType(objcObjectPointerType()))))))
175  .bind(ParamBind);
176 
177  auto ReferencedParamM =
178  declRefExpr(to(parmVarDecl(DoublePointerParamM))).bind(CapturedBind);
179 
180  // Write into a binded object, e.g. *ParamBind = X.
181  auto WritesIntoM = binaryOperator(
182  hasLHS(unaryOperator(
183  hasOperatorName("*"),
184  hasUnaryOperand(
185  ignoringParenImpCasts(ReferencedParamM))
186  )),
187  hasOperatorName("=")
188  ).bind(ProblematicWriteBind);
189 
190  auto ArgumentCaptureM = hasAnyArgument(
191  ignoringParenImpCasts(ReferencedParamM));
192  auto CapturedInParamM = stmt(anyOf(
193  callExpr(ArgumentCaptureM),
194  objcMessageExpr(ArgumentCaptureM)));
195 
196  // WritesIntoM happens inside a block passed as an argument.
197  auto WritesOrCapturesInBlockM = hasAnyArgument(allOf(
198  hasType(hasCanonicalType(blockPointerType())),
200  stmt(anyOf(WritesIntoM, CapturedInParamM))
201  )));
202 
203  auto BlockPassedToMarkedFuncM = stmt(anyOf(
204  callExpr(allOf(
205  callsNames(FunctionsWithAutoreleasingPool), WritesOrCapturesInBlockM)),
207  hasAnySelector(toRefs(SelectorsWithAutoreleasingPool)),
208  WritesOrCapturesInBlockM))
209  ));
210 
211  // WritesIntoM happens inside an explicit @autoreleasepool.
212  auto WritesOrCapturesInPoolM =
214  forEachDescendant(stmt(anyOf(WritesIntoM, CapturedInParamM))))
215  .bind(IsARPBind);
216 
217  auto HasParamAndWritesInMarkedFuncM =
218  allOf(hasAnyParameter(DoublePointerParamM),
219  anyOf(forEachDescendant(BlockPassedToMarkedFuncM),
220  forEachDescendant(WritesOrCapturesInPoolM)));
221 
222  auto MatcherM = decl(anyOf(
223  objcMethodDecl(HasParamAndWritesInMarkedFuncM).bind(IsMethodBind),
224  functionDecl(HasParamAndWritesInMarkedFuncM),
225  blockDecl(HasParamAndWritesInMarkedFuncM)));
226 
227  auto Matches = match(MatcherM, *D, AM.getASTContext());
228  for (BoundNodes Match : Matches)
229  emitDiagnostics(Match, D, BR, AM, this);
230 }
231 
232 void ento::registerAutoreleaseWriteChecker(CheckerManager &Mgr) {
233  Mgr.registerChecker<ObjCAutoreleaseWriteChecker>();
234 }
235 
236 bool ento::shouldRegisterAutoreleaseWriteChecker(const CheckerManager &mgr) {
237  return true;
238 }
static decltype(auto) callsNames(std::vector< std::string > FunctionNames)
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:89
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:1595
AnalysisDeclContext contains the context data for the function, method or block under analysis...
Definition: Format.h:2679
const internal::VariadicDynCastAllOfMatcher< Stmt, DeclRefExpr > declRefExpr
Matches expressions that refer to declarations.
const char *const MemoryRefCount
const T * getNodeAs(StringRef ID) const
Returns the AST node bound to ID.
Definition: ASTMatchers.h:114
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.
const internal::ArgumentAdaptingMatcherFunc< internal::ForEachDescendantMatcher > forEachDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher. ...
const internal::VariadicDynCastAllOfMatcher< Stmt, ObjCAutoreleasePoolStmt > autoreleasePoolStmt
Matches an Objective-C autorelease pool statement.
This represents one expression.
Definition: Expr.h:110
#define V(N, I)
Definition: ASTContext.h:2899
const internal::VariadicAllOfMatcher< Decl > decl
Matches declarations.
Maps string IDs to AST nodes matched by parts of a matcher.
Definition: ASTMatchers.h:107
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:414
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:178
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.
Represents Objective-C&#39;s @autoreleasepool Statement.
Definition: StmtObjC.h:368
const AstTypeMatcher< BlockPointerType > blockPointerType
Matches block pointer types, i.e.