]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
Merge clang trunk r351319, resolve conflicts, and update FREEBSD-Xlist.
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / ObjCAutoreleaseWriteChecker.cpp
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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
31 #include "clang/ASTMatchers/ASTMatchFinder.h"
32 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
33 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
34 #include "clang/StaticAnalyzer/Core/Checker.h"
35 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.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) {
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   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())),
181       forEachDescendant(
182         stmt(anyOf(WritesIntoM, CapturedInParamM))
183       )));
184
185   auto BlockPassedToMarkedFuncM = stmt(anyOf(
186     callExpr(allOf(
187       callsNames(FunctionsWithAutoreleasingPool), WritesOrCapturesInBlockM)),
188     objcMessageExpr(allOf(
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 }