1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
26 | #include "clang/AST/Decl.h" |
27 | #include "clang/AST/DeclObjC.h" |
28 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
29 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
30 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
31 | #include "clang/StaticAnalyzer/Core/Checker.h" |
32 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
33 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
34 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
35 | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
36 | |
37 | using namespace clang; |
38 | using namespace ento; |
39 | using namespace ast_matchers; |
40 | |
41 | namespace { |
42 | |
43 | const char * RunLoopBind = "NSRunLoopM"; |
44 | const char * RunLoopRunBind = "RunLoopRunM"; |
45 | const char * OtherMsgBind = "OtherMessageSentM"; |
46 | const char * AutoreleasePoolBind = "AutoreleasePoolM"; |
47 | const char * OtherStmtAutoreleasePoolBind = "OtherAutoreleasePoolM"; |
48 | |
49 | class RunLoopAutoreleaseLeakChecker : public Checker<check::ASTCodeBody> { |
50 | |
51 | public: |
52 | void checkASTCodeBody(const Decl *D, |
53 | AnalysisManager &AM, |
54 | BugReporter &BR) const; |
55 | |
56 | }; |
57 | |
58 | } |
59 | |
60 | |
61 | |
62 | |
63 | |
64 | |
65 | bool seenBefore(const Stmt *Parent, const Stmt *A, const Stmt *B) { |
66 | for (const Stmt *C : Parent->children()) { |
67 | if (!C) continue; |
68 | |
69 | if (C == A) |
70 | return true; |
71 | |
72 | if (C == B) |
73 | return false; |
74 | |
75 | return seenBefore(C, A, B); |
76 | } |
77 | return false; |
78 | } |
79 | |
80 | static void emitDiagnostics(BoundNodes &Match, |
81 | const Decl *D, |
82 | BugReporter &BR, |
83 | AnalysisManager &AM, |
84 | const RunLoopAutoreleaseLeakChecker *Checker) { |
85 | |
86 | hasBody()", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp", 86, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(D->hasBody()); |
87 | const Stmt *DeclBody = D->getBody(); |
88 | |
89 | AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D); |
90 | |
91 | const auto *ME = Match.getNodeAs<ObjCMessageExpr>(OtherMsgBind); |
92 | assert(ME); |
93 | |
94 | const auto *AP = |
95 | Match.getNodeAs<ObjCAutoreleasePoolStmt>(AutoreleasePoolBind); |
96 | const auto *OAP = |
97 | Match.getNodeAs<ObjCAutoreleasePoolStmt>(OtherStmtAutoreleasePoolBind); |
98 | bool HasAutoreleasePool = (AP != nullptr); |
99 | |
100 | const auto *RL = Match.getNodeAs<ObjCMessageExpr>(RunLoopBind); |
101 | const auto *RLR = Match.getNodeAs<Stmt>(RunLoopRunBind); |
102 | (0) . __assert_fail ("RLR && \"Run loop launch not found\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp", 102, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(RLR && "Run loop launch not found"); |
103 | assert(ME != RLR); |
104 | |
105 | |
106 | if (seenBefore(DeclBody, RLR, ME)) |
107 | return; |
108 | |
109 | if (HasAutoreleasePool && (OAP != AP)) |
110 | return; |
111 | |
112 | PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin( |
113 | ME, BR.getSourceManager(), ADC); |
114 | SourceRange Range = ME->getSourceRange(); |
115 | |
116 | BR.EmitBasicReport(ADC->getDecl(), Checker, |
117 | , |
118 | , |
119 | |
120 | (Twine("Temporary objects allocated in the") + |
121 | " autorelease pool " + |
122 | (HasAutoreleasePool ? "" : "of last resort ") + |
123 | "followed by the launch of " + |
124 | (RL ? "main run loop " : "xpc_main ") + |
125 | "may never get released; consider moving them to a " |
126 | "separate autorelease pool") |
127 | .str(), |
128 | Location, Range); |
129 | } |
130 | |
131 | static StatementMatcher getRunLoopRunM(StatementMatcher = anything()) { |
132 | StatementMatcher MainRunLoopM = |
133 | objcMessageExpr(hasSelector("mainRunLoop"), |
134 | hasReceiverType(asString("NSRunLoop")), |
135 | Extra) |
136 | .bind(RunLoopBind); |
137 | |
138 | StatementMatcher MainRunLoopRunM = objcMessageExpr(hasSelector("run"), |
139 | hasReceiver(MainRunLoopM), |
140 | Extra).bind(RunLoopRunBind); |
141 | |
142 | StatementMatcher XPCRunM = |
143 | callExpr(callee(functionDecl(hasName("xpc_main")))).bind(RunLoopRunBind); |
144 | return anyOf(MainRunLoopRunM, XPCRunM); |
145 | } |
146 | |
147 | static StatementMatcher getOtherMessageSentM(StatementMatcher = anything()) { |
148 | return objcMessageExpr(unless(anyOf(equalsBoundNode(RunLoopBind), |
149 | equalsBoundNode(RunLoopRunBind))), |
150 | Extra) |
151 | .bind(OtherMsgBind); |
152 | } |
153 | |
154 | static void |
155 | checkTempObjectsInSamePool(const Decl *D, AnalysisManager &AM, BugReporter &BR, |
156 | const RunLoopAutoreleaseLeakChecker *Chkr) { |
157 | StatementMatcher RunLoopRunM = getRunLoopRunM(); |
158 | StatementMatcher OtherMessageSentM = getOtherMessageSentM( |
159 | hasAncestor(autoreleasePoolStmt().bind(OtherStmtAutoreleasePoolBind))); |
160 | |
161 | StatementMatcher RunLoopInAutorelease = |
162 | autoreleasePoolStmt( |
163 | hasDescendant(RunLoopRunM), |
164 | hasDescendant(OtherMessageSentM)).bind(AutoreleasePoolBind); |
165 | |
166 | DeclarationMatcher GroupM = decl(hasDescendant(RunLoopInAutorelease)); |
167 | |
168 | auto Matches = match(GroupM, *D, AM.getASTContext()); |
169 | for (BoundNodes Match : Matches) |
170 | emitDiagnostics(Match, D, BR, AM, Chkr); |
171 | } |
172 | |
173 | static void |
174 | checkTempObjectsInNoPool(const Decl *D, AnalysisManager &AM, BugReporter &BR, |
175 | const RunLoopAutoreleaseLeakChecker *Chkr) { |
176 | |
177 | auto NoPoolM = unless(hasAncestor(autoreleasePoolStmt())); |
178 | |
179 | StatementMatcher RunLoopRunM = getRunLoopRunM(NoPoolM); |
180 | StatementMatcher OtherMessageSentM = getOtherMessageSentM(NoPoolM); |
181 | |
182 | DeclarationMatcher GroupM = functionDecl( |
183 | isMain(), |
184 | hasDescendant(RunLoopRunM), |
185 | hasDescendant(OtherMessageSentM) |
186 | ); |
187 | |
188 | auto Matches = match(GroupM, *D, AM.getASTContext()); |
189 | |
190 | for (BoundNodes Match : Matches) |
191 | emitDiagnostics(Match, D, BR, AM, Chkr); |
192 | |
193 | } |
194 | |
195 | void RunLoopAutoreleaseLeakChecker::checkASTCodeBody(const Decl *D, |
196 | AnalysisManager &AM, |
197 | BugReporter &BR) const { |
198 | checkTempObjectsInSamePool(D, AM, BR, this); |
199 | checkTempObjectsInNoPool(D, AM, BR, this); |
200 | } |
201 | |
202 | void ento::registerRunLoopAutoreleaseLeakChecker(CheckerManager &mgr) { |
203 | mgr.registerChecker<RunLoopAutoreleaseLeakChecker>(); |
204 | } |
205 | |
206 | bool ento::shouldRegisterRunLoopAutoreleaseLeakChecker(const LangOptions &LO) { |
207 | return true; |
208 | } |
209 | |