/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ----------===// |
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 a meta-engine for path-sensitive dataflow analysis that |
10 | | // is built on CoreEngine, but provides the boilerplate to execute transfer |
11 | | // functions and build the ExplodedGraph at the expression level. |
12 | | // |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
16 | | #include "PrettyStackTraceLocationContext.h" |
17 | | #include "clang/AST/ASTContext.h" |
18 | | #include "clang/AST/Decl.h" |
19 | | #include "clang/AST/DeclBase.h" |
20 | | #include "clang/AST/DeclCXX.h" |
21 | | #include "clang/AST/DeclObjC.h" |
22 | | #include "clang/AST/Expr.h" |
23 | | #include "clang/AST/ExprCXX.h" |
24 | | #include "clang/AST/ExprObjC.h" |
25 | | #include "clang/AST/ParentMap.h" |
26 | | #include "clang/AST/PrettyPrinter.h" |
27 | | #include "clang/AST/Stmt.h" |
28 | | #include "clang/AST/StmtCXX.h" |
29 | | #include "clang/AST/StmtObjC.h" |
30 | | #include "clang/AST/Type.h" |
31 | | #include "clang/Analysis/AnalysisDeclContext.h" |
32 | | #include "clang/Analysis/CFG.h" |
33 | | #include "clang/Analysis/ConstructionContext.h" |
34 | | #include "clang/Analysis/ProgramPoint.h" |
35 | | #include "clang/Basic/IdentifierTable.h" |
36 | | #include "clang/Basic/JsonSupport.h" |
37 | | #include "clang/Basic/LLVM.h" |
38 | | #include "clang/Basic/LangOptions.h" |
39 | | #include "clang/Basic/PrettyStackTrace.h" |
40 | | #include "clang/Basic/SourceLocation.h" |
41 | | #include "clang/Basic/SourceManager.h" |
42 | | #include "clang/Basic/Specifiers.h" |
43 | | #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" |
44 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
45 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
46 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
47 | | #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" |
48 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
49 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" |
50 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" |
51 | | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" |
52 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" |
53 | | #include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h" |
54 | | #include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" |
55 | | #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" |
56 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" |
57 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" |
58 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" |
59 | | #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" |
60 | | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
61 | | #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" |
62 | | #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" |
63 | | #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" |
64 | | #include "llvm/ADT/APSInt.h" |
65 | | #include "llvm/ADT/DenseMap.h" |
66 | | #include "llvm/ADT/ImmutableMap.h" |
67 | | #include "llvm/ADT/ImmutableSet.h" |
68 | | #include "llvm/ADT/STLExtras.h" |
69 | | #include "llvm/ADT/SmallVector.h" |
70 | | #include "llvm/ADT/Statistic.h" |
71 | | #include "llvm/Support/Casting.h" |
72 | | #include "llvm/Support/Compiler.h" |
73 | | #include "llvm/Support/DOTGraphTraits.h" |
74 | | #include "llvm/Support/ErrorHandling.h" |
75 | | #include "llvm/Support/GraphWriter.h" |
76 | | #include "llvm/Support/SaveAndRestore.h" |
77 | | #include "llvm/Support/raw_ostream.h" |
78 | | #include <cassert> |
79 | | #include <cstdint> |
80 | | #include <memory> |
81 | | #include <optional> |
82 | | #include <string> |
83 | | #include <tuple> |
84 | | #include <utility> |
85 | | #include <vector> |
86 | | |
87 | | using namespace clang; |
88 | | using namespace ento; |
89 | | |
90 | | #define DEBUG_TYPE "ExprEngine" |
91 | | |
92 | | STATISTIC(NumRemoveDeadBindings, |
93 | | "The # of times RemoveDeadBindings is called"); |
94 | | STATISTIC(NumMaxBlockCountReached, |
95 | | "The # of aborted paths due to reaching the maximum block count in " |
96 | | "a top level function"); |
97 | | STATISTIC(NumMaxBlockCountReachedInInlined, |
98 | | "The # of aborted paths due to reaching the maximum block count in " |
99 | | "an inlined function"); |
100 | | STATISTIC(NumTimesRetriedWithoutInlining, |
101 | | "The # of times we re-evaluated a call without inlining"); |
102 | | |
103 | | //===----------------------------------------------------------------------===// |
104 | | // Internal program state traits. |
105 | | //===----------------------------------------------------------------------===// |
106 | | |
107 | | namespace { |
108 | | |
109 | | // When modeling a C++ constructor, for a variety of reasons we need to track |
110 | | // the location of the object for the duration of its ConstructionContext. |
111 | | // ObjectsUnderConstruction maps statements within the construction context |
112 | | // to the object's location, so that on every such statement the location |
113 | | // could have been retrieved. |
114 | | |
115 | | /// ConstructedObjectKey is used for being able to find the path-sensitive |
116 | | /// memory region of a freshly constructed object while modeling the AST node |
117 | | /// that syntactically represents the object that is being constructed. |
118 | | /// Semantics of such nodes may sometimes require access to the region that's |
119 | | /// not otherwise present in the program state, or to the very fact that |
120 | | /// the construction context was present and contained references to these |
121 | | /// AST nodes. |
122 | | class ConstructedObjectKey { |
123 | | using ConstructedObjectKeyImpl = |
124 | | std::pair<ConstructionContextItem, const LocationContext *>; |
125 | | const ConstructedObjectKeyImpl Impl; |
126 | | |
127 | | public: |
128 | | explicit ConstructedObjectKey(const ConstructionContextItem &Item, |
129 | | const LocationContext *LC) |
130 | 332k | : Impl(Item, LC) {} |
131 | | |
132 | 922 | const ConstructionContextItem &getItem() const { return Impl.first; } |
133 | 63.2k | const LocationContext *getLocationContext() const { return Impl.second; } |
134 | | |
135 | 40 | ASTContext &getASTContext() const { |
136 | 40 | return getLocationContext()->getDecl()->getASTContext(); |
137 | 40 | } |
138 | | |
139 | | void printJson(llvm::raw_ostream &Out, PrinterHelper *Helper, |
140 | 40 | PrintingPolicy &PP) const { |
141 | 40 | const Stmt *S = getItem().getStmtOrNull(); |
142 | 40 | const CXXCtorInitializer *I = nullptr; |
143 | 40 | if (!S) |
144 | 5 | I = getItem().getCXXCtorInitializer(); |
145 | | |
146 | 40 | if (S) |
147 | 35 | Out << "\"stmt_id\": " << S->getID(getASTContext()); |
148 | 5 | else |
149 | 5 | Out << "\"init_id\": " << I->getID(getASTContext()); |
150 | | |
151 | | // Kind |
152 | 40 | Out << ", \"kind\": \"" << getItem().getKindAsString() |
153 | 40 | << "\", \"argument_index\": "; |
154 | | |
155 | 40 | if (getItem().getKind() == ConstructionContextItem::ArgumentKind) |
156 | 0 | Out << getItem().getIndex(); |
157 | 40 | else |
158 | 40 | Out << "null"; |
159 | | |
160 | | // Pretty-print |
161 | 40 | Out << ", \"pretty\": "; |
162 | | |
163 | 40 | if (S) { |
164 | 35 | S->printJson(Out, Helper, PP, /*AddQuotes=*/true); |
165 | 35 | } else { |
166 | 5 | Out << '\"' << I->getAnyMember()->getDeclName() << '\"'; |
167 | 5 | } |
168 | 40 | } |
169 | | |
170 | 96.8k | void Profile(llvm::FoldingSetNodeID &ID) const { |
171 | 96.8k | ID.Add(Impl.first); |
172 | 96.8k | ID.AddPointer(Impl.second); |
173 | 96.8k | } |
174 | | |
175 | 383k | bool operator==(const ConstructedObjectKey &RHS) const { |
176 | 383k | return Impl == RHS.Impl; |
177 | 383k | } |
178 | | |
179 | 206k | bool operator<(const ConstructedObjectKey &RHS) const { |
180 | 206k | return Impl < RHS.Impl; |
181 | 206k | } |
182 | | }; |
183 | | } // namespace |
184 | | |
185 | | typedef llvm::ImmutableMap<ConstructedObjectKey, SVal> |
186 | | ObjectsUnderConstructionMap; |
187 | | REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction, |
188 | | ObjectsUnderConstructionMap) |
189 | | |
190 | | // This trait is responsible for storing the index of the element that is to be |
191 | | // constructed in the next iteration. As a result a CXXConstructExpr is only |
192 | | // stored if it is array type. Also the index is the index of the continuous |
193 | | // memory region, which is important for multi-dimensional arrays. E.g:: int |
194 | | // arr[2][2]; assume arr[1][1] will be the next element under construction, so |
195 | | // the index is 3. |
196 | | typedef llvm::ImmutableMap< |
197 | | std::pair<const CXXConstructExpr *, const LocationContext *>, unsigned> |
198 | | IndexOfElementToConstructMap; |
199 | | REGISTER_TRAIT_WITH_PROGRAMSTATE(IndexOfElementToConstruct, |
200 | | IndexOfElementToConstructMap) |
201 | | |
202 | | // This trait is responsible for holding our pending ArrayInitLoopExprs. |
203 | | // It pairs the LocationContext and the initializer CXXConstructExpr with |
204 | | // the size of the array that's being copy initialized. |
205 | | typedef llvm::ImmutableMap< |
206 | | std::pair<const CXXConstructExpr *, const LocationContext *>, unsigned> |
207 | | PendingInitLoopMap; |
208 | | REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingInitLoop, PendingInitLoopMap) |
209 | | |
210 | | typedef llvm::ImmutableMap<const LocationContext *, unsigned> |
211 | | PendingArrayDestructionMap; |
212 | | REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingArrayDestruction, |
213 | | PendingArrayDestructionMap) |
214 | | |
215 | | //===----------------------------------------------------------------------===// |
216 | | // Engine construction and deletion. |
217 | | //===----------------------------------------------------------------------===// |
218 | | |
219 | | static const char* TagProviderName = "ExprEngine"; |
220 | | |
221 | | ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, |
222 | | AnalysisManager &mgr, SetOfConstDecls *VisitedCalleesIn, |
223 | | FunctionSummariesTy *FS, InliningModes HowToInlineIn) |
224 | | : CTU(CTU), IsCTUEnabled(mgr.getAnalyzerOptions().IsNaiveCTUEnabled), |
225 | | AMgr(mgr), AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), |
226 | | Engine(*this, FS, mgr.getAnalyzerOptions()), G(Engine.getGraph()), |
227 | | StateMgr(getContext(), mgr.getStoreManagerCreator(), |
228 | | mgr.getConstraintManagerCreator(), G.getAllocator(), this), |
229 | | SymMgr(StateMgr.getSymbolManager()), MRMgr(StateMgr.getRegionManager()), |
230 | | svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()), |
231 | | BR(mgr, *this), VisitedCallees(VisitedCalleesIn), |
232 | 16.1k | HowToInline(HowToInlineIn) { |
233 | 16.1k | unsigned TrimInterval = mgr.options.GraphTrimInterval; |
234 | 16.1k | if (TrimInterval != 0) { |
235 | | // Enable eager node reclamation when constructing the ExplodedGraph. |
236 | 16.1k | G.enableNodeReclamation(TrimInterval); |
237 | 16.1k | } |
238 | 16.1k | } |
239 | | |
240 | | //===----------------------------------------------------------------------===// |
241 | | // Utility methods. |
242 | | //===----------------------------------------------------------------------===// |
243 | | |
244 | 16.1k | ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { |
245 | 16.1k | ProgramStateRef state = StateMgr.getInitialState(InitLoc); |
246 | 16.1k | const Decl *D = InitLoc->getDecl(); |
247 | | |
248 | | // Preconditions. |
249 | | // FIXME: It would be nice if we had a more general mechanism to add |
250 | | // such preconditions. Some day. |
251 | 16.1k | do { |
252 | 16.1k | if (const auto *FD = dyn_cast<FunctionDecl>(D)) { |
253 | | // Precondition: the first argument of 'main' is an integer guaranteed |
254 | | // to be > 0. |
255 | 14.9k | const IdentifierInfo *II = FD->getIdentifier(); |
256 | 14.9k | if (!II || !(13.8k II->getName() == "main"13.8k && FD->getNumParams() > 067 )) |
257 | 14.9k | break; |
258 | | |
259 | 10 | const ParmVarDecl *PD = FD->getParamDecl(0); |
260 | 10 | QualType T = PD->getType(); |
261 | 10 | const auto *BT = dyn_cast<BuiltinType>(T); |
262 | 10 | if (!BT || !BT->isInteger()) |
263 | 0 | break; |
264 | | |
265 | 10 | const MemRegion *R = state->getRegion(PD, InitLoc); |
266 | 10 | if (!R) |
267 | 0 | break; |
268 | | |
269 | 10 | SVal V = state->getSVal(loc::MemRegionVal(R)); |
270 | 10 | SVal Constraint_untested = evalBinOp(state, BO_GT, V, |
271 | 10 | svalBuilder.makeZeroVal(T), |
272 | 10 | svalBuilder.getConditionType()); |
273 | | |
274 | 10 | std::optional<DefinedOrUnknownSVal> Constraint = |
275 | 10 | Constraint_untested.getAs<DefinedOrUnknownSVal>(); |
276 | | |
277 | 10 | if (!Constraint) |
278 | 0 | break; |
279 | | |
280 | 10 | if (ProgramStateRef newState = state->assume(*Constraint, true)) |
281 | 10 | state = newState; |
282 | 10 | } |
283 | 1.27k | break; |
284 | 16.1k | } |
285 | 16.1k | while (false0 ); |
286 | | |
287 | 16.1k | if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { |
288 | | // Precondition: 'self' is always non-null upon entry to an Objective-C |
289 | | // method. |
290 | 1.03k | const ImplicitParamDecl *SelfD = MD->getSelfDecl(); |
291 | 1.03k | const MemRegion *R = state->getRegion(SelfD, InitLoc); |
292 | 1.03k | SVal V = state->getSVal(loc::MemRegionVal(R)); |
293 | | |
294 | 1.03k | if (std::optional<Loc> LV = V.getAs<Loc>()) { |
295 | | // Assume that the pointer value in 'self' is non-null. |
296 | 1.03k | state = state->assume(*LV, true); |
297 | 1.03k | assert(state && "'self' cannot be null"); |
298 | 1.03k | } |
299 | 1.03k | } |
300 | | |
301 | 16.1k | if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { |
302 | 1.32k | if (!MD->isStatic()) { |
303 | | // Precondition: 'this' is always non-null upon entry to the |
304 | | // top-level function. This is our starting assumption for |
305 | | // analyzing an "open" program. |
306 | 1.29k | const StackFrameContext *SFC = InitLoc->getStackFrame(); |
307 | 1.29k | if (SFC->getParent() == nullptr) { |
308 | 1.29k | loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC); |
309 | 1.29k | SVal V = state->getSVal(L); |
310 | 1.29k | if (std::optional<Loc> LV = V.getAs<Loc>()) { |
311 | 1.29k | state = state->assume(*LV, true); |
312 | 1.29k | assert(state && "'this' cannot be null"); |
313 | 1.29k | } |
314 | 1.29k | } |
315 | 1.29k | } |
316 | 1.32k | } |
317 | | |
318 | 16.1k | return state; |
319 | 16.1k | } |
320 | | |
321 | | ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded( |
322 | | ProgramStateRef State, const LocationContext *LC, |
323 | | const Expr *InitWithAdjustments, const Expr *Result, |
324 | 63.8k | const SubRegion **OutRegionWithAdjustments) { |
325 | | // FIXME: This function is a hack that works around the quirky AST |
326 | | // we're often having with respect to C++ temporaries. If only we modelled |
327 | | // the actual execution order of statements properly in the CFG, |
328 | | // all the hassle with adjustments would not be necessary, |
329 | | // and perhaps the whole function would be removed. |
330 | 63.8k | SVal InitValWithAdjustments = State->getSVal(InitWithAdjustments, LC); |
331 | 63.8k | if (!Result) { |
332 | | // If we don't have an explicit result expression, we're in "if needed" |
333 | | // mode. Only create a region if the current value is a NonLoc. |
334 | 55.0k | if (!isa<NonLoc>(InitValWithAdjustments)) { |
335 | 54.9k | if (OutRegionWithAdjustments) |
336 | 42.8k | *OutRegionWithAdjustments = nullptr; |
337 | 54.9k | return State; |
338 | 54.9k | } |
339 | 48 | Result = InitWithAdjustments; |
340 | 8.81k | } else { |
341 | | // We need to create a region no matter what. Make sure we don't try to |
342 | | // stuff a Loc into a non-pointer temporary region. |
343 | 8.81k | assert(!isa<Loc>(InitValWithAdjustments) || |
344 | 8.81k | Loc::isLocType(Result->getType()) || |
345 | 8.81k | Result->getType()->isMemberPointerType()); |
346 | 8.81k | } |
347 | | |
348 | 8.86k | ProgramStateManager &StateMgr = State->getStateManager(); |
349 | 8.86k | MemRegionManager &MRMgr = StateMgr.getRegionManager(); |
350 | 8.86k | StoreManager &StoreMgr = StateMgr.getStoreManager(); |
351 | | |
352 | | // MaterializeTemporaryExpr may appear out of place, after a few field and |
353 | | // base-class accesses have been made to the object, even though semantically |
354 | | // it is the whole object that gets materialized and lifetime-extended. |
355 | | // |
356 | | // For example: |
357 | | // |
358 | | // `-MaterializeTemporaryExpr |
359 | | // `-MemberExpr |
360 | | // `-CXXTemporaryObjectExpr |
361 | | // |
362 | | // instead of the more natural |
363 | | // |
364 | | // `-MemberExpr |
365 | | // `-MaterializeTemporaryExpr |
366 | | // `-CXXTemporaryObjectExpr |
367 | | // |
368 | | // Use the usual methods for obtaining the expression of the base object, |
369 | | // and record the adjustments that we need to make to obtain the sub-object |
370 | | // that the whole expression 'Ex' refers to. This trick is usual, |
371 | | // in the sense that CodeGen takes a similar route. |
372 | | |
373 | 8.86k | SmallVector<const Expr *, 2> CommaLHSs; |
374 | 8.86k | SmallVector<SubobjectAdjustment, 2> Adjustments; |
375 | | |
376 | 8.86k | const Expr *Init = InitWithAdjustments->skipRValueSubobjectAdjustments( |
377 | 8.86k | CommaLHSs, Adjustments); |
378 | | |
379 | | // Take the region for Init, i.e. for the whole object. If we do not remember |
380 | | // the region in which the object originally was constructed, come up with |
381 | | // a new temporary region out of thin air and copy the contents of the object |
382 | | // (which are currently present in the Environment, because Init is an rvalue) |
383 | | // into that region. This is not correct, but it is better than nothing. |
384 | 8.86k | const TypedValueRegion *TR = nullptr; |
385 | 8.86k | if (const auto *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) { |
386 | 8.75k | if (std::optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) { |
387 | 8.08k | State = finishObjectConstruction(State, MT, LC); |
388 | 8.08k | State = State->BindExpr(Result, LC, *V); |
389 | 8.08k | return State; |
390 | 8.08k | } else if (const ValueDecl *663 VD663 = MT->getExtendingDecl()) { |
391 | 157 | StorageDuration SD = MT->getStorageDuration(); |
392 | 157 | assert(SD != SD_FullExpression); |
393 | | // If this object is bound to a reference with static storage duration, we |
394 | | // put it in a different region to prevent "address leakage" warnings. |
395 | 157 | if (SD == SD_Static || SD == SD_Thread156 ) { |
396 | 1 | TR = MRMgr.getCXXStaticLifetimeExtendedObjectRegion(Init, VD); |
397 | 156 | } else { |
398 | 156 | TR = MRMgr.getCXXLifetimeExtendedObjectRegion(Init, VD, LC); |
399 | 156 | } |
400 | 506 | } else { |
401 | 506 | assert(MT->getStorageDuration() == SD_FullExpression); |
402 | 506 | TR = MRMgr.getCXXTempObjectRegion(Init, LC); |
403 | 506 | } |
404 | 8.75k | } else { |
405 | 112 | TR = MRMgr.getCXXTempObjectRegion(Init, LC); |
406 | 112 | } |
407 | | |
408 | 775 | SVal Reg = loc::MemRegionVal(TR); |
409 | 775 | SVal BaseReg = Reg; |
410 | | |
411 | | // Make the necessary adjustments to obtain the sub-object. |
412 | 775 | for (const SubobjectAdjustment &Adj : llvm::reverse(Adjustments)) { |
413 | 5 | switch (Adj.Kind) { |
414 | 1 | case SubobjectAdjustment::DerivedToBaseAdjustment: |
415 | 1 | Reg = StoreMgr.evalDerivedToBase(Reg, Adj.DerivedToBase.BasePath); |
416 | 1 | break; |
417 | 4 | case SubobjectAdjustment::FieldAdjustment: |
418 | 4 | Reg = StoreMgr.getLValueField(Adj.Field, Reg); |
419 | 4 | break; |
420 | 0 | case SubobjectAdjustment::MemberPointerAdjustment: |
421 | | // FIXME: Unimplemented. |
422 | 0 | State = State->invalidateRegions(Reg, InitWithAdjustments, |
423 | 0 | currBldrCtx->blockCount(), LC, true, |
424 | 0 | nullptr, nullptr, nullptr); |
425 | 0 | return State; |
426 | 5 | } |
427 | 5 | } |
428 | | |
429 | | // What remains is to copy the value of the object to the new region. |
430 | | // FIXME: In other words, what we should always do is copy value of the |
431 | | // Init expression (which corresponds to the bigger object) to the whole |
432 | | // temporary region TR. However, this value is often no longer present |
433 | | // in the Environment. If it has disappeared, we instead invalidate TR. |
434 | | // Still, what we can do is assign the value of expression Ex (which |
435 | | // corresponds to the sub-object) to the TR's sub-region Reg. At least, |
436 | | // values inside Reg would be correct. |
437 | 775 | SVal InitVal = State->getSVal(Init, LC); |
438 | 775 | if (InitVal.isUnknown()) { |
439 | 149 | InitVal = getSValBuilder().conjureSymbolVal(Result, LC, Init->getType(), |
440 | 149 | currBldrCtx->blockCount()); |
441 | 149 | State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false); |
442 | | |
443 | | // Then we'd need to take the value that certainly exists and bind it |
444 | | // over. |
445 | 149 | if (InitValWithAdjustments.isUnknown()) { |
446 | | // Try to recover some path sensitivity in case we couldn't |
447 | | // compute the value. |
448 | 149 | InitValWithAdjustments = getSValBuilder().conjureSymbolVal( |
449 | 149 | Result, LC, InitWithAdjustments->getType(), |
450 | 149 | currBldrCtx->blockCount()); |
451 | 149 | } |
452 | 149 | State = |
453 | 149 | State->bindLoc(Reg.castAs<Loc>(), InitValWithAdjustments, LC, false); |
454 | 626 | } else { |
455 | 626 | State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false); |
456 | 626 | } |
457 | | |
458 | | // The result expression would now point to the correct sub-region of the |
459 | | // newly created temporary region. Do this last in order to getSVal of Init |
460 | | // correctly in case (Result == Init). |
461 | 775 | if (Result->isGLValue()) { |
462 | 727 | State = State->BindExpr(Result, LC, Reg); |
463 | 727 | } else { |
464 | 48 | State = State->BindExpr(Result, LC, InitValWithAdjustments); |
465 | 48 | } |
466 | | |
467 | | // Notify checkers once for two bindLoc()s. |
468 | 775 | State = processRegionChange(State, TR, LC); |
469 | | |
470 | 775 | if (OutRegionWithAdjustments) |
471 | 48 | *OutRegionWithAdjustments = cast<SubRegion>(Reg.getAsRegion()); |
472 | 775 | return State; |
473 | 775 | } |
474 | | |
475 | | ProgramStateRef ExprEngine::setIndexOfElementToConstruct( |
476 | | ProgramStateRef State, const CXXConstructExpr *E, |
477 | 634 | const LocationContext *LCtx, unsigned Idx) { |
478 | 634 | auto Key = std::make_pair(E, LCtx->getStackFrame()); |
479 | | |
480 | 634 | assert(!State->contains<IndexOfElementToConstruct>(Key) || Idx > 0); |
481 | | |
482 | 634 | return State->set<IndexOfElementToConstruct>(Key, Idx); |
483 | 634 | } |
484 | | |
485 | | std::optional<unsigned> |
486 | | ExprEngine::getPendingInitLoop(ProgramStateRef State, const CXXConstructExpr *E, |
487 | 19.6k | const LocationContext *LCtx) { |
488 | 19.6k | const unsigned *V = State->get<PendingInitLoop>({E, LCtx->getStackFrame()}); |
489 | 19.6k | return V ? std::make_optional(*V)306 : std::nullopt19.3k ; |
490 | 19.6k | } |
491 | | |
492 | | ProgramStateRef ExprEngine::removePendingInitLoop(ProgramStateRef State, |
493 | | const CXXConstructExpr *E, |
494 | 37 | const LocationContext *LCtx) { |
495 | 37 | auto Key = std::make_pair(E, LCtx->getStackFrame()); |
496 | | |
497 | 37 | assert(E && State->contains<PendingInitLoop>(Key)); |
498 | 37 | return State->remove<PendingInitLoop>(Key); |
499 | 37 | } |
500 | | |
501 | | ProgramStateRef ExprEngine::setPendingInitLoop(ProgramStateRef State, |
502 | | const CXXConstructExpr *E, |
503 | | const LocationContext *LCtx, |
504 | 39 | unsigned Size) { |
505 | 39 | auto Key = std::make_pair(E, LCtx->getStackFrame()); |
506 | | |
507 | 39 | assert(!State->contains<PendingInitLoop>(Key) && Size > 0); |
508 | | |
509 | 39 | return State->set<PendingInitLoop>(Key, Size); |
510 | 39 | } |
511 | | |
512 | | std::optional<unsigned> |
513 | | ExprEngine::getIndexOfElementToConstruct(ProgramStateRef State, |
514 | | const CXXConstructExpr *E, |
515 | 11.7k | const LocationContext *LCtx) { |
516 | 11.7k | const unsigned *V = |
517 | 11.7k | State->get<IndexOfElementToConstruct>({E, LCtx->getStackFrame()}); |
518 | 11.7k | return V ? std::make_optional(*V)1.21k : std::nullopt10.5k ; |
519 | 11.7k | } |
520 | | |
521 | | ProgramStateRef |
522 | | ExprEngine::removeIndexOfElementToConstruct(ProgramStateRef State, |
523 | | const CXXConstructExpr *E, |
524 | 279 | const LocationContext *LCtx) { |
525 | 279 | auto Key = std::make_pair(E, LCtx->getStackFrame()); |
526 | | |
527 | 279 | assert(E && State->contains<IndexOfElementToConstruct>(Key)); |
528 | 279 | return State->remove<IndexOfElementToConstruct>(Key); |
529 | 279 | } |
530 | | |
531 | | std::optional<unsigned> |
532 | | ExprEngine::getPendingArrayDestruction(ProgramStateRef State, |
533 | 78.5k | const LocationContext *LCtx) { |
534 | 78.5k | assert(LCtx && "LocationContext shouldn't be null!"); |
535 | | |
536 | 78.5k | const unsigned *V = |
537 | 78.5k | State->get<PendingArrayDestruction>(LCtx->getStackFrame()); |
538 | 78.5k | return V ? std::make_optional(*V)353 : std::nullopt78.1k ; |
539 | 78.5k | } |
540 | | |
541 | | ProgramStateRef ExprEngine::setPendingArrayDestruction( |
542 | 184 | ProgramStateRef State, const LocationContext *LCtx, unsigned Idx) { |
543 | 184 | assert(LCtx && "LocationContext shouldn't be null!"); |
544 | | |
545 | 184 | auto Key = LCtx->getStackFrame(); |
546 | | |
547 | 184 | return State->set<PendingArrayDestruction>(Key, Idx); |
548 | 184 | } |
549 | | |
550 | | ProgramStateRef |
551 | | ExprEngine::removePendingArrayDestruction(ProgramStateRef State, |
552 | 54 | const LocationContext *LCtx) { |
553 | 54 | assert(LCtx && "LocationContext shouldn't be null!"); |
554 | | |
555 | 54 | auto Key = LCtx->getStackFrame(); |
556 | | |
557 | 54 | assert(LCtx && State->contains<PendingArrayDestruction>(Key)); |
558 | 54 | return State->remove<PendingArrayDestruction>(Key); |
559 | 54 | } |
560 | | |
561 | | ProgramStateRef |
562 | | ExprEngine::addObjectUnderConstruction(ProgramStateRef State, |
563 | | const ConstructionContextItem &Item, |
564 | 37.9k | const LocationContext *LC, SVal V) { |
565 | 37.9k | ConstructedObjectKey Key(Item, LC->getStackFrame()); |
566 | | |
567 | 37.9k | const Expr *Init = nullptr; |
568 | | |
569 | 37.9k | if (auto DS = dyn_cast_or_null<DeclStmt>(Item.getStmtOrNull())) { |
570 | 7.76k | if (auto VD = dyn_cast_or_null<VarDecl>(DS->getSingleDecl())) |
571 | 7.76k | Init = VD->getInit(); |
572 | 7.76k | } |
573 | | |
574 | 37.9k | if (auto LE = dyn_cast_or_null<LambdaExpr>(Item.getStmtOrNull())) |
575 | 43 | Init = *(LE->capture_init_begin() + Item.getIndex()); |
576 | | |
577 | 37.9k | if (!Init && !Item.getStmtOrNull()30.1k ) |
578 | 2.82k | Init = Item.getCXXCtorInitializer()->getInit(); |
579 | | |
580 | | // In an ArrayInitLoopExpr the real initializer is returned by |
581 | | // getSubExpr(). Note that AILEs can be nested in case of |
582 | | // multidimesnional arrays. |
583 | 37.9k | if (const auto *AILE = dyn_cast_or_null<ArrayInitLoopExpr>(Init)) |
584 | 106 | Init = extractElementInitializerFromNestedAILE(AILE); |
585 | | |
586 | | // FIXME: Currently the state might already contain the marker due to |
587 | | // incorrect handling of temporaries bound to default parameters. |
588 | | // The state will already contain the marker if we construct elements |
589 | | // in an array, as we visit the same statement multiple times before |
590 | | // the array declaration. The marker is removed when we exit the |
591 | | // constructor call. |
592 | 37.9k | assert((!State->get<ObjectsUnderConstruction>(Key) || |
593 | 37.9k | Key.getItem().getKind() == |
594 | 37.9k | ConstructionContextItem::TemporaryDestructorKind || |
595 | 37.9k | State->contains<IndexOfElementToConstruct>( |
596 | 37.9k | {dyn_cast_or_null<CXXConstructExpr>(Init), LC})) && |
597 | 37.9k | "The object is already marked as `UnderConstruction`, when it's not " |
598 | 37.9k | "supposed to!"); |
599 | 37.9k | return State->set<ObjectsUnderConstruction>(Key, V); |
600 | 37.9k | } |
601 | | |
602 | | std::optional<SVal> |
603 | | ExprEngine::getObjectUnderConstruction(ProgramStateRef State, |
604 | | const ConstructionContextItem &Item, |
605 | 255k | const LocationContext *LC) { |
606 | 255k | ConstructedObjectKey Key(Item, LC->getStackFrame()); |
607 | 255k | const SVal *V = State->get<ObjectsUnderConstruction>(Key); |
608 | 255k | return V ? std::make_optional(*V)50.6k : std::nullopt204k ; |
609 | 255k | } |
610 | | |
611 | | ProgramStateRef |
612 | | ExprEngine::finishObjectConstruction(ProgramStateRef State, |
613 | | const ConstructionContextItem &Item, |
614 | 37.6k | const LocationContext *LC) { |
615 | 37.6k | ConstructedObjectKey Key(Item, LC->getStackFrame()); |
616 | 37.6k | assert(State->contains<ObjectsUnderConstruction>(Key)); |
617 | 37.6k | return State->remove<ObjectsUnderConstruction>(Key); |
618 | 37.6k | } |
619 | | |
620 | | ProgramStateRef ExprEngine::elideDestructor(ProgramStateRef State, |
621 | | const CXXBindTemporaryExpr *BTE, |
622 | 334 | const LocationContext *LC) { |
623 | 334 | ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC); |
624 | | // FIXME: Currently the state might already contain the marker due to |
625 | | // incorrect handling of temporaries bound to default parameters. |
626 | 334 | return State->set<ObjectsUnderConstruction>(Key, UnknownVal()); |
627 | 334 | } |
628 | | |
629 | | ProgramStateRef |
630 | | ExprEngine::cleanupElidedDestructor(ProgramStateRef State, |
631 | | const CXXBindTemporaryExpr *BTE, |
632 | 162 | const LocationContext *LC) { |
633 | 162 | ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC); |
634 | 162 | assert(State->contains<ObjectsUnderConstruction>(Key)); |
635 | 162 | return State->remove<ObjectsUnderConstruction>(Key); |
636 | 162 | } |
637 | | |
638 | | bool ExprEngine::isDestructorElided(ProgramStateRef State, |
639 | | const CXXBindTemporaryExpr *BTE, |
640 | 813 | const LocationContext *LC) { |
641 | 813 | ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC); |
642 | 813 | return State->contains<ObjectsUnderConstruction>(Key); |
643 | 813 | } |
644 | | |
645 | | bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State, |
646 | | const LocationContext *FromLC, |
647 | 67.8k | const LocationContext *ToLC) { |
648 | 67.8k | const LocationContext *LC = FromLC; |
649 | 135k | while (LC != ToLC) { |
650 | 67.8k | assert(LC && "ToLC must be a parent of FromLC!"); |
651 | 67.8k | for (auto I : State->get<ObjectsUnderConstruction>()) |
652 | 31.3k | if (I.first.getLocationContext() == LC) |
653 | 0 | return false; |
654 | | |
655 | 67.8k | LC = LC->getParent(); |
656 | 67.8k | } |
657 | 67.8k | return true; |
658 | 67.8k | } |
659 | | |
660 | | |
661 | | //===----------------------------------------------------------------------===// |
662 | | // Top-level transfer function logic (Dispatcher). |
663 | | //===----------------------------------------------------------------------===// |
664 | | |
665 | | /// evalAssume - Called by ConstraintManager. Used to call checker-specific |
666 | | /// logic for handling assumptions on symbolic values. |
667 | | ProgramStateRef ExprEngine::processAssume(ProgramStateRef state, |
668 | 1.32M | SVal cond, bool assumption) { |
669 | 1.32M | return getCheckerManager().runCheckersForEvalAssume(state, cond, assumption); |
670 | 1.32M | } |
671 | | |
672 | | ProgramStateRef |
673 | | ExprEngine::processRegionChanges(ProgramStateRef state, |
674 | | const InvalidatedSymbols *invalidated, |
675 | | ArrayRef<const MemRegion *> Explicits, |
676 | | ArrayRef<const MemRegion *> Regions, |
677 | | const LocationContext *LCtx, |
678 | 154k | const CallEvent *Call) { |
679 | 154k | return getCheckerManager().runCheckersForRegionChanges(state, invalidated, |
680 | 154k | Explicits, Regions, |
681 | 154k | LCtx, Call); |
682 | 154k | } |
683 | | |
684 | | static void |
685 | | printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State, |
686 | | const char *NL, const LocationContext *LCtx, |
687 | 40 | unsigned int Space = 0, bool IsDot = false) { |
688 | 40 | PrintingPolicy PP = |
689 | 40 | LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); |
690 | | |
691 | 40 | ++Space; |
692 | 40 | bool HasItem = false; |
693 | | |
694 | | // Store the last key. |
695 | 40 | const ConstructedObjectKey *LastKey = nullptr; |
696 | 56 | for (const auto &I : State->get<ObjectsUnderConstruction>()) { |
697 | 56 | const ConstructedObjectKey &Key = I.first; |
698 | 56 | if (Key.getLocationContext() != LCtx) |
699 | 16 | continue; |
700 | | |
701 | 40 | if (!HasItem) { |
702 | 34 | Out << '[' << NL; |
703 | 34 | HasItem = true; |
704 | 34 | } |
705 | | |
706 | 40 | LastKey = &Key; |
707 | 40 | } |
708 | | |
709 | 56 | for (const auto &I : State->get<ObjectsUnderConstruction>()) { |
710 | 56 | const ConstructedObjectKey &Key = I.first; |
711 | 56 | SVal Value = I.second; |
712 | 56 | if (Key.getLocationContext() != LCtx) |
713 | 16 | continue; |
714 | | |
715 | 40 | Indent(Out, Space, IsDot) << "{ "; |
716 | 40 | Key.printJson(Out, nullptr, PP); |
717 | 40 | Out << ", \"value\": \"" << Value << "\" }"; |
718 | | |
719 | 40 | if (&Key != LastKey) |
720 | 6 | Out << ','; |
721 | 40 | Out << NL; |
722 | 40 | } |
723 | | |
724 | 40 | if (HasItem) |
725 | 34 | Indent(Out, --Space, IsDot) << ']'; // End of "location_context". |
726 | 6 | else { |
727 | 6 | Out << "null "; |
728 | 6 | } |
729 | 40 | } |
730 | | |
731 | | static void printIndicesOfElementsToConstructJson( |
732 | | raw_ostream &Out, ProgramStateRef State, const char *NL, |
733 | 0 | const LocationContext *LCtx, unsigned int Space = 0, bool IsDot = false) { |
734 | 0 | using KeyT = std::pair<const Expr *, const LocationContext *>; |
735 | |
|
736 | 0 | const auto &Context = LCtx->getAnalysisDeclContext()->getASTContext(); |
737 | 0 | PrintingPolicy PP = Context.getPrintingPolicy(); |
738 | |
|
739 | 0 | ++Space; |
740 | 0 | bool HasItem = false; |
741 | | |
742 | | // Store the last key. |
743 | 0 | KeyT LastKey; |
744 | 0 | for (const auto &I : State->get<IndexOfElementToConstruct>()) { |
745 | 0 | const KeyT &Key = I.first; |
746 | 0 | if (Key.second != LCtx) |
747 | 0 | continue; |
748 | | |
749 | 0 | if (!HasItem) { |
750 | 0 | Out << '[' << NL; |
751 | 0 | HasItem = true; |
752 | 0 | } |
753 | |
|
754 | 0 | LastKey = Key; |
755 | 0 | } |
756 | |
|
757 | 0 | for (const auto &I : State->get<IndexOfElementToConstruct>()) { |
758 | 0 | const KeyT &Key = I.first; |
759 | 0 | unsigned Value = I.second; |
760 | 0 | if (Key.second != LCtx) |
761 | 0 | continue; |
762 | | |
763 | 0 | Indent(Out, Space, IsDot) << "{ "; |
764 | | |
765 | | // Expr |
766 | 0 | const Expr *E = Key.first; |
767 | 0 | Out << "\"stmt_id\": " << E->getID(Context); |
768 | | |
769 | | // Kind |
770 | 0 | Out << ", \"kind\": null"; |
771 | | |
772 | | // Pretty-print |
773 | 0 | Out << ", \"pretty\": "; |
774 | 0 | Out << "\"" << E->getStmtClassName() << ' ' |
775 | 0 | << E->getSourceRange().printToString(Context.getSourceManager()) << " '" |
776 | 0 | << QualType::getAsString(E->getType().split(), PP); |
777 | 0 | Out << "'\""; |
778 | |
|
779 | 0 | Out << ", \"value\": \"Current index: " << Value - 1 << "\" }"; |
780 | |
|
781 | 0 | if (Key != LastKey) |
782 | 0 | Out << ','; |
783 | 0 | Out << NL; |
784 | 0 | } |
785 | |
|
786 | 0 | if (HasItem) |
787 | 0 | Indent(Out, --Space, IsDot) << ']'; // End of "location_context". |
788 | 0 | else { |
789 | 0 | Out << "null "; |
790 | 0 | } |
791 | 0 | } |
792 | | |
793 | | static void printPendingInitLoopJson(raw_ostream &Out, ProgramStateRef State, |
794 | | const char *NL, |
795 | | const LocationContext *LCtx, |
796 | | unsigned int Space = 0, |
797 | 0 | bool IsDot = false) { |
798 | 0 | using KeyT = std::pair<const CXXConstructExpr *, const LocationContext *>; |
799 | |
|
800 | 0 | const auto &Context = LCtx->getAnalysisDeclContext()->getASTContext(); |
801 | 0 | PrintingPolicy PP = Context.getPrintingPolicy(); |
802 | |
|
803 | 0 | ++Space; |
804 | 0 | bool HasItem = false; |
805 | | |
806 | | // Store the last key. |
807 | 0 | KeyT LastKey; |
808 | 0 | for (const auto &I : State->get<PendingInitLoop>()) { |
809 | 0 | const KeyT &Key = I.first; |
810 | 0 | if (Key.second != LCtx) |
811 | 0 | continue; |
812 | | |
813 | 0 | if (!HasItem) { |
814 | 0 | Out << '[' << NL; |
815 | 0 | HasItem = true; |
816 | 0 | } |
817 | |
|
818 | 0 | LastKey = Key; |
819 | 0 | } |
820 | |
|
821 | 0 | for (const auto &I : State->get<PendingInitLoop>()) { |
822 | 0 | const KeyT &Key = I.first; |
823 | 0 | unsigned Value = I.second; |
824 | 0 | if (Key.second != LCtx) |
825 | 0 | continue; |
826 | | |
827 | 0 | Indent(Out, Space, IsDot) << "{ "; |
828 | |
|
829 | 0 | const CXXConstructExpr *E = Key.first; |
830 | 0 | Out << "\"stmt_id\": " << E->getID(Context); |
831 | |
|
832 | 0 | Out << ", \"kind\": null"; |
833 | 0 | Out << ", \"pretty\": "; |
834 | 0 | Out << '\"' << E->getStmtClassName() << ' ' |
835 | 0 | << E->getSourceRange().printToString(Context.getSourceManager()) << " '" |
836 | 0 | << QualType::getAsString(E->getType().split(), PP); |
837 | 0 | Out << "'\""; |
838 | |
|
839 | 0 | Out << ", \"value\": \"Flattened size: " << Value << "\"}"; |
840 | |
|
841 | 0 | if (Key != LastKey) |
842 | 0 | Out << ','; |
843 | 0 | Out << NL; |
844 | 0 | } |
845 | |
|
846 | 0 | if (HasItem) |
847 | 0 | Indent(Out, --Space, IsDot) << ']'; // End of "location_context". |
848 | 0 | else { |
849 | 0 | Out << "null "; |
850 | 0 | } |
851 | 0 | } |
852 | | |
853 | | static void |
854 | | printPendingArrayDestructionsJson(raw_ostream &Out, ProgramStateRef State, |
855 | | const char *NL, const LocationContext *LCtx, |
856 | 0 | unsigned int Space = 0, bool IsDot = false) { |
857 | 0 | using KeyT = const LocationContext *; |
858 | |
|
859 | 0 | ++Space; |
860 | 0 | bool HasItem = false; |
861 | | |
862 | | // Store the last key. |
863 | 0 | KeyT LastKey = nullptr; |
864 | 0 | for (const auto &I : State->get<PendingArrayDestruction>()) { |
865 | 0 | const KeyT &Key = I.first; |
866 | 0 | if (Key != LCtx) |
867 | 0 | continue; |
868 | | |
869 | 0 | if (!HasItem) { |
870 | 0 | Out << '[' << NL; |
871 | 0 | HasItem = true; |
872 | 0 | } |
873 | |
|
874 | 0 | LastKey = Key; |
875 | 0 | } |
876 | |
|
877 | 0 | for (const auto &I : State->get<PendingArrayDestruction>()) { |
878 | 0 | const KeyT &Key = I.first; |
879 | 0 | if (Key != LCtx) |
880 | 0 | continue; |
881 | | |
882 | 0 | Indent(Out, Space, IsDot) << "{ "; |
883 | |
|
884 | 0 | Out << "\"stmt_id\": null"; |
885 | 0 | Out << ", \"kind\": null"; |
886 | 0 | Out << ", \"pretty\": \"Current index: \""; |
887 | 0 | Out << ", \"value\": \"" << I.second << "\" }"; |
888 | |
|
889 | 0 | if (Key != LastKey) |
890 | 0 | Out << ','; |
891 | 0 | Out << NL; |
892 | 0 | } |
893 | |
|
894 | 0 | if (HasItem) |
895 | 0 | Indent(Out, --Space, IsDot) << ']'; // End of "location_context". |
896 | 0 | else { |
897 | 0 | Out << "null "; |
898 | 0 | } |
899 | 0 | } |
900 | | |
901 | | /// A helper function to generalize program state trait printing. |
902 | | /// The function invokes Printer as 'Printer(Out, State, NL, LC, Space, IsDot, |
903 | | /// std::forward<Args>(args)...)'. \n One possible type for Printer is |
904 | | /// 'void()(raw_ostream &, ProgramStateRef, const char *, const LocationContext |
905 | | /// *, unsigned int, bool, ...)' \n \param Trait The state trait to be printed. |
906 | | /// \param Printer A void function that prints Trait. |
907 | | /// \param Args An additional parameter pack that is passed to Print upon |
908 | | /// invocation. |
909 | | template <typename Trait, typename Printer, typename... Args> |
910 | | static void printStateTraitWithLocationContextJson( |
911 | | raw_ostream &Out, ProgramStateRef State, const LocationContext *LCtx, |
912 | | const char *NL, unsigned int Space, bool IsDot, |
913 | 632 | const char *jsonPropertyName, Printer printer, Args &&...args) { |
914 | | |
915 | 632 | using RequiredType = |
916 | 632 | void (*)(raw_ostream &, ProgramStateRef, const char *, |
917 | 632 | const LocationContext *, unsigned int, bool, Args &&...); |
918 | | |
919 | | // Try to do as much compile time checking as possible. |
920 | | // FIXME: check for invocable instead of function? |
921 | 632 | static_assert(std::is_function_v<std::remove_pointer_t<Printer>>, |
922 | 632 | "Printer is not a function!"); |
923 | 632 | static_assert(std::is_convertible_v<Printer, RequiredType>, |
924 | 632 | "Printer doesn't have the required type!"); |
925 | | |
926 | 632 | if (LCtx && !State->get<Trait>().isEmpty()400 ) { |
927 | 29 | Indent(Out, Space, IsDot) << '\"' << jsonPropertyName << "\": "; |
928 | 29 | ++Space; |
929 | 29 | Out << '[' << NL; |
930 | 40 | LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { |
931 | 40 | printer(Out, State, NL, LC, Space, IsDot, std::forward<Args>(args)...); |
932 | 40 | }); ExprEngine.cpp:void printStateTraitWithLocationContextJson<(anonymous namespace)::ObjectsUnderConstruction, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)>(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::LocationContext const*, char const*, unsigned int, bool, char const*, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool))::'lambda'(clang::LocationContext const*)::operator()(clang::LocationContext const*) const Line | Count | Source | 930 | 40 | LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { | 931 | 40 | printer(Out, State, NL, LC, Space, IsDot, std::forward<Args>(args)...); | 932 | 40 | }); |
Unexecuted instantiation: ExprEngine.cpp:void printStateTraitWithLocationContextJson<(anonymous namespace)::IndexOfElementToConstruct, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)>(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::LocationContext const*, char const*, unsigned int, bool, char const*, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool))::'lambda'(clang::LocationContext const*)::operator()(clang::LocationContext const*) const Unexecuted instantiation: ExprEngine.cpp:void printStateTraitWithLocationContextJson<(anonymous namespace)::PendingInitLoop, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)>(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::LocationContext const*, char const*, unsigned int, bool, char const*, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool))::'lambda'(clang::LocationContext const*)::operator()(clang::LocationContext const*) const Unexecuted instantiation: ExprEngine.cpp:void printStateTraitWithLocationContextJson<(anonymous namespace)::PendingArrayDestruction, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)>(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::LocationContext const*, char const*, unsigned int, bool, char const*, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool))::'lambda'(clang::LocationContext const*)::operator()(clang::LocationContext const*) const |
933 | | |
934 | 29 | --Space; |
935 | 29 | Indent(Out, Space, IsDot) << "]," << NL; // End of "jsonPropertyName". |
936 | 29 | } |
937 | 632 | } ExprEngine.cpp:void printStateTraitWithLocationContextJson<(anonymous namespace)::ObjectsUnderConstruction, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)>(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::LocationContext const*, char const*, unsigned int, bool, char const*, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)) Line | Count | Source | 913 | 158 | const char *jsonPropertyName, Printer printer, Args &&...args) { | 914 | | | 915 | 158 | using RequiredType = | 916 | 158 | void (*)(raw_ostream &, ProgramStateRef, const char *, | 917 | 158 | const LocationContext *, unsigned int, bool, Args &&...); | 918 | | | 919 | | // Try to do as much compile time checking as possible. | 920 | | // FIXME: check for invocable instead of function? | 921 | 158 | static_assert(std::is_function_v<std::remove_pointer_t<Printer>>, | 922 | 158 | "Printer is not a function!"); | 923 | 158 | static_assert(std::is_convertible_v<Printer, RequiredType>, | 924 | 158 | "Printer doesn't have the required type!"); | 925 | | | 926 | 158 | if (LCtx && !State->get<Trait>().isEmpty()100 ) { | 927 | 29 | Indent(Out, Space, IsDot) << '\"' << jsonPropertyName << "\": "; | 928 | 29 | ++Space; | 929 | 29 | Out << '[' << NL; | 930 | 29 | LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { | 931 | 29 | printer(Out, State, NL, LC, Space, IsDot, std::forward<Args>(args)...); | 932 | 29 | }); | 933 | | | 934 | 29 | --Space; | 935 | 29 | Indent(Out, Space, IsDot) << "]," << NL; // End of "jsonPropertyName". | 936 | 29 | } | 937 | 158 | } |
ExprEngine.cpp:void printStateTraitWithLocationContextJson<(anonymous namespace)::IndexOfElementToConstruct, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)>(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::LocationContext const*, char const*, unsigned int, bool, char const*, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)) Line | Count | Source | 913 | 158 | const char *jsonPropertyName, Printer printer, Args &&...args) { | 914 | | | 915 | 158 | using RequiredType = | 916 | 158 | void (*)(raw_ostream &, ProgramStateRef, const char *, | 917 | 158 | const LocationContext *, unsigned int, bool, Args &&...); | 918 | | | 919 | | // Try to do as much compile time checking as possible. | 920 | | // FIXME: check for invocable instead of function? | 921 | 158 | static_assert(std::is_function_v<std::remove_pointer_t<Printer>>, | 922 | 158 | "Printer is not a function!"); | 923 | 158 | static_assert(std::is_convertible_v<Printer, RequiredType>, | 924 | 158 | "Printer doesn't have the required type!"); | 925 | | | 926 | 158 | if (LCtx && !State->get<Trait>().isEmpty()100 ) { | 927 | 0 | Indent(Out, Space, IsDot) << '\"' << jsonPropertyName << "\": "; | 928 | 0 | ++Space; | 929 | 0 | Out << '[' << NL; | 930 | 0 | LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { | 931 | 0 | printer(Out, State, NL, LC, Space, IsDot, std::forward<Args>(args)...); | 932 | 0 | }); | 933 | |
| 934 | 0 | --Space; | 935 | 0 | Indent(Out, Space, IsDot) << "]," << NL; // End of "jsonPropertyName". | 936 | 0 | } | 937 | 158 | } |
ExprEngine.cpp:void printStateTraitWithLocationContextJson<(anonymous namespace)::PendingInitLoop, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)>(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::LocationContext const*, char const*, unsigned int, bool, char const*, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)) Line | Count | Source | 913 | 158 | const char *jsonPropertyName, Printer printer, Args &&...args) { | 914 | | | 915 | 158 | using RequiredType = | 916 | 158 | void (*)(raw_ostream &, ProgramStateRef, const char *, | 917 | 158 | const LocationContext *, unsigned int, bool, Args &&...); | 918 | | | 919 | | // Try to do as much compile time checking as possible. | 920 | | // FIXME: check for invocable instead of function? | 921 | 158 | static_assert(std::is_function_v<std::remove_pointer_t<Printer>>, | 922 | 158 | "Printer is not a function!"); | 923 | 158 | static_assert(std::is_convertible_v<Printer, RequiredType>, | 924 | 158 | "Printer doesn't have the required type!"); | 925 | | | 926 | 158 | if (LCtx && !State->get<Trait>().isEmpty()100 ) { | 927 | 0 | Indent(Out, Space, IsDot) << '\"' << jsonPropertyName << "\": "; | 928 | 0 | ++Space; | 929 | 0 | Out << '[' << NL; | 930 | 0 | LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { | 931 | 0 | printer(Out, State, NL, LC, Space, IsDot, std::forward<Args>(args)...); | 932 | 0 | }); | 933 | |
| 934 | 0 | --Space; | 935 | 0 | Indent(Out, Space, IsDot) << "]," << NL; // End of "jsonPropertyName". | 936 | 0 | } | 937 | 158 | } |
ExprEngine.cpp:void printStateTraitWithLocationContextJson<(anonymous namespace)::PendingArrayDestruction, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)>(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::LocationContext const*, char const*, unsigned int, bool, char const*, void (*)(llvm::raw_ostream&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, char const*, clang::LocationContext const*, unsigned int, bool)) Line | Count | Source | 913 | 158 | const char *jsonPropertyName, Printer printer, Args &&...args) { | 914 | | | 915 | 158 | using RequiredType = | 916 | 158 | void (*)(raw_ostream &, ProgramStateRef, const char *, | 917 | 158 | const LocationContext *, unsigned int, bool, Args &&...); | 918 | | | 919 | | // Try to do as much compile time checking as possible. | 920 | | // FIXME: check for invocable instead of function? | 921 | 158 | static_assert(std::is_function_v<std::remove_pointer_t<Printer>>, | 922 | 158 | "Printer is not a function!"); | 923 | 158 | static_assert(std::is_convertible_v<Printer, RequiredType>, | 924 | 158 | "Printer doesn't have the required type!"); | 925 | | | 926 | 158 | if (LCtx && !State->get<Trait>().isEmpty()100 ) { | 927 | 0 | Indent(Out, Space, IsDot) << '\"' << jsonPropertyName << "\": "; | 928 | 0 | ++Space; | 929 | 0 | Out << '[' << NL; | 930 | 0 | LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { | 931 | 0 | printer(Out, State, NL, LC, Space, IsDot, std::forward<Args>(args)...); | 932 | 0 | }); | 933 | |
| 934 | 0 | --Space; | 935 | 0 | Indent(Out, Space, IsDot) << "]," << NL; // End of "jsonPropertyName". | 936 | 0 | } | 937 | 158 | } |
|
938 | | |
939 | | void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State, |
940 | | const LocationContext *LCtx, const char *NL, |
941 | 158 | unsigned int Space, bool IsDot) const { |
942 | | |
943 | 158 | printStateTraitWithLocationContextJson<ObjectsUnderConstruction>( |
944 | 158 | Out, State, LCtx, NL, Space, IsDot, "constructing_objects", |
945 | 158 | printObjectsUnderConstructionJson); |
946 | 158 | printStateTraitWithLocationContextJson<IndexOfElementToConstruct>( |
947 | 158 | Out, State, LCtx, NL, Space, IsDot, "index_of_element", |
948 | 158 | printIndicesOfElementsToConstructJson); |
949 | 158 | printStateTraitWithLocationContextJson<PendingInitLoop>( |
950 | 158 | Out, State, LCtx, NL, Space, IsDot, "pending_init_loops", |
951 | 158 | printPendingInitLoopJson); |
952 | 158 | printStateTraitWithLocationContextJson<PendingArrayDestruction>( |
953 | 158 | Out, State, LCtx, NL, Space, IsDot, "pending_destructors", |
954 | 158 | printPendingArrayDestructionsJson); |
955 | | |
956 | 158 | getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space, |
957 | 158 | IsDot); |
958 | 158 | } |
959 | | |
960 | 16.1k | void ExprEngine::processEndWorklist() { |
961 | | // This prints the name of the top-level function if we crash. |
962 | 16.1k | PrettyStackTraceLocationContext CrashInfo(getRootLocationContext()); |
963 | 16.1k | getCheckerManager().runCheckersForEndAnalysis(G, BR, *this); |
964 | 16.1k | } |
965 | | |
966 | | void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, |
967 | 1.35M | unsigned StmtIdx, NodeBuilderContext *Ctx) { |
968 | 1.35M | PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); |
969 | 1.35M | currStmtIdx = StmtIdx; |
970 | 1.35M | currBldrCtx = Ctx; |
971 | | |
972 | 1.35M | switch (E.getKind()) { |
973 | 1.30M | case CFGElement::Statement: |
974 | 1.33M | case CFGElement::Constructor: |
975 | 1.33M | case CFGElement::CXXRecordTypedCall: |
976 | 1.33M | ProcessStmt(E.castAs<CFGStmt>().getStmt(), Pred); |
977 | 1.33M | return; |
978 | 12.0k | case CFGElement::Initializer: |
979 | 12.0k | ProcessInitializer(E.castAs<CFGInitializer>(), Pred); |
980 | 12.0k | return; |
981 | 1.10k | case CFGElement::NewAllocator: |
982 | 1.10k | ProcessNewAllocator(E.castAs<CFGNewAllocator>().getAllocatorExpr(), |
983 | 1.10k | Pred); |
984 | 1.10k | return; |
985 | 710 | case CFGElement::AutomaticObjectDtor: |
986 | 889 | case CFGElement::DeleteDtor: |
987 | 1.01k | case CFGElement::BaseDtor: |
988 | 1.17k | case CFGElement::MemberDtor: |
989 | 1.98k | case CFGElement::TemporaryDtor: |
990 | 1.98k | ProcessImplicitDtor(E.castAs<CFGImplicitDtor>(), Pred); |
991 | 1.98k | return; |
992 | 148 | case CFGElement::LoopExit: |
993 | 148 | ProcessLoopExit(E.castAs<CFGLoopExit>().getLoopStmt(), Pred); |
994 | 148 | return; |
995 | 0 | case CFGElement::LifetimeEnds: |
996 | 0 | case CFGElement::ScopeBegin: |
997 | 0 | case CFGElement::ScopeEnd: |
998 | 0 | return; |
999 | 1.35M | } |
1000 | 1.35M | } |
1001 | | |
1002 | | static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, |
1003 | | const Stmt *S, |
1004 | | const ExplodedNode *Pred, |
1005 | 1.33M | const LocationContext *LC) { |
1006 | | // Are we never purging state values? |
1007 | 1.33M | if (AMgr.options.AnalysisPurgeOpt == PurgeNone) |
1008 | 739 | return false; |
1009 | | |
1010 | | // Is this the beginning of a basic block? |
1011 | 1.33M | if (Pred->getLocation().getAs<BlockEntrance>()) |
1012 | 137k | return true; |
1013 | | |
1014 | | // Is this on a non-expression? |
1015 | 1.19M | if (!isa<Expr>(S)) |
1016 | 64.3k | return true; |
1017 | | |
1018 | | // Run before processing a call. |
1019 | 1.13M | if (CallEvent::isCallStmt(S)) |
1020 | 113k | return true; |
1021 | | |
1022 | | // Is this an expression that is consumed by another expression? If so, |
1023 | | // postpone cleaning out the state. |
1024 | 1.02M | ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap(); |
1025 | 1.02M | return !PM.isConsumedExpr(cast<Expr>(S)); |
1026 | 1.13M | } |
1027 | | |
1028 | | void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, |
1029 | | const Stmt *ReferenceStmt, |
1030 | | const LocationContext *LC, |
1031 | | const Stmt *DiagnosticStmt, |
1032 | 426k | ProgramPoint::Kind K) { |
1033 | 426k | assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind || |
1034 | 426k | ReferenceStmt == nullptr || isa<ReturnStmt>(ReferenceStmt)) |
1035 | 426k | && "PostStmt is not generally supported by the SymbolReaper yet"); |
1036 | 426k | assert(LC && "Must pass the current (or expiring) LocationContext"); |
1037 | | |
1038 | 426k | if (!DiagnosticStmt) { |
1039 | 362k | DiagnosticStmt = ReferenceStmt; |
1040 | 362k | assert(DiagnosticStmt && "Required for clearing a LocationContext"); |
1041 | 362k | } |
1042 | | |
1043 | 426k | NumRemoveDeadBindings++; |
1044 | 426k | ProgramStateRef CleanedState = Pred->getState(); |
1045 | | |
1046 | | // LC is the location context being destroyed, but SymbolReaper wants a |
1047 | | // location context that is still live. (If this is the top-level stack |
1048 | | // frame, this will be null.) |
1049 | 426k | if (!ReferenceStmt) { |
1050 | 56.3k | assert(K == ProgramPoint::PostStmtPurgeDeadSymbolsKind && |
1051 | 56.3k | "Use PostStmtPurgeDeadSymbolsKind for clearing a LocationContext"); |
1052 | 56.3k | LC = LC->getParent(); |
1053 | 56.3k | } |
1054 | | |
1055 | 426k | const StackFrameContext *SFC = LC ? LC->getStackFrame()408k : nullptr17.6k ; |
1056 | 426k | SymbolReaper SymReaper(SFC, ReferenceStmt, SymMgr, getStoreManager()); |
1057 | | |
1058 | 426k | for (auto I : CleanedState->get<ObjectsUnderConstruction>()) { |
1059 | 123k | if (SymbolRef Sym = I.second.getAsSymbol()) |
1060 | 2.67k | SymReaper.markLive(Sym); |
1061 | 123k | if (const MemRegion *MR = I.second.getAsRegion()) |
1062 | 112k | SymReaper.markLive(MR); |
1063 | 123k | } |
1064 | | |
1065 | 426k | getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); |
1066 | | |
1067 | | // Create a state in which dead bindings are removed from the environment |
1068 | | // and the store. TODO: The function should just return new env and store, |
1069 | | // not a new state. |
1070 | 426k | CleanedState = StateMgr.removeDeadBindingsFromEnvironmentAndStore( |
1071 | 426k | CleanedState, SFC, SymReaper); |
1072 | | |
1073 | | // Process any special transfer function for dead symbols. |
1074 | | // A tag to track convenience transitions, which can be removed at cleanup. |
1075 | 426k | static SimpleProgramPointTag cleanupTag(TagProviderName, "Clean Node"); |
1076 | | // Call checkers with the non-cleaned state so that they could query the |
1077 | | // values of the soon to be dead symbols. |
1078 | 426k | ExplodedNodeSet CheckedSet; |
1079 | 426k | getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper, |
1080 | 426k | DiagnosticStmt, *this, K); |
1081 | | |
1082 | | // For each node in CheckedSet, generate CleanedNodes that have the |
1083 | | // environment, the store, and the constraints cleaned up but have the |
1084 | | // user-supplied states as the predecessors. |
1085 | 426k | StmtNodeBuilder Bldr(CheckedSet, Out, *currBldrCtx); |
1086 | 426k | for (const auto I : CheckedSet) { |
1087 | 426k | ProgramStateRef CheckerState = I->getState(); |
1088 | | |
1089 | | // The constraint manager has not been cleaned up yet, so clean up now. |
1090 | 426k | CheckerState = |
1091 | 426k | getConstraintManager().removeDeadBindings(CheckerState, SymReaper); |
1092 | | |
1093 | 426k | assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) && |
1094 | 426k | "Checkers are not allowed to modify the Environment as a part of " |
1095 | 426k | "checkDeadSymbols processing."); |
1096 | 426k | assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) && |
1097 | 426k | "Checkers are not allowed to modify the Store as a part of " |
1098 | 426k | "checkDeadSymbols processing."); |
1099 | | |
1100 | | // Create a state based on CleanedState with CheckerState GDM and |
1101 | | // generate a transition to that state. |
1102 | 426k | ProgramStateRef CleanedCheckerSt = |
1103 | 426k | StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); |
1104 | 426k | Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K); |
1105 | 426k | } |
1106 | 426k | } |
1107 | | |
1108 | 1.33M | void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { |
1109 | | // Reclaim any unnecessary nodes in the ExplodedGraph. |
1110 | 1.33M | G.reclaimRecentlyAllocatedNodes(); |
1111 | | |
1112 | 1.33M | PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), |
1113 | 1.33M | currStmt->getBeginLoc(), |
1114 | 1.33M | "Error evaluating statement"); |
1115 | | |
1116 | | // Remove dead bindings and symbols. |
1117 | 1.33M | ExplodedNodeSet CleanedStates; |
1118 | 1.33M | if (shouldRemoveDeadBindings(AMgr, currStmt, Pred, |
1119 | 1.33M | Pred->getLocationContext())) { |
1120 | 362k | removeDead(Pred, CleanedStates, currStmt, |
1121 | 362k | Pred->getLocationContext()); |
1122 | 362k | } else |
1123 | 975k | CleanedStates.Add(Pred); |
1124 | | |
1125 | | // Visit the statement. |
1126 | 1.33M | ExplodedNodeSet Dst; |
1127 | 1.33M | for (const auto I : CleanedStates) { |
1128 | 1.33M | ExplodedNodeSet DstI; |
1129 | | // Visit the statement. |
1130 | 1.33M | Visit(currStmt, I, DstI); |
1131 | 1.33M | Dst.insert(DstI); |
1132 | 1.33M | } |
1133 | | |
1134 | | // Enqueue the new nodes onto the work list. |
1135 | 1.33M | Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); |
1136 | 1.33M | } |
1137 | | |
1138 | 148 | void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) { |
1139 | 148 | PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), |
1140 | 148 | S->getBeginLoc(), |
1141 | 148 | "Error evaluating end of the loop"); |
1142 | 148 | ExplodedNodeSet Dst; |
1143 | 148 | Dst.Add(Pred); |
1144 | 148 | NodeBuilder Bldr(Pred, Dst, *currBldrCtx); |
1145 | 148 | ProgramStateRef NewState = Pred->getState(); |
1146 | | |
1147 | 148 | if(AMgr.options.ShouldUnrollLoops) |
1148 | 148 | NewState = processLoopEnd(S, NewState); |
1149 | | |
1150 | 148 | LoopExit PP(S, Pred->getLocationContext()); |
1151 | 148 | Bldr.generateNode(PP, NewState, Pred); |
1152 | | // Enqueue the new nodes onto the work list. |
1153 | 148 | Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); |
1154 | 148 | } |
1155 | | |
1156 | | void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit, |
1157 | 12.0k | ExplodedNode *Pred) { |
1158 | 12.0k | const CXXCtorInitializer *BMI = CFGInit.getInitializer(); |
1159 | 12.0k | const Expr *Init = BMI->getInit()->IgnoreImplicit(); |
1160 | 12.0k | const LocationContext *LC = Pred->getLocationContext(); |
1161 | | |
1162 | 12.0k | PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), |
1163 | 12.0k | BMI->getSourceLocation(), |
1164 | 12.0k | "Error evaluating initializer"); |
1165 | | |
1166 | | // We don't clean up dead bindings here. |
1167 | 12.0k | const auto *stackFrame = cast<StackFrameContext>(Pred->getLocationContext()); |
1168 | 12.0k | const auto *decl = cast<CXXConstructorDecl>(stackFrame->getDecl()); |
1169 | | |
1170 | 12.0k | ProgramStateRef State = Pred->getState(); |
1171 | 12.0k | SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame)); |
1172 | | |
1173 | 12.0k | ExplodedNodeSet Tmp; |
1174 | 12.0k | SVal FieldLoc; |
1175 | | |
1176 | | // Evaluate the initializer, if necessary |
1177 | 12.0k | if (BMI->isAnyMemberInitializer()) { |
1178 | | // Constructors build the object directly in the field, |
1179 | | // but non-objects must be copied in from the initializer. |
1180 | 11.3k | if (getObjectUnderConstruction(State, BMI, LC)) { |
1181 | | // The field was directly constructed, so there is no need to bind. |
1182 | | // But we still need to stop tracking the object under construction. |
1183 | 2.64k | State = finishObjectConstruction(State, BMI, LC); |
1184 | 2.64k | NodeBuilder Bldr(Pred, Tmp, *currBldrCtx); |
1185 | 2.64k | PostStore PS(Init, LC, /*Loc*/ nullptr, /*tag*/ nullptr); |
1186 | 2.64k | Bldr.generateNode(PS, State, Pred); |
1187 | 8.65k | } else { |
1188 | 8.65k | const ValueDecl *Field; |
1189 | 8.65k | if (BMI->isIndirectMemberInitializer()) { |
1190 | 4 | Field = BMI->getIndirectMember(); |
1191 | 4 | FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal); |
1192 | 8.65k | } else { |
1193 | 8.65k | Field = BMI->getMember(); |
1194 | 8.65k | FieldLoc = State->getLValue(BMI->getMember(), thisVal); |
1195 | 8.65k | } |
1196 | | |
1197 | 8.65k | SVal InitVal; |
1198 | 8.65k | if (Init->getType()->isArrayType()) { |
1199 | | // Handle arrays of trivial type. We can represent this with a |
1200 | | // primitive load/copy from the base array region. |
1201 | 29 | const ArraySubscriptExpr *ASE; |
1202 | 29 | while ((ASE = dyn_cast<ArraySubscriptExpr>(Init))) |
1203 | 0 | Init = ASE->getBase()->IgnoreImplicit(); |
1204 | | |
1205 | 29 | SVal LValue = State->getSVal(Init, stackFrame); |
1206 | 29 | if (!Field->getType()->isReferenceType()) |
1207 | 25 | if (std::optional<Loc> LValueLoc = LValue.getAs<Loc>()) |
1208 | 8 | InitVal = State->getSVal(*LValueLoc); |
1209 | | |
1210 | | // If we fail to get the value for some reason, use a symbolic value. |
1211 | 29 | if (InitVal.isUnknownOrUndef()) { |
1212 | 21 | SValBuilder &SVB = getSValBuilder(); |
1213 | 21 | InitVal = SVB.conjureSymbolVal(BMI->getInit(), stackFrame, |
1214 | 21 | Field->getType(), |
1215 | 21 | currBldrCtx->blockCount()); |
1216 | 21 | } |
1217 | 8.63k | } else { |
1218 | 8.63k | InitVal = State->getSVal(BMI->getInit(), stackFrame); |
1219 | 8.63k | } |
1220 | | |
1221 | 8.65k | PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); |
1222 | 8.65k | evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP); |
1223 | 8.65k | } |
1224 | 11.3k | } else { |
1225 | 756 | assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer()); |
1226 | 756 | Tmp.insert(Pred); |
1227 | | // We already did all the work when visiting the CXXConstructExpr. |
1228 | 756 | } |
1229 | | |
1230 | | // Construct PostInitializer nodes whether the state changed or not, |
1231 | | // so that the diagnostics don't get confused. |
1232 | 12.0k | PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); |
1233 | 12.0k | ExplodedNodeSet Dst; |
1234 | 12.0k | NodeBuilder Bldr(Tmp, Dst, *currBldrCtx); |
1235 | 12.0k | for (const auto I : Tmp) { |
1236 | 12.0k | ProgramStateRef State = I->getState(); |
1237 | 12.0k | Bldr.generateNode(PP, State, I); |
1238 | 12.0k | } |
1239 | | |
1240 | | // Enqueue the new nodes onto the work list. |
1241 | 12.0k | Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); |
1242 | 12.0k | } |
1243 | | |
1244 | | std::pair<ProgramStateRef, uint64_t> |
1245 | | ExprEngine::prepareStateForArrayDestruction(const ProgramStateRef State, |
1246 | | const MemRegion *Region, |
1247 | | const QualType &ElementTy, |
1248 | | const LocationContext *LCtx, |
1249 | 208 | SVal *ElementCountVal) { |
1250 | 208 | assert(Region != nullptr && "Not-null region expected"); |
1251 | | |
1252 | 208 | QualType Ty = ElementTy.getDesugaredType(getContext()); |
1253 | 351 | while (const auto *NTy = dyn_cast<ArrayType>(Ty)) |
1254 | 143 | Ty = NTy->getElementType().getDesugaredType(getContext()); |
1255 | | |
1256 | 208 | auto ElementCount = getDynamicElementCount(State, Region, svalBuilder, Ty); |
1257 | | |
1258 | 208 | if (ElementCountVal) |
1259 | 208 | *ElementCountVal = ElementCount; |
1260 | | |
1261 | | // Note: the destructors are called in reverse order. |
1262 | 208 | unsigned Idx = 0; |
1263 | 208 | if (auto OptionalIdx = getPendingArrayDestruction(State, LCtx)) { |
1264 | 128 | Idx = *OptionalIdx; |
1265 | 128 | } else { |
1266 | | // The element count is either unknown, or an SVal that's not an integer. |
1267 | 80 | if (!ElementCount.isConstant()) |
1268 | 4 | return {State, 0}; |
1269 | | |
1270 | 76 | Idx = ElementCount.getAsInteger()->getLimitedValue(); |
1271 | 76 | } |
1272 | | |
1273 | 204 | if (Idx == 0) |
1274 | 20 | return {State, 0}; |
1275 | | |
1276 | 184 | --Idx; |
1277 | | |
1278 | 184 | return {setPendingArrayDestruction(State, LCtx, Idx), Idx}; |
1279 | 204 | } |
1280 | | |
1281 | | void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, |
1282 | 1.98k | ExplodedNode *Pred) { |
1283 | 1.98k | ExplodedNodeSet Dst; |
1284 | 1.98k | switch (D.getKind()) { |
1285 | 710 | case CFGElement::AutomaticObjectDtor: |
1286 | 710 | ProcessAutomaticObjDtor(D.castAs<CFGAutomaticObjDtor>(), Pred, Dst); |
1287 | 710 | break; |
1288 | 129 | case CFGElement::BaseDtor: |
1289 | 129 | ProcessBaseDtor(D.castAs<CFGBaseDtor>(), Pred, Dst); |
1290 | 129 | break; |
1291 | 155 | case CFGElement::MemberDtor: |
1292 | 155 | ProcessMemberDtor(D.castAs<CFGMemberDtor>(), Pred, Dst); |
1293 | 155 | break; |
1294 | 813 | case CFGElement::TemporaryDtor: |
1295 | 813 | ProcessTemporaryDtor(D.castAs<CFGTemporaryDtor>(), Pred, Dst); |
1296 | 813 | break; |
1297 | 179 | case CFGElement::DeleteDtor: |
1298 | 179 | ProcessDeleteDtor(D.castAs<CFGDeleteDtor>(), Pred, Dst); |
1299 | 179 | break; |
1300 | 0 | default: |
1301 | 0 | llvm_unreachable("Unexpected dtor kind."); |
1302 | 1.98k | } |
1303 | | |
1304 | | // Enqueue the new nodes onto the work list. |
1305 | 1.98k | Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); |
1306 | 1.98k | } |
1307 | | |
1308 | | void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE, |
1309 | 1.10k | ExplodedNode *Pred) { |
1310 | 1.10k | ExplodedNodeSet Dst; |
1311 | 1.10k | AnalysisManager &AMgr = getAnalysisManager(); |
1312 | 1.10k | AnalyzerOptions &Opts = AMgr.options; |
1313 | | // TODO: We're not evaluating allocators for all cases just yet as |
1314 | | // we're not handling the return value correctly, which causes false |
1315 | | // positives when the alpha.cplusplus.NewDeleteLeaks check is on. |
1316 | 1.10k | if (Opts.MayInlineCXXAllocator) |
1317 | 1.09k | VisitCXXNewAllocatorCall(NE, Pred, Dst); |
1318 | 7 | else { |
1319 | 7 | NodeBuilder Bldr(Pred, Dst, *currBldrCtx); |
1320 | 7 | const LocationContext *LCtx = Pred->getLocationContext(); |
1321 | 7 | PostImplicitCall PP(NE->getOperatorNew(), NE->getBeginLoc(), LCtx, |
1322 | 7 | getCFGElementRef()); |
1323 | 7 | Bldr.generateNode(PP, Pred->getState(), Pred); |
1324 | 7 | } |
1325 | 1.10k | Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); |
1326 | 1.10k | } |
1327 | | |
1328 | | void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, |
1329 | | ExplodedNode *Pred, |
1330 | 710 | ExplodedNodeSet &Dst) { |
1331 | 710 | const auto *DtorDecl = Dtor.getDestructorDecl(getContext()); |
1332 | 710 | const VarDecl *varDecl = Dtor.getVarDecl(); |
1333 | 710 | QualType varType = varDecl->getType(); |
1334 | | |
1335 | 710 | ProgramStateRef state = Pred->getState(); |
1336 | 710 | const LocationContext *LCtx = Pred->getLocationContext(); |
1337 | | |
1338 | 710 | SVal dest = state->getLValue(varDecl, LCtx); |
1339 | 710 | const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion(); |
1340 | | |
1341 | 710 | if (varType->isReferenceType()) { |
1342 | 104 | const MemRegion *ValueRegion = state->getSVal(Region).getAsRegion(); |
1343 | 104 | if (!ValueRegion) { |
1344 | | // FIXME: This should not happen. The language guarantees a presence |
1345 | | // of a valid initializer here, so the reference shall not be undefined. |
1346 | | // It seems that we're calling destructors over variables that |
1347 | | // were not initialized yet. |
1348 | 4 | return; |
1349 | 4 | } |
1350 | 100 | Region = ValueRegion->getBaseRegion(); |
1351 | 100 | varType = cast<TypedValueRegion>(Region)->getValueType(); |
1352 | 100 | } |
1353 | | |
1354 | 706 | unsigned Idx = 0; |
1355 | 706 | if (isa<ArrayType>(varType)) { |
1356 | 28 | SVal ElementCount; |
1357 | 28 | std::tie(state, Idx) = prepareStateForArrayDestruction( |
1358 | 28 | state, Region, varType, LCtx, &ElementCount); |
1359 | | |
1360 | 28 | if (ElementCount.isConstant()) { |
1361 | 28 | uint64_t ArrayLength = ElementCount.getAsInteger()->getLimitedValue(); |
1362 | 28 | assert(ArrayLength && |
1363 | 28 | "An automatic dtor for a 0 length array shouldn't be triggered!"); |
1364 | | |
1365 | | // Still handle this case if we don't have assertions enabled. |
1366 | 28 | if (!ArrayLength) { |
1367 | 0 | static SimpleProgramPointTag PT( |
1368 | 0 | "ExprEngine", "Skipping automatic 0 length array destruction, " |
1369 | 0 | "which shouldn't be in the CFG."); |
1370 | 0 | PostImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, |
1371 | 0 | getCFGElementRef(), &PT); |
1372 | 0 | NodeBuilder Bldr(Pred, Dst, *currBldrCtx); |
1373 | 0 | Bldr.generateSink(PP, Pred->getState(), Pred); |
1374 | 0 | return; |
1375 | 0 | } |
1376 | 28 | } |
1377 | 28 | } |
1378 | | |
1379 | 706 | EvalCallOptions CallOpts; |
1380 | 706 | Region = makeElementRegion(state, loc::MemRegionVal(Region), varType, |
1381 | 706 | CallOpts.IsArrayCtorOrDtor, Idx) |
1382 | 706 | .getAsRegion(); |
1383 | | |
1384 | 706 | NodeBuilder Bldr(Pred, Dst, getBuilderContext()); |
1385 | | |
1386 | 706 | static SimpleProgramPointTag PT("ExprEngine", |
1387 | 706 | "Prepare for object destruction"); |
1388 | 706 | PreImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, getCFGElementRef(), |
1389 | 706 | &PT); |
1390 | 706 | Pred = Bldr.generateNode(PP, state, Pred); |
1391 | | |
1392 | 706 | if (!Pred) |
1393 | 0 | return; |
1394 | 706 | Bldr.takeNodes(Pred); |
1395 | | |
1396 | 706 | VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), |
1397 | 706 | /*IsBase=*/false, Pred, Dst, CallOpts); |
1398 | 706 | } |
1399 | | |
1400 | | void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, |
1401 | | ExplodedNode *Pred, |
1402 | 179 | ExplodedNodeSet &Dst) { |
1403 | 179 | ProgramStateRef State = Pred->getState(); |
1404 | 179 | const LocationContext *LCtx = Pred->getLocationContext(); |
1405 | 179 | const CXXDeleteExpr *DE = Dtor.getDeleteExpr(); |
1406 | 179 | const Stmt *Arg = DE->getArgument(); |
1407 | 179 | QualType DTy = DE->getDestroyedType(); |
1408 | 179 | SVal ArgVal = State->getSVal(Arg, LCtx); |
1409 | | |
1410 | | // If the argument to delete is known to be a null value, |
1411 | | // don't run destructor. |
1412 | 179 | if (State->isNull(ArgVal).isConstrainedTrue()) { |
1413 | 9 | QualType BTy = getContext().getBaseElementType(DTy); |
1414 | 9 | const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl(); |
1415 | 9 | const CXXDestructorDecl *Dtor = RD->getDestructor(); |
1416 | | |
1417 | 9 | PostImplicitCall PP(Dtor, DE->getBeginLoc(), LCtx, getCFGElementRef()); |
1418 | 9 | NodeBuilder Bldr(Pred, Dst, *currBldrCtx); |
1419 | 9 | Bldr.generateNode(PP, Pred->getState(), Pred); |
1420 | 9 | return; |
1421 | 9 | } |
1422 | | |
1423 | 170 | auto getDtorDecl = [](const QualType &DTy) { |
1424 | 170 | const CXXRecordDecl *RD = DTy->getAsCXXRecordDecl(); |
1425 | 170 | return RD->getDestructor(); |
1426 | 170 | }; |
1427 | | |
1428 | 170 | unsigned Idx = 0; |
1429 | 170 | EvalCallOptions CallOpts; |
1430 | 170 | const MemRegion *ArgR = ArgVal.getAsRegion(); |
1431 | | |
1432 | 170 | if (DE->isArrayForm()) { |
1433 | 88 | CallOpts.IsArrayCtorOrDtor = true; |
1434 | | // Yes, it may even be a multi-dimensional array. |
1435 | 138 | while (const auto *AT = getContext().getAsArrayType(DTy)) |
1436 | 50 | DTy = AT->getElementType(); |
1437 | | |
1438 | 88 | if (ArgR) { |
1439 | 84 | SVal ElementCount; |
1440 | 84 | std::tie(State, Idx) = prepareStateForArrayDestruction( |
1441 | 84 | State, ArgR, DTy, LCtx, &ElementCount); |
1442 | | |
1443 | | // If we're about to destruct a 0 length array, don't run any of the |
1444 | | // destructors. |
1445 | 84 | if (ElementCount.isConstant() && |
1446 | 84 | ElementCount.getAsInteger()->getLimitedValue() == 080 ) { |
1447 | | |
1448 | 20 | static SimpleProgramPointTag PT( |
1449 | 20 | "ExprEngine", "Skipping 0 length array delete destruction"); |
1450 | 20 | PostImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, |
1451 | 20 | getCFGElementRef(), &PT); |
1452 | 20 | NodeBuilder Bldr(Pred, Dst, *currBldrCtx); |
1453 | 20 | Bldr.generateNode(PP, Pred->getState(), Pred); |
1454 | 20 | return; |
1455 | 20 | } |
1456 | | |
1457 | 64 | ArgR = State->getLValue(DTy, svalBuilder.makeArrayIndex(Idx), ArgVal) |
1458 | 64 | .getAsRegion(); |
1459 | 64 | } |
1460 | 88 | } |
1461 | | |
1462 | 150 | NodeBuilder Bldr(Pred, Dst, getBuilderContext()); |
1463 | 150 | static SimpleProgramPointTag PT("ExprEngine", |
1464 | 150 | "Prepare for object destruction"); |
1465 | 150 | PreImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, |
1466 | 150 | getCFGElementRef(), &PT); |
1467 | 150 | Pred = Bldr.generateNode(PP, State, Pred); |
1468 | | |
1469 | 150 | if (!Pred) |
1470 | 0 | return; |
1471 | 150 | Bldr.takeNodes(Pred); |
1472 | | |
1473 | 150 | VisitCXXDestructor(DTy, ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts); |
1474 | 150 | } |
1475 | | |
1476 | | void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, |
1477 | 129 | ExplodedNode *Pred, ExplodedNodeSet &Dst) { |
1478 | 129 | const LocationContext *LCtx = Pred->getLocationContext(); |
1479 | | |
1480 | 129 | const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); |
1481 | 129 | Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor, |
1482 | 129 | LCtx->getStackFrame()); |
1483 | 129 | SVal ThisVal = Pred->getState()->getSVal(ThisPtr); |
1484 | | |
1485 | | // Create the base object region. |
1486 | 129 | const CXXBaseSpecifier *Base = D.getBaseSpecifier(); |
1487 | 129 | QualType BaseTy = Base->getType(); |
1488 | 129 | SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy, |
1489 | 129 | Base->isVirtual()); |
1490 | | |
1491 | 129 | EvalCallOptions CallOpts; |
1492 | 129 | VisitCXXDestructor(BaseTy, BaseVal.getAsRegion(), CurDtor->getBody(), |
1493 | 129 | /*IsBase=*/true, Pred, Dst, CallOpts); |
1494 | 129 | } |
1495 | | |
1496 | | void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, |
1497 | 155 | ExplodedNode *Pred, ExplodedNodeSet &Dst) { |
1498 | 155 | const auto *DtorDecl = D.getDestructorDecl(getContext()); |
1499 | 155 | const FieldDecl *Member = D.getFieldDecl(); |
1500 | 155 | QualType T = Member->getType(); |
1501 | 155 | ProgramStateRef State = Pred->getState(); |
1502 | 155 | const LocationContext *LCtx = Pred->getLocationContext(); |
1503 | | |
1504 | 155 | const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); |
1505 | 155 | Loc ThisStorageLoc = |
1506 | 155 | getSValBuilder().getCXXThis(CurDtor, LCtx->getStackFrame()); |
1507 | 155 | Loc ThisLoc = State->getSVal(ThisStorageLoc).castAs<Loc>(); |
1508 | 155 | SVal FieldVal = State->getLValue(Member, ThisLoc); |
1509 | | |
1510 | 155 | unsigned Idx = 0; |
1511 | 155 | if (isa<ArrayType>(T)) { |
1512 | 96 | SVal ElementCount; |
1513 | 96 | std::tie(State, Idx) = prepareStateForArrayDestruction( |
1514 | 96 | State, FieldVal.getAsRegion(), T, LCtx, &ElementCount); |
1515 | | |
1516 | 96 | if (ElementCount.isConstant()) { |
1517 | 96 | uint64_t ArrayLength = ElementCount.getAsInteger()->getLimitedValue(); |
1518 | 96 | assert(ArrayLength && |
1519 | 96 | "A member dtor for a 0 length array shouldn't be triggered!"); |
1520 | | |
1521 | | // Still handle this case if we don't have assertions enabled. |
1522 | 96 | if (!ArrayLength) { |
1523 | 0 | static SimpleProgramPointTag PT( |
1524 | 0 | "ExprEngine", "Skipping member 0 length array destruction, which " |
1525 | 0 | "shouldn't be in the CFG."); |
1526 | 0 | PostImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, |
1527 | 0 | getCFGElementRef(), &PT); |
1528 | 0 | NodeBuilder Bldr(Pred, Dst, *currBldrCtx); |
1529 | 0 | Bldr.generateSink(PP, Pred->getState(), Pred); |
1530 | 0 | return; |
1531 | 0 | } |
1532 | 96 | } |
1533 | 96 | } |
1534 | | |
1535 | 155 | EvalCallOptions CallOpts; |
1536 | 155 | FieldVal = |
1537 | 155 | makeElementRegion(State, FieldVal, T, CallOpts.IsArrayCtorOrDtor, Idx); |
1538 | | |
1539 | 155 | NodeBuilder Bldr(Pred, Dst, getBuilderContext()); |
1540 | | |
1541 | 155 | static SimpleProgramPointTag PT("ExprEngine", |
1542 | 155 | "Prepare for object destruction"); |
1543 | 155 | PreImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, getCFGElementRef(), |
1544 | 155 | &PT); |
1545 | 155 | Pred = Bldr.generateNode(PP, State, Pred); |
1546 | | |
1547 | 155 | if (!Pred) |
1548 | 0 | return; |
1549 | 155 | Bldr.takeNodes(Pred); |
1550 | | |
1551 | 155 | VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(), |
1552 | 155 | /*IsBase=*/false, Pred, Dst, CallOpts); |
1553 | 155 | } |
1554 | | |
1555 | | void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, |
1556 | | ExplodedNode *Pred, |
1557 | 813 | ExplodedNodeSet &Dst) { |
1558 | 813 | const CXXBindTemporaryExpr *BTE = D.getBindTemporaryExpr(); |
1559 | 813 | ProgramStateRef State = Pred->getState(); |
1560 | 813 | const LocationContext *LC = Pred->getLocationContext(); |
1561 | 813 | const MemRegion *MR = nullptr; |
1562 | | |
1563 | 813 | if (std::optional<SVal> V = getObjectUnderConstruction( |
1564 | 813 | State, D.getBindTemporaryExpr(), Pred->getLocationContext())) { |
1565 | | // FIXME: Currently we insert temporary destructors for default parameters, |
1566 | | // but we don't insert the constructors, so the entry in |
1567 | | // ObjectsUnderConstruction may be missing. |
1568 | 756 | State = finishObjectConstruction(State, D.getBindTemporaryExpr(), |
1569 | 756 | Pred->getLocationContext()); |
1570 | 756 | MR = V->getAsRegion(); |
1571 | 756 | } |
1572 | | |
1573 | | // If copy elision has occurred, and the constructor corresponding to the |
1574 | | // destructor was elided, we need to skip the destructor as well. |
1575 | 813 | if (isDestructorElided(State, BTE, LC)) { |
1576 | 162 | State = cleanupElidedDestructor(State, BTE, LC); |
1577 | 162 | NodeBuilder Bldr(Pred, Dst, *currBldrCtx); |
1578 | 162 | PostImplicitCall PP(D.getDestructorDecl(getContext()), |
1579 | 162 | D.getBindTemporaryExpr()->getBeginLoc(), |
1580 | 162 | Pred->getLocationContext(), getCFGElementRef()); |
1581 | 162 | Bldr.generateNode(PP, State, Pred); |
1582 | 162 | return; |
1583 | 162 | } |
1584 | | |
1585 | 651 | ExplodedNodeSet CleanDtorState; |
1586 | 651 | StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx); |
1587 | 651 | StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State); |
1588 | | |
1589 | 651 | QualType T = D.getBindTemporaryExpr()->getSubExpr()->getType(); |
1590 | | // FIXME: Currently CleanDtorState can be empty here due to temporaries being |
1591 | | // bound to default parameters. |
1592 | 651 | assert(CleanDtorState.size() <= 1); |
1593 | 651 | ExplodedNode *CleanPred = |
1594 | 651 | CleanDtorState.empty() ? Pred0 : *CleanDtorState.begin(); |
1595 | | |
1596 | 651 | EvalCallOptions CallOpts; |
1597 | 651 | CallOpts.IsTemporaryCtorOrDtor = true; |
1598 | 651 | if (!MR) { |
1599 | | // FIXME: If we have no MR, we still need to unwrap the array to avoid |
1600 | | // destroying the whole array at once. |
1601 | | // |
1602 | | // For this case there is no universal solution as there is no way to |
1603 | | // directly create an array of temporary objects. There are some expressions |
1604 | | // however which can create temporary objects and have an array type. |
1605 | | // |
1606 | | // E.g.: std::initializer_list<S>{S(), S()}; |
1607 | | // |
1608 | | // The expression above has a type of 'const struct S[2]' but it's a single |
1609 | | // 'std::initializer_list<>'. The destructors of the 2 temporary 'S()' |
1610 | | // objects will be called anyway, because they are 2 separate objects in 2 |
1611 | | // separate clusters, i.e.: not an array. |
1612 | | // |
1613 | | // Now the 'std::initializer_list<>' is not an array either even though it |
1614 | | // has the type of an array. The point is, we only want to invoke the |
1615 | | // destructor for the initializer list once not twice or so. |
1616 | 74 | while (const ArrayType *AT = getContext().getAsArrayType(T)) { |
1617 | 2 | T = AT->getElementType(); |
1618 | | |
1619 | | // FIXME: Enable this flag once we handle this case properly. |
1620 | | // CallOpts.IsArrayCtorOrDtor = true; |
1621 | 2 | } |
1622 | 579 | } else { |
1623 | | // FIXME: We'd eventually need to makeElementRegion() trick here, |
1624 | | // but for now we don't have the respective construction contexts, |
1625 | | // so MR would always be null in this case. Do nothing for now. |
1626 | 579 | } |
1627 | 651 | VisitCXXDestructor(T, MR, D.getBindTemporaryExpr(), |
1628 | 651 | /*IsBase=*/false, CleanPred, Dst, CallOpts); |
1629 | 651 | } |
1630 | | |
1631 | | void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, |
1632 | | NodeBuilderContext &BldCtx, |
1633 | | ExplodedNode *Pred, |
1634 | | ExplodedNodeSet &Dst, |
1635 | | const CFGBlock *DstT, |
1636 | 601 | const CFGBlock *DstF) { |
1637 | 601 | BranchNodeBuilder TempDtorBuilder(Pred, Dst, BldCtx, DstT, DstF); |
1638 | 601 | ProgramStateRef State = Pred->getState(); |
1639 | 601 | const LocationContext *LC = Pred->getLocationContext(); |
1640 | 601 | if (getObjectUnderConstruction(State, BTE, LC)) { |
1641 | 399 | TempDtorBuilder.markInfeasible(false); |
1642 | 399 | TempDtorBuilder.generateNode(State, true, Pred); |
1643 | 399 | } else { |
1644 | 202 | TempDtorBuilder.markInfeasible(true); |
1645 | 202 | TempDtorBuilder.generateNode(State, false, Pred); |
1646 | 202 | } |
1647 | 601 | } |
1648 | | |
1649 | | void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, |
1650 | | ExplodedNodeSet &PreVisit, |
1651 | 1.19k | ExplodedNodeSet &Dst) { |
1652 | | // This is a fallback solution in case we didn't have a construction |
1653 | | // context when we were constructing the temporary. Otherwise the map should |
1654 | | // have been populated there. |
1655 | 1.19k | if (!getAnalysisManager().options.ShouldIncludeTemporaryDtorsInCFG) { |
1656 | | // In case we don't have temporary destructors in the CFG, do not mark |
1657 | | // the initialization - we would otherwise never clean it up. |
1658 | 244 | Dst = PreVisit; |
1659 | 244 | return; |
1660 | 244 | } |
1661 | 955 | StmtNodeBuilder StmtBldr(PreVisit, Dst, *currBldrCtx); |
1662 | 955 | for (ExplodedNode *Node : PreVisit) { |
1663 | 955 | ProgramStateRef State = Node->getState(); |
1664 | 955 | const LocationContext *LC = Node->getLocationContext(); |
1665 | 955 | if (!getObjectUnderConstruction(State, BTE, LC)) { |
1666 | | // FIXME: Currently the state might also already contain the marker due to |
1667 | | // incorrect handling of temporaries bound to default parameters; for |
1668 | | // those, we currently skip the CXXBindTemporaryExpr but rely on adding |
1669 | | // temporary destructor nodes. |
1670 | 349 | State = addObjectUnderConstruction(State, BTE, LC, UnknownVal()); |
1671 | 349 | } |
1672 | 955 | StmtBldr.generateNode(BTE, Node, State); |
1673 | 955 | } |
1674 | 955 | } |
1675 | | |
1676 | | ProgramStateRef ExprEngine::escapeValues(ProgramStateRef State, |
1677 | | ArrayRef<SVal> Vs, |
1678 | | PointerEscapeKind K, |
1679 | 25.4k | const CallEvent *Call) const { |
1680 | 25.4k | class CollectReachableSymbolsCallback final : public SymbolVisitor { |
1681 | 25.4k | InvalidatedSymbols &Symbols; |
1682 | | |
1683 | 25.4k | public: |
1684 | 25.4k | explicit CollectReachableSymbolsCallback(InvalidatedSymbols &Symbols) |
1685 | 25.4k | : Symbols(Symbols) {} |
1686 | | |
1687 | 25.4k | const InvalidatedSymbols &getSymbols() const { return Symbols; } |
1688 | | |
1689 | 48.0k | bool VisitSymbol(SymbolRef Sym) override { |
1690 | 48.0k | Symbols.insert(Sym); |
1691 | 48.0k | return true; |
1692 | 48.0k | } |
1693 | 25.4k | }; |
1694 | 25.4k | InvalidatedSymbols Symbols; |
1695 | 25.4k | CollectReachableSymbolsCallback CallBack(Symbols); |
1696 | 25.4k | for (SVal V : Vs) |
1697 | 25.6k | State->scanReachableSymbols(V, CallBack); |
1698 | | |
1699 | 25.4k | return getCheckerManager().runCheckersForPointerEscape( |
1700 | 25.4k | State, CallBack.getSymbols(), Call, K, nullptr); |
1701 | 25.4k | } |
1702 | | |
1703 | | void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, |
1704 | 1.33M | ExplodedNodeSet &DstTop) { |
1705 | 1.33M | PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), |
1706 | 1.33M | S->getBeginLoc(), "Error evaluating statement"); |
1707 | 1.33M | ExplodedNodeSet Dst; |
1708 | 1.33M | StmtNodeBuilder Bldr(Pred, DstTop, *currBldrCtx); |
1709 | | |
1710 | 1.33M | assert(!isa<Expr>(S) || S == cast<Expr>(S)->IgnoreParens()); |
1711 | | |
1712 | 1.33M | switch (S->getStmtClass()) { |
1713 | | // C++, OpenMP and ARC stuff we don't support yet. |
1714 | 0 | case Stmt::CXXDependentScopeMemberExprClass: |
1715 | 0 | case Stmt::CXXTryStmtClass: |
1716 | 0 | case Stmt::CXXTypeidExprClass: |
1717 | 0 | case Stmt::CXXUuidofExprClass: |
1718 | 0 | case Stmt::CXXFoldExprClass: |
1719 | 0 | case Stmt::MSPropertyRefExprClass: |
1720 | 0 | case Stmt::MSPropertySubscriptExprClass: |
1721 | 0 | case Stmt::CXXUnresolvedConstructExprClass: |
1722 | 0 | case Stmt::DependentScopeDeclRefExprClass: |
1723 | 0 | case Stmt::ArrayTypeTraitExprClass: |
1724 | 0 | case Stmt::ExpressionTraitExprClass: |
1725 | 0 | case Stmt::UnresolvedLookupExprClass: |
1726 | 0 | case Stmt::UnresolvedMemberExprClass: |
1727 | 0 | case Stmt::TypoExprClass: |
1728 | 0 | case Stmt::RecoveryExprClass: |
1729 | 0 | case Stmt::CXXNoexceptExprClass: |
1730 | 0 | case Stmt::PackExpansionExprClass: |
1731 | 0 | case Stmt::SubstNonTypeTemplateParmPackExprClass: |
1732 | 0 | case Stmt::FunctionParmPackExprClass: |
1733 | 0 | case Stmt::CoroutineBodyStmtClass: |
1734 | 0 | case Stmt::CoawaitExprClass: |
1735 | 0 | case Stmt::DependentCoawaitExprClass: |
1736 | 0 | case Stmt::CoreturnStmtClass: |
1737 | 0 | case Stmt::CoyieldExprClass: |
1738 | 0 | case Stmt::SEHTryStmtClass: |
1739 | 0 | case Stmt::SEHExceptStmtClass: |
1740 | 0 | case Stmt::SEHLeaveStmtClass: |
1741 | 0 | case Stmt::SEHFinallyStmtClass: |
1742 | 0 | case Stmt::OMPCanonicalLoopClass: |
1743 | 1 | case Stmt::OMPParallelDirectiveClass: |
1744 | 1 | case Stmt::OMPSimdDirectiveClass: |
1745 | 1 | case Stmt::OMPForDirectiveClass: |
1746 | 1 | case Stmt::OMPForSimdDirectiveClass: |
1747 | 1 | case Stmt::OMPSectionsDirectiveClass: |
1748 | 1 | case Stmt::OMPSectionDirectiveClass: |
1749 | 1 | case Stmt::OMPScopeDirectiveClass: |
1750 | 1 | case Stmt::OMPSingleDirectiveClass: |
1751 | 1 | case Stmt::OMPMasterDirectiveClass: |
1752 | 1 | case Stmt::OMPCriticalDirectiveClass: |
1753 | 1 | case Stmt::OMPParallelForDirectiveClass: |
1754 | 1 | case Stmt::OMPParallelForSimdDirectiveClass: |
1755 | 1 | case Stmt::OMPParallelSectionsDirectiveClass: |
1756 | 1 | case Stmt::OMPParallelMasterDirectiveClass: |
1757 | 1 | case Stmt::OMPParallelMaskedDirectiveClass: |
1758 | 1 | case Stmt::OMPTaskDirectiveClass: |
1759 | 1 | case Stmt::OMPTaskyieldDirectiveClass: |
1760 | 1 | case Stmt::OMPBarrierDirectiveClass: |
1761 | 1 | case Stmt::OMPTaskwaitDirectiveClass: |
1762 | 1 | case Stmt::OMPErrorDirectiveClass: |
1763 | 1 | case Stmt::OMPTaskgroupDirectiveClass: |
1764 | 1 | case Stmt::OMPFlushDirectiveClass: |
1765 | 1 | case Stmt::OMPDepobjDirectiveClass: |
1766 | 1 | case Stmt::OMPScanDirectiveClass: |
1767 | 1 | case Stmt::OMPOrderedDirectiveClass: |
1768 | 1 | case Stmt::OMPAtomicDirectiveClass: |
1769 | 1 | case Stmt::OMPTargetDirectiveClass: |
1770 | 1 | case Stmt::OMPTargetDataDirectiveClass: |
1771 | 1 | case Stmt::OMPTargetEnterDataDirectiveClass: |
1772 | 1 | case Stmt::OMPTargetExitDataDirectiveClass: |
1773 | 1 | case Stmt::OMPTargetParallelDirectiveClass: |
1774 | 1 | case Stmt::OMPTargetParallelForDirectiveClass: |
1775 | 1 | case Stmt::OMPTargetUpdateDirectiveClass: |
1776 | 1 | case Stmt::OMPTeamsDirectiveClass: |
1777 | 1 | case Stmt::OMPCancellationPointDirectiveClass: |
1778 | 1 | case Stmt::OMPCancelDirectiveClass: |
1779 | 1 | case Stmt::OMPTaskLoopDirectiveClass: |
1780 | 1 | case Stmt::OMPTaskLoopSimdDirectiveClass: |
1781 | 1 | case Stmt::OMPMasterTaskLoopDirectiveClass: |
1782 | 1 | case Stmt::OMPMaskedTaskLoopDirectiveClass: |
1783 | 1 | case Stmt::OMPMasterTaskLoopSimdDirectiveClass: |
1784 | 1 | case Stmt::OMPMaskedTaskLoopSimdDirectiveClass: |
1785 | 1 | case Stmt::OMPParallelMasterTaskLoopDirectiveClass: |
1786 | 1 | case Stmt::OMPParallelMaskedTaskLoopDirectiveClass: |
1787 | 1 | case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass: |
1788 | 1 | case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass: |
1789 | 1 | case Stmt::OMPDistributeDirectiveClass: |
1790 | 1 | case Stmt::OMPDistributeParallelForDirectiveClass: |
1791 | 1 | case Stmt::OMPDistributeParallelForSimdDirectiveClass: |
1792 | 1 | case Stmt::OMPDistributeSimdDirectiveClass: |
1793 | 1 | case Stmt::OMPTargetParallelForSimdDirectiveClass: |
1794 | 1 | case Stmt::OMPTargetSimdDirectiveClass: |
1795 | 1 | case Stmt::OMPTeamsDistributeDirectiveClass: |
1796 | 1 | case Stmt::OMPTeamsDistributeSimdDirectiveClass: |
1797 | 1 | case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: |
1798 | 1 | case Stmt::OMPTeamsDistributeParallelForDirectiveClass: |
1799 | 1 | case Stmt::OMPTargetTeamsDirectiveClass: |
1800 | 1 | case Stmt::OMPTargetTeamsDistributeDirectiveClass: |
1801 | 1 | case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: |
1802 | 1 | case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: |
1803 | 1 | case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: |
1804 | 1 | case Stmt::OMPTileDirectiveClass: |
1805 | 1 | case Stmt::OMPInteropDirectiveClass: |
1806 | 1 | case Stmt::OMPDispatchDirectiveClass: |
1807 | 1 | case Stmt::OMPMaskedDirectiveClass: |
1808 | 1 | case Stmt::OMPGenericLoopDirectiveClass: |
1809 | 1 | case Stmt::OMPTeamsGenericLoopDirectiveClass: |
1810 | 1 | case Stmt::OMPTargetTeamsGenericLoopDirectiveClass: |
1811 | 1 | case Stmt::OMPParallelGenericLoopDirectiveClass: |
1812 | 1 | case Stmt::OMPTargetParallelGenericLoopDirectiveClass: |
1813 | 1 | case Stmt::CapturedStmtClass: |
1814 | 1 | case Stmt::OMPUnrollDirectiveClass: |
1815 | 1 | case Stmt::OMPMetaDirectiveClass: { |
1816 | 1 | const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); |
1817 | 1 | Engine.addAbortedBlock(node, currBldrCtx->getBlock()); |
1818 | 1 | break; |
1819 | 1 | } |
1820 | | |
1821 | 0 | case Stmt::ParenExprClass: |
1822 | 0 | llvm_unreachable("ParenExprs already handled."); |
1823 | 0 | case Stmt::GenericSelectionExprClass: |
1824 | 0 | llvm_unreachable("GenericSelectionExprs already handled."); |
1825 | | // Cases that should never be evaluated simply because they shouldn't |
1826 | | // appear in the CFG. |
1827 | 0 | case Stmt::BreakStmtClass: |
1828 | 0 | case Stmt::CaseStmtClass: |
1829 | 0 | case Stmt::CompoundStmtClass: |
1830 | 0 | case Stmt::ContinueStmtClass: |
1831 | 0 | case Stmt::CXXForRangeStmtClass: |
1832 | 0 | case Stmt::DefaultStmtClass: |
1833 | 0 | case Stmt::DoStmtClass: |
1834 | 0 | case Stmt::ForStmtClass: |
1835 | 0 | case Stmt::GotoStmtClass: |
1836 | 0 | case Stmt::IfStmtClass: |
1837 | 0 | case Stmt::IndirectGotoStmtClass: |
1838 | 0 | case Stmt::LabelStmtClass: |
1839 | 0 | case Stmt::NoStmtClass: |
1840 | 0 | case Stmt::NullStmtClass: |
1841 | 0 | case Stmt::SwitchStmtClass: |
1842 | 0 | case Stmt::WhileStmtClass: |
1843 | 0 | case Expr::MSDependentExistsStmtClass: |
1844 | 0 | llvm_unreachable("Stmt should not be in analyzer evaluation loop"); |
1845 | 0 | case Stmt::ImplicitValueInitExprClass: |
1846 | | // These nodes are shared in the CFG and would case caching out. |
1847 | | // Moreover, no additional evaluation required for them, the |
1848 | | // analyzer can reconstruct these values from the AST. |
1849 | 0 | llvm_unreachable("Should be pruned from CFG"); |
1850 | |
|
1851 | 0 | case Stmt::ObjCSubscriptRefExprClass: |
1852 | 0 | case Stmt::ObjCPropertyRefExprClass: |
1853 | 0 | llvm_unreachable("These are handled by PseudoObjectExpr"); |
1854 | |
|
1855 | 61 | case Stmt::GNUNullExprClass: { |
1856 | | // GNU __null is a pointer-width integer, not an actual pointer. |
1857 | 61 | ProgramStateRef state = Pred->getState(); |
1858 | 61 | state = state->BindExpr( |
1859 | 61 | S, Pred->getLocationContext(), |
1860 | 61 | svalBuilder.makeIntValWithWidth(getContext().VoidPtrTy, 0)); |
1861 | 61 | Bldr.generateNode(S, Pred, state); |
1862 | 61 | break; |
1863 | 0 | } |
1864 | | |
1865 | 19 | case Stmt::ObjCAtSynchronizedStmtClass: |
1866 | 19 | Bldr.takeNodes(Pred); |
1867 | 19 | VisitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(S), Pred, Dst); |
1868 | 19 | Bldr.addNodes(Dst); |
1869 | 19 | break; |
1870 | | |
1871 | 0 | case Expr::ConstantExprClass: |
1872 | 0 | case Stmt::ExprWithCleanupsClass: |
1873 | | // Handled due to fully linearised CFG. |
1874 | 0 | break; |
1875 | | |
1876 | 1.19k | case Stmt::CXXBindTemporaryExprClass: { |
1877 | 1.19k | Bldr.takeNodes(Pred); |
1878 | 1.19k | ExplodedNodeSet PreVisit; |
1879 | 1.19k | getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); |
1880 | 1.19k | ExplodedNodeSet Next; |
1881 | 1.19k | VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), PreVisit, Next); |
1882 | 1.19k | getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this); |
1883 | 1.19k | Bldr.addNodes(Dst); |
1884 | 1.19k | break; |
1885 | 0 | } |
1886 | | |
1887 | 103 | case Stmt::ArrayInitLoopExprClass: |
1888 | 103 | Bldr.takeNodes(Pred); |
1889 | 103 | VisitArrayInitLoopExpr(cast<ArrayInitLoopExpr>(S), Pred, Dst); |
1890 | 103 | Bldr.addNodes(Dst); |
1891 | 103 | break; |
1892 | | // Cases not handled yet; but will handle some day. |
1893 | 0 | case Stmt::DesignatedInitExprClass: |
1894 | 0 | case Stmt::DesignatedInitUpdateExprClass: |
1895 | 105 | case Stmt::ArrayInitIndexExprClass: |
1896 | 105 | case Stmt::ExtVectorElementExprClass: |
1897 | 125 | case Stmt::ImaginaryLiteralClass: |
1898 | 125 | case Stmt::ObjCAtCatchStmtClass: |
1899 | 125 | case Stmt::ObjCAtFinallyStmtClass: |
1900 | 125 | case Stmt::ObjCAtTryStmtClass: |
1901 | 125 | case Stmt::ObjCAutoreleasePoolStmtClass: |
1902 | 126 | case Stmt::ObjCEncodeExprClass: |
1903 | 128 | case Stmt::ObjCIsaExprClass: |
1904 | 128 | case Stmt::ObjCProtocolExprClass: |
1905 | 135 | case Stmt::ObjCSelectorExprClass: |
1906 | 135 | case Stmt::ParenListExprClass: |
1907 | 135 | case Stmt::ShuffleVectorExprClass: |
1908 | 135 | case Stmt::ConvertVectorExprClass: |
1909 | 194 | case Stmt::VAArgExprClass: |
1910 | 194 | case Stmt::CUDAKernelCallExprClass: |
1911 | 194 | case Stmt::OpaqueValueExprClass: |
1912 | 194 | case Stmt::AsTypeExprClass: |
1913 | 194 | case Stmt::ConceptSpecializationExprClass: |
1914 | 194 | case Stmt::CXXRewrittenBinaryOperatorClass: |
1915 | 194 | case Stmt::RequiresExprClass: |
1916 | 194 | case Expr::CXXParenListInitExprClass: |
1917 | | // Fall through. |
1918 | | |
1919 | | // Cases we intentionally don't evaluate, since they don't need |
1920 | | // to be explicitly evaluated. |
1921 | 762 | case Stmt::PredefinedExprClass: |
1922 | 855 | case Stmt::AddrLabelExprClass: |
1923 | 856 | case Stmt::AttributedStmtClass: |
1924 | 80.8k | case Stmt::IntegerLiteralClass: |
1925 | 80.8k | case Stmt::FixedPointLiteralClass: |
1926 | 81.6k | case Stmt::CharacterLiteralClass: |
1927 | 81.7k | case Stmt::CXXScalarValueInitExprClass: |
1928 | 82.6k | case Stmt::CXXBoolLiteralExprClass: |
1929 | 82.9k | case Stmt::ObjCBoolLiteralExprClass: |
1930 | 82.9k | case Stmt::ObjCAvailabilityCheckExprClass: |
1931 | 83.5k | case Stmt::FloatingLiteralClass: |
1932 | 83.5k | case Stmt::NoInitExprClass: |
1933 | 83.5k | case Stmt::SizeOfPackExprClass: |
1934 | 89.4k | case Stmt::StringLiteralClass: |
1935 | 89.4k | case Stmt::SourceLocExprClass: |
1936 | 89.7k | case Stmt::ObjCStringLiteralClass: |
1937 | 89.7k | case Stmt::CXXPseudoDestructorExprClass: |
1938 | 89.8k | case Stmt::SubstNonTypeTemplateParmExprClass: |
1939 | 90.2k | case Stmt::CXXNullPtrLiteralExprClass: |
1940 | 90.2k | case Stmt::OMPArraySectionExprClass: |
1941 | 90.2k | case Stmt::OMPArrayShapingExprClass: |
1942 | 90.2k | case Stmt::OMPIteratorExprClass: |
1943 | 90.2k | case Stmt::SYCLUniqueStableNameExprClass: |
1944 | 90.2k | case Stmt::TypeTraitExprClass: { |
1945 | 90.2k | Bldr.takeNodes(Pred); |
1946 | 90.2k | ExplodedNodeSet preVisit; |
1947 | 90.2k | getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); |
1948 | 90.2k | getCheckerManager().runCheckersForPostStmt(Dst, preVisit, S, *this); |
1949 | 90.2k | Bldr.addNodes(Dst); |
1950 | 90.2k | break; |
1951 | 90.2k | } |
1952 | | |
1953 | 3.14k | case Stmt::CXXDefaultArgExprClass: |
1954 | 3.44k | case Stmt::CXXDefaultInitExprClass: { |
1955 | 3.44k | Bldr.takeNodes(Pred); |
1956 | 3.44k | ExplodedNodeSet PreVisit; |
1957 | 3.44k | getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); |
1958 | | |
1959 | 3.44k | ExplodedNodeSet Tmp; |
1960 | 3.44k | StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx); |
1961 | | |
1962 | 3.44k | const Expr *ArgE; |
1963 | 3.44k | if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S)) |
1964 | 3.14k | ArgE = DefE->getExpr(); |
1965 | 303 | else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S)) |
1966 | 303 | ArgE = DefE->getExpr(); |
1967 | 0 | else |
1968 | 0 | llvm_unreachable("unknown constant wrapper kind"); |
1969 | | |
1970 | 3.44k | bool IsTemporary = false; |
1971 | 3.44k | if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) { |
1972 | 64 | ArgE = MTE->getSubExpr(); |
1973 | 64 | IsTemporary = true; |
1974 | 64 | } |
1975 | | |
1976 | 3.44k | std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE); |
1977 | 3.44k | if (!ConstantVal) |
1978 | 1.53k | ConstantVal = UnknownVal(); |
1979 | | |
1980 | 3.44k | const LocationContext *LCtx = Pred->getLocationContext(); |
1981 | 3.44k | for (const auto I : PreVisit) { |
1982 | 3.44k | ProgramStateRef State = I->getState(); |
1983 | 3.44k | State = State->BindExpr(S, LCtx, *ConstantVal); |
1984 | 3.44k | if (IsTemporary) |
1985 | 64 | State = createTemporaryRegionIfNeeded(State, LCtx, |
1986 | 64 | cast<Expr>(S), |
1987 | 64 | cast<Expr>(S)); |
1988 | 3.44k | Bldr2.generateNode(S, I, State); |
1989 | 3.44k | } |
1990 | | |
1991 | 3.44k | getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); |
1992 | 3.44k | Bldr.addNodes(Dst); |
1993 | 3.44k | break; |
1994 | 3.14k | } |
1995 | | |
1996 | | // Cases we evaluate as opaque expressions, conjuring a symbol. |
1997 | 29 | case Stmt::CXXStdInitializerListExprClass: |
1998 | 71 | case Expr::ObjCArrayLiteralClass: |
1999 | 89 | case Expr::ObjCDictionaryLiteralClass: |
2000 | 124 | case Expr::ObjCBoxedExprClass: { |
2001 | 124 | Bldr.takeNodes(Pred); |
2002 | | |
2003 | 124 | ExplodedNodeSet preVisit; |
2004 | 124 | getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); |
2005 | | |
2006 | 124 | ExplodedNodeSet Tmp; |
2007 | 124 | StmtNodeBuilder Bldr2(preVisit, Tmp, *currBldrCtx); |
2008 | | |
2009 | 124 | const auto *Ex = cast<Expr>(S); |
2010 | 124 | QualType resultType = Ex->getType(); |
2011 | | |
2012 | 124 | for (const auto N : preVisit) { |
2013 | 124 | const LocationContext *LCtx = N->getLocationContext(); |
2014 | 124 | SVal result = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx, |
2015 | 124 | resultType, |
2016 | 124 | currBldrCtx->blockCount()); |
2017 | 124 | ProgramStateRef State = N->getState()->BindExpr(Ex, LCtx, result); |
2018 | | |
2019 | | // Escape pointers passed into the list, unless it's an ObjC boxed |
2020 | | // expression which is not a boxable C structure. |
2021 | 124 | if (!(isa<ObjCBoxedExpr>(Ex) && |
2022 | 124 | !cast<ObjCBoxedExpr>(Ex)->getSubExpr() |
2023 | 35 | ->getType()->isRecordType())) |
2024 | 116 | for (auto Child : Ex->children())91 { |
2025 | 116 | assert(Child); |
2026 | 116 | SVal Val = State->getSVal(Child, LCtx); |
2027 | 116 | State = escapeValues(State, Val, PSK_EscapeOther); |
2028 | 116 | } |
2029 | | |
2030 | 124 | Bldr2.generateNode(S, N, State); |
2031 | 124 | } |
2032 | | |
2033 | 124 | getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); |
2034 | 124 | Bldr.addNodes(Dst); |
2035 | 124 | break; |
2036 | 124 | } |
2037 | | |
2038 | 17.4k | case Stmt::ArraySubscriptExprClass: |
2039 | 17.4k | Bldr.takeNodes(Pred); |
2040 | 17.4k | VisitArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst); |
2041 | 17.4k | Bldr.addNodes(Dst); |
2042 | 17.4k | break; |
2043 | | |
2044 | 0 | case Stmt::MatrixSubscriptExprClass: |
2045 | 0 | llvm_unreachable("Support for MatrixSubscriptExpr is not implemented."); |
2046 | 0 | break; |
2047 | | |
2048 | 6 | case Stmt::GCCAsmStmtClass: |
2049 | 6 | Bldr.takeNodes(Pred); |
2050 | 6 | VisitGCCAsmStmt(cast<GCCAsmStmt>(S), Pred, Dst); |
2051 | 6 | Bldr.addNodes(Dst); |
2052 | 6 | break; |
2053 | | |
2054 | 0 | case Stmt::MSAsmStmtClass: |
2055 | 0 | Bldr.takeNodes(Pred); |
2056 | 0 | VisitMSAsmStmt(cast<MSAsmStmt>(S), Pred, Dst); |
2057 | 0 | Bldr.addNodes(Dst); |
2058 | 0 | break; |
2059 | | |
2060 | 398 | case Stmt::BlockExprClass: |
2061 | 398 | Bldr.takeNodes(Pred); |
2062 | 398 | VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst); |
2063 | 398 | Bldr.addNodes(Dst); |
2064 | 398 | break; |
2065 | | |
2066 | 539 | case Stmt::LambdaExprClass: |
2067 | 539 | if (AMgr.options.ShouldInlineLambdas) { |
2068 | 538 | Bldr.takeNodes(Pred); |
2069 | 538 | VisitLambdaExpr(cast<LambdaExpr>(S), Pred, Dst); |
2070 | 538 | Bldr.addNodes(Dst); |
2071 | 538 | } else { |
2072 | 1 | const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); |
2073 | 1 | Engine.addAbortedBlock(node, currBldrCtx->getBlock()); |
2074 | 1 | } |
2075 | 539 | break; |
2076 | | |
2077 | 133k | case Stmt::BinaryOperatorClass: { |
2078 | 133k | const auto *B = cast<BinaryOperator>(S); |
2079 | 133k | if (B->isLogicalOp()) { |
2080 | 1.24k | Bldr.takeNodes(Pred); |
2081 | 1.24k | VisitLogicalExpr(B, Pred, Dst); |
2082 | 1.24k | Bldr.addNodes(Dst); |
2083 | 1.24k | break; |
2084 | 1.24k | } |
2085 | 132k | else if (B->getOpcode() == BO_Comma) { |
2086 | 320 | ProgramStateRef state = Pred->getState(); |
2087 | 320 | Bldr.generateNode(B, Pred, |
2088 | 320 | state->BindExpr(B, Pred->getLocationContext(), |
2089 | 320 | state->getSVal(B->getRHS(), |
2090 | 320 | Pred->getLocationContext()))); |
2091 | 320 | break; |
2092 | 320 | } |
2093 | | |
2094 | 132k | Bldr.takeNodes(Pred); |
2095 | | |
2096 | 132k | if (AMgr.options.ShouldEagerlyAssume && |
2097 | 132k | (105k B->isRelationalOp()105k || B->isEqualityOp()96.6k )) { |
2098 | 14.0k | ExplodedNodeSet Tmp; |
2099 | 14.0k | VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Tmp); |
2100 | 14.0k | evalEagerlyAssumeBinOpBifurcation(Dst, Tmp, cast<Expr>(S)); |
2101 | 14.0k | } |
2102 | 118k | else |
2103 | 118k | VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); |
2104 | | |
2105 | 132k | Bldr.addNodes(Dst); |
2106 | 132k | break; |
2107 | 133k | } |
2108 | | |
2109 | 3.85k | case Stmt::CXXOperatorCallExprClass: { |
2110 | 3.85k | const auto *OCE = cast<CXXOperatorCallExpr>(S); |
2111 | | |
2112 | | // For instance method operators, make sure the 'this' argument has a |
2113 | | // valid region. |
2114 | 3.85k | const Decl *Callee = OCE->getCalleeDecl(); |
2115 | 3.85k | if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) { |
2116 | 3.76k | if (MD->isInstance()) { |
2117 | 3.76k | ProgramStateRef State = Pred->getState(); |
2118 | 3.76k | const LocationContext *LCtx = Pred->getLocationContext(); |
2119 | 3.76k | ProgramStateRef NewState = |
2120 | 3.76k | createTemporaryRegionIfNeeded(State, LCtx, OCE->getArg(0)); |
2121 | 3.76k | if (NewState != State) { |
2122 | 0 | Pred = Bldr.generateNode(OCE, Pred, NewState, /*tag=*/nullptr, |
2123 | 0 | ProgramPoint::PreStmtKind); |
2124 | | // Did we cache out? |
2125 | 0 | if (!Pred) |
2126 | 0 | break; |
2127 | 0 | } |
2128 | 3.76k | } |
2129 | 3.76k | } |
2130 | 3.85k | [[fallthrough]]; |
2131 | 3.85k | } |
2132 | | |
2133 | 72.4k | case Stmt::CallExprClass: |
2134 | 80.7k | case Stmt::CXXMemberCallExprClass: |
2135 | 80.7k | case Stmt::UserDefinedLiteralClass: |
2136 | 80.7k | Bldr.takeNodes(Pred); |
2137 | 80.7k | VisitCallExpr(cast<CallExpr>(S), Pred, Dst); |
2138 | 80.7k | Bldr.addNodes(Dst); |
2139 | 80.7k | break; |
2140 | | |
2141 | 0 | case Stmt::CXXCatchStmtClass: |
2142 | 0 | Bldr.takeNodes(Pred); |
2143 | 0 | VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst); |
2144 | 0 | Bldr.addNodes(Dst); |
2145 | 0 | break; |
2146 | | |
2147 | 2.80k | case Stmt::CXXTemporaryObjectExprClass: |
2148 | 29.8k | case Stmt::CXXConstructExprClass: |
2149 | 29.8k | Bldr.takeNodes(Pred); |
2150 | 29.8k | VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst); |
2151 | 29.8k | Bldr.addNodes(Dst); |
2152 | 29.8k | break; |
2153 | | |
2154 | 5 | case Stmt::CXXInheritedCtorInitExprClass: |
2155 | 5 | Bldr.takeNodes(Pred); |
2156 | 5 | VisitCXXInheritedCtorInitExpr(cast<CXXInheritedCtorInitExpr>(S), Pred, |
2157 | 5 | Dst); |
2158 | 5 | Bldr.addNodes(Dst); |
2159 | 5 | break; |
2160 | | |
2161 | 1.08k | case Stmt::CXXNewExprClass: { |
2162 | 1.08k | Bldr.takeNodes(Pred); |
2163 | | |
2164 | 1.08k | ExplodedNodeSet PreVisit; |
2165 | 1.08k | getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); |
2166 | | |
2167 | 1.08k | ExplodedNodeSet PostVisit; |
2168 | 1.08k | for (const auto i : PreVisit) |
2169 | 1.05k | VisitCXXNewExpr(cast<CXXNewExpr>(S), i, PostVisit); |
2170 | | |
2171 | 1.08k | getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); |
2172 | 1.08k | Bldr.addNodes(Dst); |
2173 | 1.08k | break; |
2174 | 2.80k | } |
2175 | | |
2176 | 525 | case Stmt::CXXDeleteExprClass: { |
2177 | 525 | Bldr.takeNodes(Pred); |
2178 | 525 | ExplodedNodeSet PreVisit; |
2179 | 525 | const auto *CDE = cast<CXXDeleteExpr>(S); |
2180 | 525 | getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); |
2181 | 525 | ExplodedNodeSet PostVisit; |
2182 | 525 | getCheckerManager().runCheckersForPostStmt(PostVisit, PreVisit, S, *this); |
2183 | | |
2184 | 525 | for (const auto i : PostVisit) |
2185 | 525 | VisitCXXDeleteExpr(CDE, i, Dst); |
2186 | | |
2187 | 525 | Bldr.addNodes(Dst); |
2188 | 525 | break; |
2189 | 2.80k | } |
2190 | | // FIXME: ChooseExpr is really a constant. We need to fix |
2191 | | // the CFG do not model them as explicit control-flow. |
2192 | | |
2193 | 0 | case Stmt::ChooseExprClass: { // __builtin_choose_expr |
2194 | 0 | Bldr.takeNodes(Pred); |
2195 | 0 | const auto *C = cast<ChooseExpr>(S); |
2196 | 0 | VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); |
2197 | 0 | Bldr.addNodes(Dst); |
2198 | 0 | break; |
2199 | 2.80k | } |
2200 | | |
2201 | 1.37k | case Stmt::CompoundAssignOperatorClass: |
2202 | 1.37k | Bldr.takeNodes(Pred); |
2203 | 1.37k | VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); |
2204 | 1.37k | Bldr.addNodes(Dst); |
2205 | 1.37k | break; |
2206 | | |
2207 | 72 | case Stmt::CompoundLiteralExprClass: |
2208 | 72 | Bldr.takeNodes(Pred); |
2209 | 72 | VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(S), Pred, Dst); |
2210 | 72 | Bldr.addNodes(Dst); |
2211 | 72 | break; |
2212 | | |
2213 | 43 | case Stmt::BinaryConditionalOperatorClass: |
2214 | 4.17k | case Stmt::ConditionalOperatorClass: { // '?' operator |
2215 | 4.17k | Bldr.takeNodes(Pred); |
2216 | 4.17k | const auto *C = cast<AbstractConditionalOperator>(S); |
2217 | 4.17k | VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst); |
2218 | 4.17k | Bldr.addNodes(Dst); |
2219 | 4.17k | break; |
2220 | 43 | } |
2221 | | |
2222 | 9.24k | case Stmt::CXXThisExprClass: |
2223 | 9.24k | Bldr.takeNodes(Pred); |
2224 | 9.24k | VisitCXXThisExpr(cast<CXXThisExpr>(S), Pred, Dst); |
2225 | 9.24k | Bldr.addNodes(Dst); |
2226 | 9.24k | break; |
2227 | | |
2228 | 328k | case Stmt::DeclRefExprClass: { |
2229 | 328k | Bldr.takeNodes(Pred); |
2230 | 328k | const auto *DE = cast<DeclRefExpr>(S); |
2231 | 328k | VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst); |
2232 | 328k | Bldr.addNodes(Dst); |
2233 | 328k | break; |
2234 | 43 | } |
2235 | | |
2236 | 44.3k | case Stmt::DeclStmtClass: |
2237 | 44.3k | Bldr.takeNodes(Pred); |
2238 | 44.3k | VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst); |
2239 | 44.3k | Bldr.addNodes(Dst); |
2240 | 44.3k | break; |
2241 | | |
2242 | 431k | case Stmt::ImplicitCastExprClass: |
2243 | 447k | case Stmt::CStyleCastExprClass: |
2244 | 454k | case Stmt::CXXStaticCastExprClass: |
2245 | 454k | case Stmt::CXXDynamicCastExprClass: |
2246 | 455k | case Stmt::CXXReinterpretCastExprClass: |
2247 | 455k | case Stmt::CXXConstCastExprClass: |
2248 | 457k | case Stmt::CXXFunctionalCastExprClass: |
2249 | 457k | case Stmt::BuiltinBitCastExprClass: |
2250 | 457k | case Stmt::ObjCBridgedCastExprClass: |
2251 | 457k | case Stmt::CXXAddrspaceCastExprClass: { |
2252 | 457k | Bldr.takeNodes(Pred); |
2253 | 457k | const auto *C = cast<CastExpr>(S); |
2254 | 457k | ExplodedNodeSet dstExpr; |
2255 | 457k | VisitCast(C, C->getSubExpr(), Pred, dstExpr); |
2256 | | |
2257 | | // Handle the postvisit checks. |
2258 | 457k | getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this); |
2259 | 457k | Bldr.addNodes(Dst); |
2260 | 457k | break; |
2261 | 457k | } |
2262 | | |
2263 | 8.75k | case Expr::MaterializeTemporaryExprClass: { |
2264 | 8.75k | Bldr.takeNodes(Pred); |
2265 | 8.75k | const auto *MTE = cast<MaterializeTemporaryExpr>(S); |
2266 | 8.75k | ExplodedNodeSet dstPrevisit; |
2267 | 8.75k | getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this); |
2268 | 8.75k | ExplodedNodeSet dstExpr; |
2269 | 8.75k | for (const auto i : dstPrevisit) |
2270 | 8.75k | CreateCXXTemporaryObject(MTE, i, dstExpr); |
2271 | 8.75k | getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this); |
2272 | 8.75k | Bldr.addNodes(Dst); |
2273 | 8.75k | break; |
2274 | 457k | } |
2275 | | |
2276 | 1.69k | case Stmt::InitListExprClass: |
2277 | 1.69k | Bldr.takeNodes(Pred); |
2278 | 1.69k | VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst); |
2279 | 1.69k | Bldr.addNodes(Dst); |
2280 | 1.69k | break; |
2281 | | |
2282 | 51.2k | case Stmt::MemberExprClass: |
2283 | 51.2k | Bldr.takeNodes(Pred); |
2284 | 51.2k | VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst); |
2285 | 51.2k | Bldr.addNodes(Dst); |
2286 | 51.2k | break; |
2287 | | |
2288 | 54 | case Stmt::AtomicExprClass: |
2289 | 54 | Bldr.takeNodes(Pred); |
2290 | 54 | VisitAtomicExpr(cast<AtomicExpr>(S), Pred, Dst); |
2291 | 54 | Bldr.addNodes(Dst); |
2292 | 54 | break; |
2293 | | |
2294 | 1.06k | case Stmt::ObjCIvarRefExprClass: |
2295 | 1.06k | Bldr.takeNodes(Pred); |
2296 | 1.06k | VisitLvalObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst); |
2297 | 1.06k | Bldr.addNodes(Dst); |
2298 | 1.06k | break; |
2299 | | |
2300 | 260 | case Stmt::ObjCForCollectionStmtClass: |
2301 | 260 | Bldr.takeNodes(Pred); |
2302 | 260 | VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst); |
2303 | 260 | Bldr.addNodes(Dst); |
2304 | 260 | break; |
2305 | | |
2306 | 4.38k | case Stmt::ObjCMessageExprClass: |
2307 | 4.38k | Bldr.takeNodes(Pred); |
2308 | 4.38k | VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst); |
2309 | 4.38k | Bldr.addNodes(Dst); |
2310 | 4.38k | break; |
2311 | | |
2312 | 1 | case Stmt::ObjCAtThrowStmtClass: |
2313 | 10 | case Stmt::CXXThrowExprClass: |
2314 | | // FIXME: This is not complete. We basically treat @throw as |
2315 | | // an abort. |
2316 | 10 | Bldr.generateSink(S, Pred, Pred->getState()); |
2317 | 10 | break; |
2318 | | |
2319 | 24.3k | case Stmt::ReturnStmtClass: |
2320 | 24.3k | Bldr.takeNodes(Pred); |
2321 | 24.3k | VisitReturnStmt(cast<ReturnStmt>(S), Pred, Dst); |
2322 | 24.3k | Bldr.addNodes(Dst); |
2323 | 24.3k | break; |
2324 | | |
2325 | 23 | case Stmt::OffsetOfExprClass: { |
2326 | 23 | Bldr.takeNodes(Pred); |
2327 | 23 | ExplodedNodeSet PreVisit; |
2328 | 23 | getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); |
2329 | | |
2330 | 23 | ExplodedNodeSet PostVisit; |
2331 | 23 | for (const auto Node : PreVisit) |
2332 | 23 | VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Node, PostVisit); |
2333 | | |
2334 | 23 | getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); |
2335 | 23 | Bldr.addNodes(Dst); |
2336 | 23 | break; |
2337 | 1 | } |
2338 | | |
2339 | 1.25k | case Stmt::UnaryExprOrTypeTraitExprClass: |
2340 | 1.25k | Bldr.takeNodes(Pred); |
2341 | 1.25k | VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S), |
2342 | 1.25k | Pred, Dst); |
2343 | 1.25k | Bldr.addNodes(Dst); |
2344 | 1.25k | break; |
2345 | | |
2346 | 55 | case Stmt::StmtExprClass: { |
2347 | 55 | const auto *SE = cast<StmtExpr>(S); |
2348 | | |
2349 | 55 | if (SE->getSubStmt()->body_empty()) { |
2350 | | // Empty statement expression. |
2351 | 2 | assert(SE->getType() == getContext().VoidTy |
2352 | 2 | && "Empty statement expression must have void type."); |
2353 | 2 | break; |
2354 | 2 | } |
2355 | | |
2356 | 53 | if (const auto *LastExpr = |
2357 | 53 | dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) { |
2358 | 53 | ProgramStateRef state = Pred->getState(); |
2359 | 53 | Bldr.generateNode(SE, Pred, |
2360 | 53 | state->BindExpr(SE, Pred->getLocationContext(), |
2361 | 53 | state->getSVal(LastExpr, |
2362 | 53 | Pred->getLocationContext()))); |
2363 | 53 | } |
2364 | 53 | break; |
2365 | 55 | } |
2366 | | |
2367 | 36.3k | case Stmt::UnaryOperatorClass: { |
2368 | 36.3k | Bldr.takeNodes(Pred); |
2369 | 36.3k | const auto *U = cast<UnaryOperator>(S); |
2370 | 36.3k | if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)26.4k ) { |
2371 | 1.23k | ExplodedNodeSet Tmp; |
2372 | 1.23k | VisitUnaryOperator(U, Pred, Tmp); |
2373 | 1.23k | evalEagerlyAssumeBinOpBifurcation(Dst, Tmp, U); |
2374 | 1.23k | } |
2375 | 35.1k | else |
2376 | 35.1k | VisitUnaryOperator(U, Pred, Dst); |
2377 | 36.3k | Bldr.addNodes(Dst); |
2378 | 36.3k | break; |
2379 | 55 | } |
2380 | | |
2381 | 446 | case Stmt::PseudoObjectExprClass: { |
2382 | 446 | Bldr.takeNodes(Pred); |
2383 | 446 | ProgramStateRef state = Pred->getState(); |
2384 | 446 | const auto *PE = cast<PseudoObjectExpr>(S); |
2385 | 446 | if (const Expr *Result = PE->getResultExpr()) { |
2386 | 446 | SVal V = state->getSVal(Result, Pred->getLocationContext()); |
2387 | 446 | Bldr.generateNode(S, Pred, |
2388 | 446 | state->BindExpr(S, Pred->getLocationContext(), V)); |
2389 | 446 | } |
2390 | 0 | else |
2391 | 0 | Bldr.generateNode(S, Pred, |
2392 | 0 | state->BindExpr(S, Pred->getLocationContext(), |
2393 | 0 | UnknownVal())); |
2394 | | |
2395 | 446 | Bldr.addNodes(Dst); |
2396 | 446 | break; |
2397 | 55 | } |
2398 | | |
2399 | 2 | case Expr::ObjCIndirectCopyRestoreExprClass: { |
2400 | | // ObjCIndirectCopyRestoreExpr implies passing a temporary for |
2401 | | // correctness of lifetime management. Due to limited analysis |
2402 | | // of ARC, this is implemented as direct arg passing. |
2403 | 2 | Bldr.takeNodes(Pred); |
2404 | 2 | ProgramStateRef state = Pred->getState(); |
2405 | 2 | const auto *OIE = cast<ObjCIndirectCopyRestoreExpr>(S); |
2406 | 2 | const Expr *E = OIE->getSubExpr(); |
2407 | 2 | SVal V = state->getSVal(E, Pred->getLocationContext()); |
2408 | 2 | Bldr.generateNode(S, Pred, |
2409 | 2 | state->BindExpr(S, Pred->getLocationContext(), V)); |
2410 | 2 | Bldr.addNodes(Dst); |
2411 | 2 | break; |
2412 | 55 | } |
2413 | 1.33M | } |
2414 | 1.33M | } |
2415 | | |
2416 | | bool ExprEngine::replayWithoutInlining(ExplodedNode *N, |
2417 | 42 | const LocationContext *CalleeLC) { |
2418 | 42 | const StackFrameContext *CalleeSF = CalleeLC->getStackFrame(); |
2419 | 42 | const StackFrameContext *CallerSF = CalleeSF->getParent()->getStackFrame(); |
2420 | 42 | assert(CalleeSF && CallerSF); |
2421 | 42 | ExplodedNode *BeforeProcessingCall = nullptr; |
2422 | 42 | const Stmt *CE = CalleeSF->getCallSite(); |
2423 | | |
2424 | | // Find the first node before we started processing the call expression. |
2425 | 28.3k | while (N) { |
2426 | 28.3k | ProgramPoint L = N->getLocation(); |
2427 | 28.3k | BeforeProcessingCall = N; |
2428 | 28.3k | N = N->pred_empty() ? nullptr0 : *(N->pred_begin()); |
2429 | | |
2430 | | // Skip the nodes corresponding to the inlined code. |
2431 | 28.3k | if (L.getStackFrame() != CallerSF) |
2432 | 28.2k | continue; |
2433 | | // We reached the caller. Find the node right before we started |
2434 | | // processing the call. |
2435 | 126 | if (L.isPurgeKind()) |
2436 | 39 | continue; |
2437 | 87 | if (L.getAs<PreImplicitCall>()) |
2438 | 3 | continue; |
2439 | 84 | if (L.getAs<CallEnter>()) |
2440 | 42 | continue; |
2441 | 42 | if (std::optional<StmtPoint> SP = L.getAs<StmtPoint>()) |
2442 | 42 | if (SP->getStmt() == CE) |
2443 | 0 | continue; |
2444 | 42 | break; |
2445 | 42 | } |
2446 | | |
2447 | 42 | if (!BeforeProcessingCall) |
2448 | 0 | return false; |
2449 | | |
2450 | | // TODO: Clean up the unneeded nodes. |
2451 | | |
2452 | | // Build an Epsilon node from which we will restart the analyzes. |
2453 | | // Note that CE is permitted to be NULL! |
2454 | 42 | static SimpleProgramPointTag PT("ExprEngine", "Replay without inlining"); |
2455 | 42 | ProgramPoint NewNodeLoc = EpsilonPoint( |
2456 | 42 | BeforeProcessingCall->getLocationContext(), CE, nullptr, &PT); |
2457 | | // Add the special flag to GDM to signal retrying with no inlining. |
2458 | | // Note, changing the state ensures that we are not going to cache out. |
2459 | 42 | ProgramStateRef NewNodeState = BeforeProcessingCall->getState(); |
2460 | 42 | NewNodeState = |
2461 | 42 | NewNodeState->set<ReplayWithoutInlining>(const_cast<Stmt *>(CE)); |
2462 | | |
2463 | | // Make the new node a successor of BeforeProcessingCall. |
2464 | 42 | bool IsNew = false; |
2465 | 42 | ExplodedNode *NewNode = G.getNode(NewNodeLoc, NewNodeState, false, &IsNew); |
2466 | | // We cached out at this point. Caching out is common due to us backtracking |
2467 | | // from the inlined function, which might spawn several paths. |
2468 | 42 | if (!IsNew) |
2469 | 4 | return true; |
2470 | | |
2471 | 38 | NewNode->addPredecessor(BeforeProcessingCall, G); |
2472 | | |
2473 | | // Add the new node to the work list. |
2474 | 38 | Engine.enqueueStmtNode(NewNode, CalleeSF->getCallSiteBlock(), |
2475 | 38 | CalleeSF->getIndex()); |
2476 | 38 | NumTimesRetriedWithoutInlining++; |
2477 | 38 | return true; |
2478 | 42 | } |
2479 | | |
2480 | | /// Block entrance. (Update counters). |
2481 | | void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, |
2482 | | NodeBuilderWithSinks &nodeBuilder, |
2483 | 168k | ExplodedNode *Pred) { |
2484 | 168k | PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); |
2485 | | // If we reach a loop which has a known bound (and meets |
2486 | | // other constraints) then consider completely unrolling it. |
2487 | 168k | if(AMgr.options.ShouldUnrollLoops) { |
2488 | 5.92k | unsigned maxBlockVisitOnPath = AMgr.options.maxBlockVisitOnPath; |
2489 | 5.92k | const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt(); |
2490 | 5.92k | if (Term) { |
2491 | 2.08k | ProgramStateRef NewState = updateLoopStack(Term, AMgr.getASTContext(), |
2492 | 2.08k | Pred, maxBlockVisitOnPath); |
2493 | 2.08k | if (NewState != Pred->getState()) { |
2494 | 197 | ExplodedNode *UpdatedNode = nodeBuilder.generateNode(NewState, Pred); |
2495 | 197 | if (!UpdatedNode) |
2496 | 0 | return; |
2497 | 197 | Pred = UpdatedNode; |
2498 | 197 | } |
2499 | 2.08k | } |
2500 | | // Is we are inside an unrolled loop then no need the check the counters. |
2501 | 5.92k | if(isUnrolledState(Pred->getState())) |
2502 | 4.49k | return; |
2503 | 5.92k | } |
2504 | | |
2505 | | // If this block is terminated by a loop and it has already been visited the |
2506 | | // maximum number of times, widen the loop. |
2507 | 164k | unsigned int BlockCount = nodeBuilder.getContext().blockCount(); |
2508 | 164k | if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 && |
2509 | 164k | AMgr.options.ShouldWidenLoops7.36k ) { |
2510 | 138 | const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt(); |
2511 | 138 | if (!isa_and_nonnull<ForStmt, WhileStmt, DoStmt>(Term)) |
2512 | 87 | return; |
2513 | | // Widen. |
2514 | 51 | const LocationContext *LCtx = Pred->getLocationContext(); |
2515 | 51 | ProgramStateRef WidenedState = |
2516 | 51 | getWidenedLoopState(Pred->getState(), LCtx, BlockCount, Term); |
2517 | 51 | nodeBuilder.generateNode(WidenedState, Pred); |
2518 | 51 | return; |
2519 | 138 | } |
2520 | | |
2521 | | // FIXME: Refactor this into a checker. |
2522 | 164k | if (BlockCount >= AMgr.options.maxBlockVisitOnPath) { |
2523 | 2.05k | static SimpleProgramPointTag tag(TagProviderName, "Block count exceeded"); |
2524 | 2.05k | const ExplodedNode *Sink = |
2525 | 2.05k | nodeBuilder.generateSink(Pred->getState(), Pred, &tag); |
2526 | | |
2527 | | // Check if we stopped at the top level function or not. |
2528 | | // Root node should have the location context of the top most function. |
2529 | 2.05k | const LocationContext *CalleeLC = Pred->getLocation().getLocationContext(); |
2530 | 2.05k | const LocationContext *CalleeSF = CalleeLC->getStackFrame(); |
2531 | 2.05k | const LocationContext *RootLC = |
2532 | 2.05k | (*G.roots_begin())->getLocation().getLocationContext(); |
2533 | 2.05k | if (RootLC->getStackFrame() != CalleeSF) { |
2534 | 47 | Engine.FunctionSummaries->markReachedMaxBlockCount(CalleeSF->getDecl()); |
2535 | | |
2536 | | // Re-run the call evaluation without inlining it, by storing the |
2537 | | // no-inlining policy in the state and enqueuing the new work item on |
2538 | | // the list. Replay should almost never fail. Use the stats to catch it |
2539 | | // if it does. |
2540 | 47 | if ((!AMgr.options.NoRetryExhausted && |
2541 | 47 | replayWithoutInlining(Pred, CalleeLC)42 )) |
2542 | 42 | return; |
2543 | 5 | NumMaxBlockCountReachedInInlined++; |
2544 | 5 | } else |
2545 | 2.00k | NumMaxBlockCountReached++; |
2546 | | |
2547 | | // Make sink nodes as exhausted(for stats) only if retry failed. |
2548 | 2.01k | Engine.blocksExhausted.push_back(std::make_pair(L, Sink)); |
2549 | 2.01k | } |
2550 | 164k | } |
2551 | | |
2552 | | //===----------------------------------------------------------------------===// |
2553 | | // Branch processing. |
2554 | | //===----------------------------------------------------------------------===// |
2555 | | |
2556 | | /// RecoverCastedSymbol - A helper function for ProcessBranch that is used |
2557 | | /// to try to recover some path-sensitivity for casts of symbolic |
2558 | | /// integers that promote their values (which are currently not tracked well). |
2559 | | /// This function returns the SVal bound to Condition->IgnoreCasts if all the |
2560 | | // cast(s) did was sign-extend the original value. |
2561 | | static SVal RecoverCastedSymbol(ProgramStateRef state, |
2562 | | const Stmt *Condition, |
2563 | | const LocationContext *LCtx, |
2564 | 412 | ASTContext &Ctx) { |
2565 | | |
2566 | 412 | const auto *Ex = dyn_cast<Expr>(Condition); |
2567 | 412 | if (!Ex) |
2568 | 0 | return UnknownVal(); |
2569 | | |
2570 | 412 | uint64_t bits = 0; |
2571 | 412 | bool bitsInit = false; |
2572 | | |
2573 | 551 | while (const auto *CE = dyn_cast<CastExpr>(Ex)) { |
2574 | 145 | QualType T = CE->getType(); |
2575 | | |
2576 | 145 | if (!T->isIntegralOrEnumerationType()) |
2577 | 6 | return UnknownVal(); |
2578 | | |
2579 | 139 | uint64_t newBits = Ctx.getTypeSize(T); |
2580 | 139 | if (!bitsInit || newBits < bits0 ) { |
2581 | 139 | bitsInit = true; |
2582 | 139 | bits = newBits; |
2583 | 139 | } |
2584 | | |
2585 | 139 | Ex = CE->getSubExpr(); |
2586 | 139 | } |
2587 | | |
2588 | | // We reached a non-cast. Is it a symbolic value? |
2589 | 406 | QualType T = Ex->getType(); |
2590 | | |
2591 | 406 | if (!bitsInit || !T->isIntegralOrEnumerationType()133 || |
2592 | 406 | Ctx.getTypeSize(T) > bits133 ) |
2593 | 396 | return UnknownVal(); |
2594 | | |
2595 | 10 | return state->getSVal(Ex, LCtx); |
2596 | 406 | } |
2597 | | |
2598 | | #ifndef NDEBUG |
2599 | 14.6k | static const Stmt *getRightmostLeaf(const Stmt *Condition) { |
2600 | 29.3k | while (Condition) { |
2601 | 29.3k | const auto *BO = dyn_cast<BinaryOperator>(Condition); |
2602 | 29.3k | if (!BO || !BO->isLogicalOp()19.6k ) { |
2603 | 14.6k | return Condition; |
2604 | 14.6k | } |
2605 | 14.7k | Condition = BO->getRHS()->IgnoreParens(); |
2606 | 14.7k | } |
2607 | 0 | return nullptr; |
2608 | 14.6k | } |
2609 | | #endif |
2610 | | |
2611 | | // Returns the condition the branch at the end of 'B' depends on and whose value |
2612 | | // has been evaluated within 'B'. |
2613 | | // In most cases, the terminator condition of 'B' will be evaluated fully in |
2614 | | // the last statement of 'B'; in those cases, the resolved condition is the |
2615 | | // given 'Condition'. |
2616 | | // If the condition of the branch is a logical binary operator tree, the CFG is |
2617 | | // optimized: in that case, we know that the expression formed by all but the |
2618 | | // rightmost leaf of the logical binary operator tree must be true, and thus |
2619 | | // the branch condition is at this point equivalent to the truth value of that |
2620 | | // rightmost leaf; the CFG block thus only evaluates this rightmost leaf |
2621 | | // expression in its final statement. As the full condition in that case was |
2622 | | // not evaluated, and is thus not in the SVal cache, we need to use that leaf |
2623 | | // expression to evaluate the truth value of the condition in the current state |
2624 | | // space. |
2625 | | static const Stmt *ResolveCondition(const Stmt *Condition, |
2626 | 60.4k | const CFGBlock *B) { |
2627 | 60.4k | if (const auto *Ex = dyn_cast<Expr>(Condition)) |
2628 | 59.9k | Condition = Ex->IgnoreParens(); |
2629 | | |
2630 | 60.4k | const auto *BO = dyn_cast<BinaryOperator>(Condition); |
2631 | 60.4k | if (!BO || !BO->isLogicalOp()38.8k ) |
2632 | 45.7k | return Condition; |
2633 | | |
2634 | 14.6k | assert(B->getTerminator().isStmtBranch() && |
2635 | 14.6k | "Other kinds of branches are handled separately!"); |
2636 | | |
2637 | | // For logical operations, we still have the case where some branches |
2638 | | // use the traditional "merge" approach and others sink the branch |
2639 | | // directly into the basic blocks representing the logical operation. |
2640 | | // We need to distinguish between those two cases here. |
2641 | | |
2642 | | // The invariants are still shifting, but it is possible that the |
2643 | | // last element in a CFGBlock is not a CFGStmt. Look for the last |
2644 | | // CFGStmt as the value of the condition. |
2645 | 14.6k | for (CFGElement Elem : llvm::reverse(*B)) { |
2646 | 14.6k | std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>(); |
2647 | 14.6k | if (!CS) |
2648 | 0 | continue; |
2649 | 14.6k | const Stmt *LastStmt = CS->getStmt(); |
2650 | 14.6k | assert(LastStmt == Condition || LastStmt == getRightmostLeaf(Condition)); |
2651 | 14.6k | return LastStmt; |
2652 | 14.6k | } |
2653 | 0 | llvm_unreachable("could not resolve condition"); |
2654 | 0 | } |
2655 | | |
2656 | | using ObjCForLctxPair = |
2657 | | std::pair<const ObjCForCollectionStmt *, const LocationContext *>; |
2658 | | |
2659 | | REGISTER_MAP_WITH_PROGRAMSTATE(ObjCForHasMoreIterations, ObjCForLctxPair, bool) |
2660 | | |
2661 | | ProgramStateRef ExprEngine::setWhetherHasMoreIteration( |
2662 | | ProgramStateRef State, const ObjCForCollectionStmt *O, |
2663 | 517 | const LocationContext *LC, bool HasMoreIteraton) { |
2664 | 517 | assert(!State->contains<ObjCForHasMoreIterations>({O, LC})); |
2665 | 517 | return State->set<ObjCForHasMoreIterations>({O, LC}, HasMoreIteraton); |
2666 | 517 | } |
2667 | | |
2668 | | ProgramStateRef |
2669 | | ExprEngine::removeIterationState(ProgramStateRef State, |
2670 | | const ObjCForCollectionStmt *O, |
2671 | 448 | const LocationContext *LC) { |
2672 | 448 | assert(State->contains<ObjCForHasMoreIterations>({O, LC})); |
2673 | 448 | return State->remove<ObjCForHasMoreIterations>({O, LC}); |
2674 | 448 | } |
2675 | | |
2676 | | bool ExprEngine::hasMoreIteration(ProgramStateRef State, |
2677 | | const ObjCForCollectionStmt *O, |
2678 | 871 | const LocationContext *LC) { |
2679 | 871 | assert(State->contains<ObjCForHasMoreIterations>({O, LC})); |
2680 | 871 | return *State->get<ObjCForHasMoreIterations>({O, LC}); |
2681 | 871 | } |
2682 | | |
2683 | | /// Split the state on whether there are any more iterations left for this loop. |
2684 | | /// Returns a (HasMoreIteration, HasNoMoreIteration) pair, or std::nullopt when |
2685 | | /// the acquisition of the loop condition value failed. |
2686 | | static std::optional<std::pair<ProgramStateRef, ProgramStateRef>> |
2687 | 60.3k | assumeCondition(const Stmt *Condition, ExplodedNode *N) { |
2688 | 60.3k | ProgramStateRef State = N->getState(); |
2689 | 60.3k | if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(Condition)) { |
2690 | 448 | bool HasMoreIteraton = |
2691 | 448 | ExprEngine::hasMoreIteration(State, ObjCFor, N->getLocationContext()); |
2692 | | // Checkers have already ran on branch conditions, so the current |
2693 | | // information as to whether the loop has more iteration becomes outdated |
2694 | | // after this point. |
2695 | 448 | State = ExprEngine::removeIterationState(State, ObjCFor, |
2696 | 448 | N->getLocationContext()); |
2697 | 448 | if (HasMoreIteraton) |
2698 | 242 | return std::pair<ProgramStateRef, ProgramStateRef>{State, nullptr}; |
2699 | 206 | else |
2700 | 206 | return std::pair<ProgramStateRef, ProgramStateRef>{nullptr, State}; |
2701 | 448 | } |
2702 | 59.9k | SVal X = State->getSVal(Condition, N->getLocationContext()); |
2703 | | |
2704 | 59.9k | if (X.isUnknownOrUndef()) { |
2705 | | // Give it a chance to recover from unknown. |
2706 | 418 | if (const auto *Ex = dyn_cast<Expr>(Condition)) { |
2707 | 418 | if (Ex->getType()->isIntegralOrEnumerationType()) { |
2708 | | // Try to recover some path-sensitivity. Right now casts of symbolic |
2709 | | // integers that promote their values are currently not tracked well. |
2710 | | // If 'Condition' is such an expression, try and recover the |
2711 | | // underlying value and use that instead. |
2712 | 412 | SVal recovered = |
2713 | 412 | RecoverCastedSymbol(State, Condition, N->getLocationContext(), |
2714 | 412 | N->getState()->getStateManager().getContext()); |
2715 | | |
2716 | 412 | if (!recovered.isUnknown()) { |
2717 | 10 | X = recovered; |
2718 | 10 | } |
2719 | 412 | } |
2720 | 418 | } |
2721 | 418 | } |
2722 | | |
2723 | | // If the condition is still unknown, give up. |
2724 | 59.9k | if (X.isUnknownOrUndef()) |
2725 | 408 | return std::nullopt; |
2726 | | |
2727 | 59.5k | DefinedSVal V = X.castAs<DefinedSVal>(); |
2728 | | |
2729 | 59.5k | ProgramStateRef StTrue, StFalse; |
2730 | 59.5k | return State->assume(V); |
2731 | 59.9k | } |
2732 | | |
2733 | | void ExprEngine::processBranch(const Stmt *Condition, |
2734 | | NodeBuilderContext& BldCtx, |
2735 | | ExplodedNode *Pred, |
2736 | | ExplodedNodeSet &Dst, |
2737 | | const CFGBlock *DstT, |
2738 | 60.6k | const CFGBlock *DstF) { |
2739 | 60.6k | assert((!Condition || !isa<CXXBindTemporaryExpr>(Condition)) && |
2740 | 60.6k | "CXXBindTemporaryExprs are handled by processBindTemporary."); |
2741 | 60.6k | const LocationContext *LCtx = Pred->getLocationContext(); |
2742 | 60.6k | PrettyStackTraceLocationContext StackCrashInfo(LCtx); |
2743 | 60.6k | currBldrCtx = &BldCtx; |
2744 | | |
2745 | | // Check for NULL conditions; e.g. "for(;;)" |
2746 | 60.6k | if (!Condition) { |
2747 | 189 | BranchNodeBuilder NullCondBldr(Pred, Dst, BldCtx, DstT, DstF); |
2748 | 189 | NullCondBldr.markInfeasible(false); |
2749 | 189 | NullCondBldr.generateNode(Pred->getState(), true, Pred); |
2750 | 189 | return; |
2751 | 189 | } |
2752 | | |
2753 | 60.4k | if (const auto *Ex = dyn_cast<Expr>(Condition)) |
2754 | 59.9k | Condition = Ex->IgnoreParens(); |
2755 | | |
2756 | 60.4k | Condition = ResolveCondition(Condition, BldCtx.getBlock()); |
2757 | 60.4k | PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), |
2758 | 60.4k | Condition->getBeginLoc(), |
2759 | 60.4k | "Error evaluating branch"); |
2760 | | |
2761 | 60.4k | ExplodedNodeSet CheckersOutSet; |
2762 | 60.4k | getCheckerManager().runCheckersForBranchCondition(Condition, CheckersOutSet, |
2763 | 60.4k | Pred, *this); |
2764 | | // We generated only sinks. |
2765 | 60.4k | if (CheckersOutSet.empty()) |
2766 | 61 | return; |
2767 | | |
2768 | 60.3k | BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF); |
2769 | 60.3k | for (ExplodedNode *PredN : CheckersOutSet) { |
2770 | 60.3k | if (PredN->isSink()) |
2771 | 0 | continue; |
2772 | | |
2773 | 60.3k | ProgramStateRef PrevState = PredN->getState(); |
2774 | | |
2775 | 60.3k | ProgramStateRef StTrue, StFalse; |
2776 | 60.3k | if (const auto KnownCondValueAssumption = assumeCondition(Condition, PredN)) |
2777 | 59.9k | std::tie(StTrue, StFalse) = *KnownCondValueAssumption; |
2778 | 408 | else { |
2779 | 408 | assert(!isa<ObjCForCollectionStmt>(Condition)); |
2780 | 408 | builder.generateNode(PrevState, true, PredN); |
2781 | 408 | builder.generateNode(PrevState, false, PredN); |
2782 | 408 | continue; |
2783 | 408 | } |
2784 | 59.9k | if (StTrue && StFalse45.1k ) |
2785 | 29.0k | assert(!isa<ObjCForCollectionStmt>(Condition)); |
2786 | | |
2787 | | // Process the true branch. |
2788 | 59.9k | if (builder.isFeasible(true)) { |
2789 | 59.7k | if (StTrue) |
2790 | 45.1k | builder.generateNode(StTrue, true, PredN); |
2791 | 14.6k | else |
2792 | 14.6k | builder.markInfeasible(true); |
2793 | 59.7k | } |
2794 | | |
2795 | | // Process the false branch. |
2796 | 59.9k | if (builder.isFeasible(false)) { |
2797 | 58.9k | if (StFalse) |
2798 | 43.9k | builder.generateNode(StFalse, false, PredN); |
2799 | 15.0k | else |
2800 | 15.0k | builder.markInfeasible(false); |
2801 | 58.9k | } |
2802 | 59.9k | } |
2803 | 60.3k | currBldrCtx = nullptr; |
2804 | 60.3k | } |
2805 | | |
2806 | | /// The GDM component containing the set of global variables which have been |
2807 | | /// previously initialized with explicit initializers. |
2808 | | REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet, |
2809 | | llvm::ImmutableSet<const VarDecl *>) |
2810 | | |
2811 | | void ExprEngine::processStaticInitializer(const DeclStmt *DS, |
2812 | | NodeBuilderContext &BuilderCtx, |
2813 | | ExplodedNode *Pred, |
2814 | | ExplodedNodeSet &Dst, |
2815 | | const CFGBlock *DstT, |
2816 | 212 | const CFGBlock *DstF) { |
2817 | 212 | PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); |
2818 | 212 | currBldrCtx = &BuilderCtx; |
2819 | | |
2820 | 212 | const auto *VD = cast<VarDecl>(DS->getSingleDecl()); |
2821 | 212 | ProgramStateRef state = Pred->getState(); |
2822 | 212 | bool initHasRun = state->contains<InitializedGlobalsSet>(VD); |
2823 | 212 | BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF); |
2824 | | |
2825 | 212 | if (!initHasRun) { |
2826 | 207 | state = state->add<InitializedGlobalsSet>(VD); |
2827 | 207 | } |
2828 | | |
2829 | 212 | builder.generateNode(state, initHasRun, Pred); |
2830 | 212 | builder.markInfeasible(!initHasRun); |
2831 | | |
2832 | 212 | currBldrCtx = nullptr; |
2833 | 212 | } |
2834 | | |
2835 | | /// processIndirectGoto - Called by CoreEngine. Used to generate successor |
2836 | | /// nodes by processing the 'effects' of a computed goto jump. |
2837 | 8 | void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { |
2838 | 8 | ProgramStateRef state = builder.getState(); |
2839 | 8 | SVal V = state->getSVal(builder.getTarget(), builder.getLocationContext()); |
2840 | | |
2841 | | // Three possibilities: |
2842 | | // |
2843 | | // (1) We know the computed label. |
2844 | | // (2) The label is NULL (or some other constant), or Undefined. |
2845 | | // (3) We have no clue about the label. Dispatch to all targets. |
2846 | | // |
2847 | | |
2848 | 8 | using iterator = IndirectGotoNodeBuilder::iterator; |
2849 | | |
2850 | 8 | if (std::optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) { |
2851 | 0 | const LabelDecl *L = LV->getLabel(); |
2852 | |
|
2853 | 0 | for (iterator Succ : builder) { |
2854 | 0 | if (Succ.getLabel() == L) { |
2855 | 0 | builder.generateNode(Succ, state); |
2856 | 0 | return; |
2857 | 0 | } |
2858 | 0 | } |
2859 | | |
2860 | 0 | llvm_unreachable("No block with label."); |
2861 | 0 | } |
2862 | | |
2863 | 8 | if (isa<UndefinedVal, loc::ConcreteInt>(V)) { |
2864 | | // Dispatch to the first target and mark it as a sink. |
2865 | | //ExplodedNode* N = builder.generateNode(builder.begin(), state, true); |
2866 | | // FIXME: add checker visit. |
2867 | | // UndefBranches.insert(N); |
2868 | 0 | return; |
2869 | 0 | } |
2870 | | |
2871 | | // This is really a catch-all. We don't support symbolics yet. |
2872 | | // FIXME: Implement dispatch for symbolic pointers. |
2873 | | |
2874 | 8 | for (iterator Succ : builder) |
2875 | 8 | builder.generateNode(Succ, state); |
2876 | 8 | } |
2877 | | |
2878 | | void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, |
2879 | | ExplodedNode *Pred, |
2880 | | ExplodedNodeSet &Dst, |
2881 | 51.3k | const BlockEdge &L) { |
2882 | 51.3k | SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC); |
2883 | 51.3k | getCheckerManager().runCheckersForBeginFunction(Dst, L, Pred, *this); |
2884 | 51.3k | } |
2885 | | |
2886 | | /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path |
2887 | | /// nodes when the control reaches the end of a function. |
2888 | | void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, |
2889 | | ExplodedNode *Pred, |
2890 | 67.8k | const ReturnStmt *RS) { |
2891 | 67.8k | ProgramStateRef State = Pred->getState(); |
2892 | | |
2893 | 67.8k | if (!Pred->getStackFrame()->inTopFrame()) |
2894 | 42.5k | State = finishArgumentConstruction( |
2895 | 42.5k | State, *getStateManager().getCallEventManager().getCaller( |
2896 | 42.5k | Pred->getStackFrame(), Pred->getState())); |
2897 | | |
2898 | | // FIXME: We currently cannot assert that temporaries are clear, because |
2899 | | // lifetime extended temporaries are not always modelled correctly. In some |
2900 | | // cases when we materialize the temporary, we do |
2901 | | // createTemporaryRegionIfNeeded(), and the region changes, and also the |
2902 | | // respective destructor becomes automatic from temporary. So for now clean up |
2903 | | // the state manually before asserting. Ideally, this braced block of code |
2904 | | // should go away. |
2905 | 67.8k | { |
2906 | 67.8k | const LocationContext *FromLC = Pred->getLocationContext(); |
2907 | 67.8k | const LocationContext *ToLC = FromLC->getStackFrame()->getParent(); |
2908 | 67.8k | const LocationContext *LC = FromLC; |
2909 | 135k | while (LC != ToLC) { |
2910 | 67.8k | assert(LC && "ToLC must be a parent of FromLC!"); |
2911 | 67.8k | for (auto I : State->get<ObjectsUnderConstruction>()) |
2912 | 31.7k | if (I.first.getLocationContext() == LC) { |
2913 | | // The comment above only pardons us for not cleaning up a |
2914 | | // temporary destructor. If any other statements are found here, |
2915 | | // it must be a separate problem. |
2916 | 382 | assert(I.first.getItem().getKind() == |
2917 | 382 | ConstructionContextItem::TemporaryDestructorKind || |
2918 | 382 | I.first.getItem().getKind() == |
2919 | 382 | ConstructionContextItem::ElidedDestructorKind); |
2920 | 382 | State = State->remove<ObjectsUnderConstruction>(I.first); |
2921 | 382 | } |
2922 | 67.8k | LC = LC->getParent(); |
2923 | 67.8k | } |
2924 | 67.8k | } |
2925 | | |
2926 | | // Perform the transition with cleanups. |
2927 | 67.8k | if (State != Pred->getState()) { |
2928 | 1.42k | ExplodedNodeSet PostCleanup; |
2929 | 1.42k | NodeBuilder Bldr(Pred, PostCleanup, BC); |
2930 | 1.42k | Pred = Bldr.generateNode(Pred->getLocation(), State, Pred); |
2931 | 1.42k | if (!Pred) { |
2932 | | // The node with clean temporaries already exists. We might have reached |
2933 | | // it on a path on which we initialize different temporaries. |
2934 | 4 | return; |
2935 | 4 | } |
2936 | 1.42k | } |
2937 | | |
2938 | 67.8k | assert(areAllObjectsFullyConstructed(Pred->getState(), |
2939 | 67.8k | Pred->getLocationContext(), |
2940 | 67.8k | Pred->getStackFrame()->getParent())); |
2941 | | |
2942 | 67.8k | PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); |
2943 | | |
2944 | 67.8k | ExplodedNodeSet Dst; |
2945 | 67.8k | if (Pred->getLocationContext()->inTopFrame()) { |
2946 | | // Remove dead symbols. |
2947 | 25.3k | ExplodedNodeSet AfterRemovedDead; |
2948 | 25.3k | removeDeadOnEndOfFunction(BC, Pred, AfterRemovedDead); |
2949 | | |
2950 | | // Notify checkers. |
2951 | 25.3k | for (const auto I : AfterRemovedDead) |
2952 | 23.3k | getCheckerManager().runCheckersForEndFunction(BC, Dst, I, *this, RS); |
2953 | 42.5k | } else { |
2954 | 42.5k | getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this, RS); |
2955 | 42.5k | } |
2956 | | |
2957 | 67.8k | Engine.enqueueEndOfFunction(Dst, RS); |
2958 | 67.8k | } |
2959 | | |
2960 | | /// ProcessSwitch - Called by CoreEngine. Used to generate successor |
2961 | | /// nodes by processing the 'effects' of a switch statement. |
2962 | 308 | void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { |
2963 | 308 | using iterator = SwitchNodeBuilder::iterator; |
2964 | | |
2965 | 308 | ProgramStateRef state = builder.getState(); |
2966 | 308 | const Expr *CondE = builder.getCondition(); |
2967 | 308 | SVal CondV_untested = state->getSVal(CondE, builder.getLocationContext()); |
2968 | | |
2969 | 308 | if (CondV_untested.isUndef()) { |
2970 | | //ExplodedNode* N = builder.generateDefaultCaseNode(state, true); |
2971 | | // FIXME: add checker |
2972 | | //UndefBranches.insert(N); |
2973 | |
|
2974 | 0 | return; |
2975 | 0 | } |
2976 | 308 | DefinedOrUnknownSVal CondV = CondV_untested.castAs<DefinedOrUnknownSVal>(); |
2977 | | |
2978 | 308 | ProgramStateRef DefaultSt = state; |
2979 | | |
2980 | 308 | iterator I = builder.begin(), EI = builder.end(); |
2981 | 308 | bool defaultIsFeasible = I == EI; |
2982 | | |
2983 | 958 | for ( ; I != EI; ++I650 ) { |
2984 | | // Successor may be pruned out during CFG construction. |
2985 | 776 | if (!I.getBlock()) |
2986 | 2 | continue; |
2987 | | |
2988 | 774 | const CaseStmt *Case = I.getCase(); |
2989 | | |
2990 | | // Evaluate the LHS of the case value. |
2991 | 774 | llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext()); |
2992 | 774 | assert(V1.getBitWidth() == getContext().getIntWidth(CondE->getType())); |
2993 | | |
2994 | | // Get the RHS of the case, if it exists. |
2995 | 774 | llvm::APSInt V2; |
2996 | 774 | if (const Expr *E = Case->getRHS()) |
2997 | 29 | V2 = E->EvaluateKnownConstInt(getContext()); |
2998 | 745 | else |
2999 | 745 | V2 = V1; |
3000 | | |
3001 | 774 | ProgramStateRef StateCase; |
3002 | 774 | if (std::optional<NonLoc> NL = CondV.getAs<NonLoc>()) |
3003 | 774 | std::tie(StateCase, DefaultSt) = |
3004 | 774 | DefaultSt->assumeInclusiveRange(*NL, V1, V2); |
3005 | 0 | else // UnknownVal |
3006 | 0 | StateCase = DefaultSt; |
3007 | | |
3008 | 774 | if (StateCase) |
3009 | 571 | builder.generateCaseStmtNode(I, StateCase); |
3010 | | |
3011 | | // Now "assume" that the case doesn't match. Add this state |
3012 | | // to the default state (if it is feasible). |
3013 | 774 | if (DefaultSt) |
3014 | 648 | defaultIsFeasible = true; |
3015 | 126 | else { |
3016 | 126 | defaultIsFeasible = false; |
3017 | 126 | break; |
3018 | 126 | } |
3019 | 774 | } |
3020 | | |
3021 | 308 | if (!defaultIsFeasible) |
3022 | 126 | return; |
3023 | | |
3024 | | // If we have switch(enum value), the default branch is not |
3025 | | // feasible if all of the enum constants not covered by 'case:' statements |
3026 | | // are not feasible values for the switch condition. |
3027 | | // |
3028 | | // Note that this isn't as accurate as it could be. Even if there isn't |
3029 | | // a case for a particular enum value as long as that enum value isn't |
3030 | | // feasible then it shouldn't be considered for making 'default:' reachable. |
3031 | 182 | const SwitchStmt *SS = builder.getSwitch(); |
3032 | 182 | const Expr *CondExpr = SS->getCond()->IgnoreParenImpCasts(); |
3033 | 182 | if (CondExpr->getType()->getAs<EnumType>()) { |
3034 | 11 | if (SS->isAllEnumCasesCovered()) |
3035 | 6 | return; |
3036 | 11 | } |
3037 | | |
3038 | 176 | builder.generateDefaultCaseNode(DefaultSt); |
3039 | 176 | } |
3040 | | |
3041 | | //===----------------------------------------------------------------------===// |
3042 | | // Transfer functions: Loads and stores. |
3043 | | //===----------------------------------------------------------------------===// |
3044 | | |
3045 | | void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, |
3046 | | ExplodedNode *Pred, |
3047 | 328k | ExplodedNodeSet &Dst) { |
3048 | 328k | StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); |
3049 | | |
3050 | 328k | ProgramStateRef state = Pred->getState(); |
3051 | 328k | const LocationContext *LCtx = Pred->getLocationContext(); |
3052 | | |
3053 | 328k | if (const auto *VD = dyn_cast<VarDecl>(D)) { |
3054 | | // C permits "extern void v", and if you cast the address to a valid type, |
3055 | | // you can even do things with it. We simply pretend |
3056 | 255k | assert(Ex->isGLValue() || VD->getType()->isVoidType()); |
3057 | 255k | const LocationContext *LocCtxt = Pred->getLocationContext(); |
3058 | 255k | const Decl *D = LocCtxt->getDecl(); |
3059 | 255k | const auto *MD = dyn_cast_or_null<CXXMethodDecl>(D); |
3060 | 255k | const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex); |
3061 | 255k | std::optional<std::pair<SVal, QualType>> VInfo; |
3062 | | |
3063 | 255k | if (AMgr.options.ShouldInlineLambdas && DeclRefEx255k && |
3064 | 255k | DeclRefEx->refersToEnclosingVariableOrCapture()255k && MD847 && |
3065 | 255k | MD->getParent()->isLambda()424 ) { |
3066 | | // Lookup the field of the lambda. |
3067 | 424 | const CXXRecordDecl *CXXRec = MD->getParent(); |
3068 | 424 | llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields; |
3069 | 424 | FieldDecl *LambdaThisCaptureField; |
3070 | 424 | CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField); |
3071 | | |
3072 | | // Sema follows a sequence of complex rules to determine whether the |
3073 | | // variable should be captured. |
3074 | 424 | if (const FieldDecl *FD = LambdaCaptureFields[VD]) { |
3075 | 416 | Loc CXXThis = |
3076 | 416 | svalBuilder.getCXXThis(MD, LocCtxt->getStackFrame()); |
3077 | 416 | SVal CXXThisVal = state->getSVal(CXXThis); |
3078 | 416 | VInfo = std::make_pair(state->getLValue(FD, CXXThisVal), FD->getType()); |
3079 | 416 | } |
3080 | 424 | } |
3081 | | |
3082 | 255k | if (!VInfo) |
3083 | 254k | VInfo = std::make_pair(state->getLValue(VD, LocCtxt), VD->getType()); |
3084 | | |
3085 | 255k | SVal V = VInfo->first; |
3086 | 255k | bool IsReference = VInfo->second->isReferenceType(); |
3087 | | |
3088 | | // For references, the 'lvalue' is the pointer address stored in the |
3089 | | // reference region. |
3090 | 255k | if (IsReference) { |
3091 | 24.8k | if (const MemRegion *R = V.getAsRegion()) |
3092 | 24.8k | V = state->getSVal(R); |
3093 | 0 | else |
3094 | 0 | V = UnknownVal(); |
3095 | 24.8k | } |
3096 | | |
3097 | 255k | Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, |
3098 | 255k | ProgramPoint::PostLValueKind); |
3099 | 255k | return; |
3100 | 255k | } |
3101 | 72.8k | if (const auto *ED = dyn_cast<EnumConstantDecl>(D)) { |
3102 | 272 | assert(!Ex->isGLValue()); |
3103 | 272 | SVal V = svalBuilder.makeIntVal(ED->getInitVal()); |
3104 | 272 | Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V)); |
3105 | 272 | return; |
3106 | 272 | } |
3107 | 72.6k | if (const auto *FD = dyn_cast<FunctionDecl>(D)) { |
3108 | 72.3k | SVal V = svalBuilder.getFunctionPointer(FD); |
3109 | 72.3k | Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, |
3110 | 72.3k | ProgramPoint::PostLValueKind); |
3111 | 72.3k | return; |
3112 | 72.3k | } |
3113 | 313 | if (isa<FieldDecl, IndirectFieldDecl>(D)) { |
3114 | | // Delegate all work related to pointer to members to the surrounding |
3115 | | // operator&. |
3116 | 34 | return; |
3117 | 34 | } |
3118 | 279 | if (const auto *BD = dyn_cast<BindingDecl>(D)) { |
3119 | 263 | const auto *DD = cast<DecompositionDecl>(BD->getDecomposedDecl()); |
3120 | | |
3121 | 263 | SVal Base = state->getLValue(DD, LCtx); |
3122 | 263 | if (DD->getType()->isReferenceType()) { |
3123 | 138 | if (const MemRegion *R = Base.getAsRegion()) |
3124 | 138 | Base = state->getSVal(R); |
3125 | 0 | else |
3126 | 0 | Base = UnknownVal(); |
3127 | 138 | } |
3128 | | |
3129 | 263 | SVal V = UnknownVal(); |
3130 | | |
3131 | | // Handle binding to data members |
3132 | 263 | if (const auto *ME = dyn_cast<MemberExpr>(BD->getBinding())) { |
3133 | 54 | const auto *Field = cast<FieldDecl>(ME->getMemberDecl()); |
3134 | 54 | V = state->getLValue(Field, Base); |
3135 | 54 | } |
3136 | | // Handle binding to arrays |
3137 | 209 | else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(BD->getBinding())) { |
3138 | 110 | SVal Idx = state->getSVal(ASE->getIdx(), LCtx); |
3139 | | |
3140 | | // Note: the index of an element in a structured binding is automatically |
3141 | | // created and it is a unique identifier of the specific element. Thus it |
3142 | | // cannot be a value that varies at runtime. |
3143 | 110 | assert(Idx.isConstant() && "BindingDecl array index is not a constant!"); |
3144 | | |
3145 | 110 | V = state->getLValue(BD->getType(), Idx, Base); |
3146 | 110 | } |
3147 | | // Handle binding to tuple-like structures |
3148 | 99 | else if (const auto *HV = BD->getHoldingVar()) { |
3149 | 99 | V = state->getLValue(HV, LCtx); |
3150 | | |
3151 | 99 | if (HV->getType()->isReferenceType()) { |
3152 | 99 | if (const MemRegion *R = V.getAsRegion()) |
3153 | 99 | V = state->getSVal(R); |
3154 | 0 | else |
3155 | 0 | V = UnknownVal(); |
3156 | 99 | } |
3157 | 99 | } else |
3158 | 0 | llvm_unreachable("An unknown case of structured binding encountered!"); |
3159 | | |
3160 | | // In case of tuple-like types the references are already handled, so we |
3161 | | // don't want to handle them again. |
3162 | 263 | if (BD->getType()->isReferenceType() && !BD->getHoldingVar()21 ) { |
3163 | 11 | if (const MemRegion *R = V.getAsRegion()) |
3164 | 11 | V = state->getSVal(R); |
3165 | 0 | else |
3166 | 0 | V = UnknownVal(); |
3167 | 11 | } |
3168 | | |
3169 | 263 | Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, |
3170 | 263 | ProgramPoint::PostLValueKind); |
3171 | | |
3172 | 263 | return; |
3173 | 263 | } |
3174 | | |
3175 | 16 | if (const auto *TPO = dyn_cast<TemplateParamObjectDecl>(D)) { |
3176 | | // FIXME: We should meaningfully implement this. |
3177 | 16 | (void)TPO; |
3178 | 16 | return; |
3179 | 16 | } |
3180 | | |
3181 | 0 | llvm_unreachable("Support for this Decl not implemented."); |
3182 | 0 | } |
3183 | | |
3184 | | /// VisitArrayInitLoopExpr - Transfer function for array init loop. |
3185 | | void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex, |
3186 | | ExplodedNode *Pred, |
3187 | 103 | ExplodedNodeSet &Dst) { |
3188 | 103 | ExplodedNodeSet CheckerPreStmt; |
3189 | 103 | getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, Ex, *this); |
3190 | | |
3191 | 103 | ExplodedNodeSet EvalSet; |
3192 | 103 | StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx); |
3193 | | |
3194 | 103 | const Expr *Arr = Ex->getCommonExpr()->getSourceExpr(); |
3195 | | |
3196 | 103 | for (auto *Node : CheckerPreStmt) { |
3197 | | |
3198 | | // The constructor visitior has already taken care of everything. |
3199 | 103 | if (isa<CXXConstructExpr>(Ex->getSubExpr())) |
3200 | 47 | break; |
3201 | | |
3202 | 56 | const LocationContext *LCtx = Node->getLocationContext(); |
3203 | 56 | ProgramStateRef state = Node->getState(); |
3204 | | |
3205 | 56 | SVal Base = UnknownVal(); |
3206 | | |
3207 | | // As in case of this expression the sub-expressions are not visited by any |
3208 | | // other transfer functions, they are handled by matching their AST. |
3209 | | |
3210 | | // Case of implicit copy or move ctor of object with array member |
3211 | | // |
3212 | | // Note: ExprEngine::VisitMemberExpr is not able to bind the array to the |
3213 | | // environment. |
3214 | | // |
3215 | | // struct S { |
3216 | | // int arr[2]; |
3217 | | // }; |
3218 | | // |
3219 | | // |
3220 | | // S a; |
3221 | | // S b = a; |
3222 | | // |
3223 | | // The AST in case of a *copy constructor* looks like this: |
3224 | | // ArrayInitLoopExpr |
3225 | | // |-OpaqueValueExpr |
3226 | | // | `-MemberExpr <-- match this |
3227 | | // | `-DeclRefExpr |
3228 | | // ` ... |
3229 | | // |
3230 | | // |
3231 | | // S c; |
3232 | | // S d = std::move(d); |
3233 | | // |
3234 | | // In case of a *move constructor* the resulting AST looks like: |
3235 | | // ArrayInitLoopExpr |
3236 | | // |-OpaqueValueExpr |
3237 | | // | `-MemberExpr <-- match this first |
3238 | | // | `-CXXStaticCastExpr <-- match this after |
3239 | | // | `-DeclRefExpr |
3240 | | // ` ... |
3241 | 56 | if (const auto *ME = dyn_cast<MemberExpr>(Arr)) { |
3242 | 25 | Expr *MEBase = ME->getBase(); |
3243 | | |
3244 | | // Move ctor |
3245 | 25 | if (auto CXXSCE = dyn_cast<CXXStaticCastExpr>(MEBase)) { |
3246 | 3 | MEBase = CXXSCE->getSubExpr(); |
3247 | 3 | } |
3248 | | |
3249 | 25 | auto ObjDeclExpr = cast<DeclRefExpr>(MEBase); |
3250 | 25 | SVal Obj = state->getLValue(cast<VarDecl>(ObjDeclExpr->getDecl()), LCtx); |
3251 | | |
3252 | 25 | Base = state->getLValue(cast<FieldDecl>(ME->getMemberDecl()), Obj); |
3253 | 25 | } |
3254 | | |
3255 | | // Case of lambda capture and decomposition declaration |
3256 | | // |
3257 | | // int arr[2]; |
3258 | | // |
3259 | | // [arr]{ int a = arr[0]; }(); |
3260 | | // auto[a, b] = arr; |
3261 | | // |
3262 | | // In both of these cases the AST looks like the following: |
3263 | | // ArrayInitLoopExpr |
3264 | | // |-OpaqueValueExpr |
3265 | | // | `-DeclRefExpr <-- match this |
3266 | | // ` ... |
3267 | 56 | if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Arr)) |
3268 | 27 | Base = state->getLValue(cast<VarDecl>(DRE->getDecl()), LCtx); |
3269 | | |
3270 | | // Create a lazy compound value to the original array |
3271 | 56 | if (const MemRegion *R = Base.getAsRegion()) |
3272 | 52 | Base = state->getSVal(R); |
3273 | 4 | else |
3274 | 4 | Base = UnknownVal(); |
3275 | | |
3276 | 56 | Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, Base)); |
3277 | 56 | } |
3278 | | |
3279 | 103 | getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this); |
3280 | 103 | } |
3281 | | |
3282 | | /// VisitArraySubscriptExpr - Transfer function for array accesses |
3283 | | void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A, |
3284 | | ExplodedNode *Pred, |
3285 | 17.4k | ExplodedNodeSet &Dst){ |
3286 | 17.4k | const Expr *Base = A->getBase()->IgnoreParens(); |
3287 | 17.4k | const Expr *Idx = A->getIdx()->IgnoreParens(); |
3288 | | |
3289 | 17.4k | ExplodedNodeSet CheckerPreStmt; |
3290 | 17.4k | getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this); |
3291 | | |
3292 | 17.4k | ExplodedNodeSet EvalSet; |
3293 | 17.4k | StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx); |
3294 | | |
3295 | 17.4k | bool IsVectorType = A->getBase()->getType()->isVectorType(); |
3296 | | |
3297 | | // The "like" case is for situations where C standard prohibits the type to |
3298 | | // be an lvalue, e.g. taking the address of a subscript of an expression of |
3299 | | // type "void *". |
3300 | 17.4k | bool IsGLValueLike = A->isGLValue() || |
3301 | 17.4k | (6 A->getType().isCForbiddenLValueType()6 && !AMgr.getLangOpts().CPlusPlus4 ); |
3302 | | |
3303 | 17.4k | for (auto *Node : CheckerPreStmt) { |
3304 | 17.4k | const LocationContext *LCtx = Node->getLocationContext(); |
3305 | 17.4k | ProgramStateRef state = Node->getState(); |
3306 | | |
3307 | 17.4k | if (IsGLValueLike) { |
3308 | 17.4k | QualType T = A->getType(); |
3309 | | |
3310 | | // One of the forbidden LValue types! We still need to have sensible |
3311 | | // symbolic locations to represent this stuff. Note that arithmetic on |
3312 | | // void pointers is a GCC extension. |
3313 | 17.4k | if (T->isVoidType()) |
3314 | 4 | T = getContext().CharTy; |
3315 | | |
3316 | 17.4k | SVal V = state->getLValue(T, |
3317 | 17.4k | state->getSVal(Idx, LCtx), |
3318 | 17.4k | state->getSVal(Base, LCtx)); |
3319 | 17.4k | Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr, |
3320 | 17.4k | ProgramPoint::PostLValueKind); |
3321 | 17.4k | } else if (2 IsVectorType2 ) { |
3322 | | // FIXME: non-glvalue vector reads are not modelled. |
3323 | 2 | Bldr.generateNode(A, Node, state, nullptr); |
3324 | 2 | } else { |
3325 | 0 | llvm_unreachable("Array subscript should be an lValue when not \ |
3326 | 0 | a vector and not a forbidden lvalue type"); |
3327 | 0 | } |
3328 | 17.4k | } |
3329 | | |
3330 | 17.4k | getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this); |
3331 | 17.4k | } |
3332 | | |
3333 | | /// VisitMemberExpr - Transfer function for member expressions. |
3334 | | void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, |
3335 | 51.2k | ExplodedNodeSet &Dst) { |
3336 | | // FIXME: Prechecks eventually go in ::Visit(). |
3337 | 51.2k | ExplodedNodeSet CheckedSet; |
3338 | 51.2k | getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, M, *this); |
3339 | | |
3340 | 51.2k | ExplodedNodeSet EvalSet; |
3341 | 51.2k | ValueDecl *Member = M->getMemberDecl(); |
3342 | | |
3343 | | // Handle static member variables and enum constants accessed via |
3344 | | // member syntax. |
3345 | 51.2k | if (isa<VarDecl, EnumConstantDecl>(Member)) { |
3346 | 8 | for (const auto I : CheckedSet) |
3347 | 8 | VisitCommonDeclRefExpr(M, Member, I, EvalSet); |
3348 | 51.2k | } else { |
3349 | 51.2k | StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx); |
3350 | 51.2k | ExplodedNodeSet Tmp; |
3351 | | |
3352 | 51.2k | for (const auto I : CheckedSet) { |
3353 | 51.2k | ProgramStateRef state = I->getState(); |
3354 | 51.2k | const LocationContext *LCtx = I->getLocationContext(); |
3355 | 51.2k | Expr *BaseExpr = M->getBase(); |
3356 | | |
3357 | | // Handle C++ method calls. |
3358 | 51.2k | if (const auto *MD = dyn_cast<CXXMethodDecl>(Member)) { |
3359 | 8.32k | if (MD->isInstance()) |
3360 | 8.32k | state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr); |
3361 | | |
3362 | 8.32k | SVal MDVal = svalBuilder.getFunctionPointer(MD); |
3363 | 8.32k | state = state->BindExpr(M, LCtx, MDVal); |
3364 | | |
3365 | 8.32k | Bldr.generateNode(M, I, state); |
3366 | 8.32k | continue; |
3367 | 8.32k | } |
3368 | | |
3369 | | // Handle regular struct fields / member variables. |
3370 | 42.9k | const SubRegion *MR = nullptr; |
3371 | 42.9k | state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr, |
3372 | 42.9k | /*Result=*/nullptr, |
3373 | 42.9k | /*OutRegionWithAdjustments=*/&MR); |
3374 | 42.9k | SVal baseExprVal = |
3375 | 42.9k | MR ? loc::MemRegionVal(MR)48 : state->getSVal(BaseExpr, LCtx)42.8k ; |
3376 | | |
3377 | | // FIXME: Copied from RegionStoreManager::bind() |
3378 | 42.9k | if (const auto *SR = |
3379 | 42.9k | dyn_cast_or_null<SymbolicRegion>(baseExprVal.getAsRegion())) { |
3380 | 3.98k | QualType T = SR->getPointeeStaticType(); |
3381 | 3.98k | baseExprVal = |
3382 | 3.98k | loc::MemRegionVal(getStoreManager().GetElementZeroRegion(SR, T)); |
3383 | 3.98k | } |
3384 | | |
3385 | 42.9k | const auto *field = cast<FieldDecl>(Member); |
3386 | 42.9k | SVal L = state->getLValue(field, baseExprVal); |
3387 | | |
3388 | 42.9k | if (M->isGLValue() || M->getType()->isArrayType()48 ) { |
3389 | | // We special-case rvalues of array type because the analyzer cannot |
3390 | | // reason about them, since we expect all regions to be wrapped in Locs. |
3391 | | // We instead treat these as lvalues and assume that they will decay to |
3392 | | // pointers as soon as they are used. |
3393 | 42.8k | if (!M->isGLValue()) { |
3394 | 3 | assert(M->getType()->isArrayType()); |
3395 | 3 | const auto *PE = |
3396 | 3 | dyn_cast<ImplicitCastExpr>(I->getParentMap().getParentIgnoreParens(M)); |
3397 | 3 | if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) { |
3398 | 0 | llvm_unreachable("should always be wrapped in ArrayToPointerDecay"); |
3399 | 0 | } |
3400 | 3 | } |
3401 | | |
3402 | 42.8k | if (field->getType()->isReferenceType()) { |
3403 | 403 | if (const MemRegion *R = L.getAsRegion()) |
3404 | 403 | L = state->getSVal(R); |
3405 | 0 | else |
3406 | 0 | L = UnknownVal(); |
3407 | 403 | } |
3408 | | |
3409 | 42.8k | Bldr.generateNode(M, I, state->BindExpr(M, LCtx, L), nullptr, |
3410 | 42.8k | ProgramPoint::PostLValueKind); |
3411 | 42.8k | } else { |
3412 | 45 | Bldr.takeNodes(I); |
3413 | 45 | evalLoad(Tmp, M, M, I, state, L); |
3414 | 45 | Bldr.addNodes(Tmp); |
3415 | 45 | } |
3416 | 42.9k | } |
3417 | 51.2k | } |
3418 | | |
3419 | 51.2k | getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, M, *this); |
3420 | 51.2k | } |
3421 | | |
3422 | | void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, |
3423 | 54 | ExplodedNodeSet &Dst) { |
3424 | 54 | ExplodedNodeSet AfterPreSet; |
3425 | 54 | getCheckerManager().runCheckersForPreStmt(AfterPreSet, Pred, AE, *this); |
3426 | | |
3427 | | // For now, treat all the arguments to C11 atomics as escaping. |
3428 | | // FIXME: Ideally we should model the behavior of the atomics precisely here. |
3429 | | |
3430 | 54 | ExplodedNodeSet AfterInvalidateSet; |
3431 | 54 | StmtNodeBuilder Bldr(AfterPreSet, AfterInvalidateSet, *currBldrCtx); |
3432 | | |
3433 | 54 | for (const auto I : AfterPreSet) { |
3434 | 54 | ProgramStateRef State = I->getState(); |
3435 | 54 | const LocationContext *LCtx = I->getLocationContext(); |
3436 | | |
3437 | 54 | SmallVector<SVal, 8> ValuesToInvalidate; |
3438 | 219 | for (unsigned SI = 0, Count = AE->getNumSubExprs(); SI != Count; SI++165 ) { |
3439 | 165 | const Expr *SubExpr = AE->getSubExprs()[SI]; |
3440 | 165 | SVal SubExprVal = State->getSVal(SubExpr, LCtx); |
3441 | 165 | ValuesToInvalidate.push_back(SubExprVal); |
3442 | 165 | } |
3443 | | |
3444 | 54 | State = State->invalidateRegions(ValuesToInvalidate, AE, |
3445 | 54 | currBldrCtx->blockCount(), |
3446 | 54 | LCtx, |
3447 | 54 | /*CausedByPointerEscape*/true, |
3448 | 54 | /*Symbols=*/nullptr); |
3449 | | |
3450 | 54 | SVal ResultVal = UnknownVal(); |
3451 | 54 | State = State->BindExpr(AE, LCtx, ResultVal); |
3452 | 54 | Bldr.generateNode(AE, I, State, nullptr, |
3453 | 54 | ProgramPoint::PostStmtKind); |
3454 | 54 | } |
3455 | | |
3456 | 54 | getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this); |
3457 | 54 | } |
3458 | | |
3459 | | // A value escapes in four possible cases: |
3460 | | // (1) We are binding to something that is not a memory region. |
3461 | | // (2) We are binding to a MemRegion that does not have stack storage. |
3462 | | // (3) We are binding to a top-level parameter region with a non-trivial |
3463 | | // destructor. We won't see the destructor during analysis, but it's there. |
3464 | | // (4) We are binding to a MemRegion with stack storage that the store |
3465 | | // does not understand. |
3466 | | ProgramStateRef ExprEngine::processPointerEscapedOnBind( |
3467 | | ProgramStateRef State, ArrayRef<std::pair<SVal, SVal>> LocAndVals, |
3468 | | const LocationContext *LCtx, PointerEscapeKind Kind, |
3469 | 140k | const CallEvent *Call) { |
3470 | 140k | SmallVector<SVal, 8> Escaped; |
3471 | 140k | for (const std::pair<SVal, SVal> &LocAndVal : LocAndVals) { |
3472 | | // Cases (1) and (2). |
3473 | 90.8k | const MemRegion *MR = LocAndVal.first.getAsRegion(); |
3474 | 90.8k | if (!MR || |
3475 | 90.8k | !isa<StackSpaceRegion, StaticGlobalSpaceRegion>(MR->getMemorySpace())90.8k ) { |
3476 | 8.16k | Escaped.push_back(LocAndVal.second); |
3477 | 8.16k | continue; |
3478 | 8.16k | } |
3479 | | |
3480 | | // Case (3). |
3481 | 82.7k | if (const auto *VR = dyn_cast<VarRegion>(MR->getBaseRegion())) |
3482 | 79.9k | if (VR->hasStackParametersStorage() && VR->getStackFrame()->inTopFrame()14.6k ) |
3483 | 3.97k | if (const auto *RD = VR->getValueType()->getAsCXXRecordDecl()) |
3484 | 26 | if (!RD->hasTrivialDestructor()) { |
3485 | 4 | Escaped.push_back(LocAndVal.second); |
3486 | 4 | continue; |
3487 | 4 | } |
3488 | | |
3489 | | // Case (4): in order to test that, generate a new state with the binding |
3490 | | // added. If it is the same state, then it escapes (since the store cannot |
3491 | | // represent the binding). |
3492 | | // Do this only if we know that the store is not supposed to generate the |
3493 | | // same state. |
3494 | 82.7k | SVal StoredVal = State->getSVal(MR); |
3495 | 82.7k | if (StoredVal != LocAndVal.second) |
3496 | 77.1k | if (State == |
3497 | 77.1k | (State->bindLoc(loc::MemRegionVal(MR), LocAndVal.second, LCtx))) |
3498 | 140 | Escaped.push_back(LocAndVal.second); |
3499 | 82.7k | } |
3500 | | |
3501 | 140k | if (Escaped.empty()) |
3502 | 132k | return State; |
3503 | | |
3504 | 8.10k | return escapeValues(State, Escaped, Kind, Call); |
3505 | 140k | } |
3506 | | |
3507 | | ProgramStateRef |
3508 | | ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, |
3509 | 85.2k | SVal Val, const LocationContext *LCtx) { |
3510 | 85.2k | std::pair<SVal, SVal> LocAndVal(Loc, Val); |
3511 | 85.2k | return processPointerEscapedOnBind(State, LocAndVal, LCtx, PSK_EscapeOnBind, |
3512 | 85.2k | nullptr); |
3513 | 85.2k | } |
3514 | | |
3515 | | ProgramStateRef |
3516 | | ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, |
3517 | | const InvalidatedSymbols *Invalidated, |
3518 | | ArrayRef<const MemRegion *> ExplicitRegions, |
3519 | | const CallEvent *Call, |
3520 | 35.8k | RegionAndSymbolInvalidationTraits &ITraits) { |
3521 | 35.8k | if (!Invalidated || Invalidated->empty()) |
3522 | 6.22k | return State; |
3523 | | |
3524 | 29.5k | if (!Call) |
3525 | 246 | return getCheckerManager().runCheckersForPointerEscape(State, |
3526 | 246 | *Invalidated, |
3527 | 246 | nullptr, |
3528 | 246 | PSK_EscapeOther, |
3529 | 246 | &ITraits); |
3530 | | |
3531 | | // If the symbols were invalidated by a call, we want to find out which ones |
3532 | | // were invalidated directly due to being arguments to the call. |
3533 | 29.3k | InvalidatedSymbols SymbolsDirectlyInvalidated; |
3534 | 31.3k | for (const auto I : ExplicitRegions) { |
3535 | 31.3k | if (const SymbolicRegion *R = I->StripCasts()->getAs<SymbolicRegion>()) |
3536 | 9.63k | SymbolsDirectlyInvalidated.insert(R->getSymbol()); |
3537 | 31.3k | } |
3538 | | |
3539 | 29.3k | InvalidatedSymbols SymbolsIndirectlyInvalidated; |
3540 | 78.9k | for (const auto &sym : *Invalidated) { |
3541 | 78.9k | if (SymbolsDirectlyInvalidated.count(sym)) |
3542 | 9.54k | continue; |
3543 | 69.3k | SymbolsIndirectlyInvalidated.insert(sym); |
3544 | 69.3k | } |
3545 | | |
3546 | 29.3k | if (!SymbolsDirectlyInvalidated.empty()) |
3547 | 8.52k | State = getCheckerManager().runCheckersForPointerEscape(State, |
3548 | 8.52k | SymbolsDirectlyInvalidated, Call, PSK_DirectEscapeOnCall, &ITraits); |
3549 | | |
3550 | | // Notify about the symbols that get indirectly invalidated by the call. |
3551 | 29.3k | if (!SymbolsIndirectlyInvalidated.empty()) |
3552 | 27.3k | State = getCheckerManager().runCheckersForPointerEscape(State, |
3553 | 27.3k | SymbolsIndirectlyInvalidated, Call, PSK_IndirectEscapeOnCall, &ITraits); |
3554 | | |
3555 | 29.3k | return State; |
3556 | 29.5k | } |
3557 | | |
3558 | | /// evalBind - Handle the semantics of binding a value to a specific location. |
3559 | | /// This method is used by evalStore and (soon) VisitDeclStmt, and others. |
3560 | | void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, |
3561 | | ExplodedNode *Pred, |
3562 | | SVal location, SVal Val, |
3563 | 85.6k | bool atDeclInit, const ProgramPoint *PP) { |
3564 | 85.6k | const LocationContext *LC = Pred->getLocationContext(); |
3565 | 85.6k | PostStmt PS(StoreE, LC); |
3566 | 85.6k | if (!PP) |
3567 | 76.9k | PP = &PS; |
3568 | | |
3569 | | // Do a previsit of the bind. |
3570 | 85.6k | ExplodedNodeSet CheckedSet; |
3571 | 85.6k | getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val, |
3572 | 85.6k | StoreE, *this, *PP); |
3573 | | |
3574 | 85.6k | StmtNodeBuilder Bldr(CheckedSet, Dst, *currBldrCtx); |
3575 | | |
3576 | | // If the location is not a 'Loc', it will already be handled by |
3577 | | // the checkers. There is nothing left to do. |
3578 | 85.6k | if (!isa<Loc>(location)) { |
3579 | 38 | const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/nullptr, |
3580 | 38 | /*tag*/nullptr); |
3581 | 38 | ProgramStateRef state = Pred->getState(); |
3582 | 38 | state = processPointerEscapedOnBind(state, location, Val, LC); |
3583 | 38 | Bldr.generateNode(L, state, Pred); |
3584 | 38 | return; |
3585 | 38 | } |
3586 | | |
3587 | 85.6k | for (const auto PredI : CheckedSet) { |
3588 | 85.1k | ProgramStateRef state = PredI->getState(); |
3589 | | |
3590 | 85.1k | state = processPointerEscapedOnBind(state, location, Val, LC); |
3591 | | |
3592 | | // When binding the value, pass on the hint that this is a initialization. |
3593 | | // For initializations, we do not need to inform clients of region |
3594 | | // changes. |
3595 | 85.1k | state = state->bindLoc(location.castAs<Loc>(), |
3596 | 85.1k | Val, LC, /* notifyChanges = */ !atDeclInit); |
3597 | | |
3598 | 85.1k | const MemRegion *LocReg = nullptr; |
3599 | 85.1k | if (std::optional<loc::MemRegionVal> LocRegVal = |
3600 | 85.1k | location.getAs<loc::MemRegionVal>()) { |
3601 | 85.1k | LocReg = LocRegVal->getRegion(); |
3602 | 85.1k | } |
3603 | | |
3604 | 85.1k | const ProgramPoint L = PostStore(StoreE, LC, LocReg, nullptr); |
3605 | 85.1k | Bldr.generateNode(L, state, PredI); |
3606 | 85.1k | } |
3607 | 85.6k | } |
3608 | | |
3609 | | /// evalStore - Handle the semantics of a store via an assignment. |
3610 | | /// @param Dst The node set to store generated state nodes |
3611 | | /// @param AssignE The assignment expression if the store happens in an |
3612 | | /// assignment. |
3613 | | /// @param LocationE The location expression that is stored to. |
3614 | | /// @param state The current simulation state |
3615 | | /// @param location The location to store the value |
3616 | | /// @param Val The value to be stored |
3617 | | void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, |
3618 | | const Expr *LocationE, |
3619 | | ExplodedNode *Pred, |
3620 | | ProgramStateRef state, SVal location, SVal Val, |
3621 | 33.6k | const ProgramPointTag *tag) { |
3622 | | // Proceed with the store. We use AssignE as the anchor for the PostStore |
3623 | | // ProgramPoint if it is non-NULL, and LocationE otherwise. |
3624 | 33.6k | const Expr *StoreE = AssignE ? AssignE : LocationE0 ; |
3625 | | |
3626 | | // Evaluate the location (checks for bad dereferences). |
3627 | 33.6k | ExplodedNodeSet Tmp; |
3628 | 33.6k | evalLocation(Tmp, AssignE, LocationE, Pred, state, location, false); |
3629 | | |
3630 | 33.6k | if (Tmp.empty()) |
3631 | 745 | return; |
3632 | | |
3633 | 32.8k | if (location.isUndef()) |
3634 | 0 | return; |
3635 | | |
3636 | 32.8k | for (const auto I : Tmp) |
3637 | 32.8k | evalBind(Dst, StoreE, I, location, Val, false); |
3638 | 32.8k | } |
3639 | | |
3640 | | void ExprEngine::evalLoad(ExplodedNodeSet &Dst, |
3641 | | const Expr *NodeEx, |
3642 | | const Expr *BoundEx, |
3643 | | ExplodedNode *Pred, |
3644 | | ProgramStateRef state, |
3645 | | SVal location, |
3646 | | const ProgramPointTag *tag, |
3647 | 242k | QualType LoadTy) { |
3648 | 242k | assert(!isa<NonLoc>(location) && "location cannot be a NonLoc."); |
3649 | 242k | assert(NodeEx); |
3650 | 242k | assert(BoundEx); |
3651 | | // Evaluate the location (checks for bad dereferences). |
3652 | 242k | ExplodedNodeSet Tmp; |
3653 | 242k | evalLocation(Tmp, NodeEx, BoundEx, Pred, state, location, true); |
3654 | 242k | if (Tmp.empty()) |
3655 | 386 | return; |
3656 | | |
3657 | 241k | StmtNodeBuilder Bldr(Tmp, Dst, *currBldrCtx); |
3658 | 241k | if (location.isUndef()) |
3659 | 0 | return; |
3660 | | |
3661 | | // Proceed with the load. |
3662 | 241k | for (const auto I : Tmp) { |
3663 | 241k | state = I->getState(); |
3664 | 241k | const LocationContext *LCtx = I->getLocationContext(); |
3665 | | |
3666 | 241k | SVal V = UnknownVal(); |
3667 | 241k | if (location.isValid()) { |
3668 | 241k | if (LoadTy.isNull()) |
3669 | 241k | LoadTy = BoundEx->getType(); |
3670 | 241k | V = state->getSVal(location.castAs<Loc>(), LoadTy); |
3671 | 241k | } |
3672 | | |
3673 | 241k | Bldr.generateNode(NodeEx, I, state->BindExpr(BoundEx, LCtx, V), tag, |
3674 | 241k | ProgramPoint::PostLoadKind); |
3675 | 241k | } |
3676 | 241k | } |
3677 | | |
3678 | | void ExprEngine::evalLocation(ExplodedNodeSet &Dst, |
3679 | | const Stmt *NodeEx, |
3680 | | const Stmt *BoundEx, |
3681 | | ExplodedNode *Pred, |
3682 | | ProgramStateRef state, |
3683 | | SVal location, |
3684 | 276k | bool isLoad) { |
3685 | 276k | StmtNodeBuilder BldrTop(Pred, Dst, *currBldrCtx); |
3686 | | // Early checks for performance reason. |
3687 | 276k | if (location.isUnknown()) { |
3688 | 127 | return; |
3689 | 127 | } |
3690 | | |
3691 | 275k | ExplodedNodeSet Src; |
3692 | 275k | BldrTop.takeNodes(Pred); |
3693 | 275k | StmtNodeBuilder Bldr(Pred, Src, *currBldrCtx); |
3694 | 275k | if (Pred->getState() != state) { |
3695 | | // Associate this new state with an ExplodedNode. |
3696 | | // FIXME: If I pass null tag, the graph is incorrect, e.g for |
3697 | | // int *p; |
3698 | | // p = 0; |
3699 | | // *p = 0xDEADBEEF; |
3700 | | // "p = 0" is not noted as "Null pointer value stored to 'p'" but |
3701 | | // instead "int *p" is noted as |
3702 | | // "Variable 'p' initialized to a null pointer value" |
3703 | | |
3704 | 33.4k | static SimpleProgramPointTag tag(TagProviderName, "Location"); |
3705 | 33.4k | Bldr.generateNode(NodeEx, Pred, state, &tag); |
3706 | 33.4k | } |
3707 | 275k | ExplodedNodeSet Tmp; |
3708 | 275k | getCheckerManager().runCheckersForLocation(Tmp, Src, location, isLoad, |
3709 | 275k | NodeEx, BoundEx, *this); |
3710 | 275k | BldrTop.addNodes(Tmp); |
3711 | 275k | } |
3712 | | |
3713 | | std::pair<const ProgramPointTag *, const ProgramPointTag*> |
3714 | 3.38M | ExprEngine::geteagerlyAssumeBinOpBifurcationTags() { |
3715 | 3.38M | static SimpleProgramPointTag |
3716 | 3.38M | eagerlyAssumeBinOpBifurcationTrue(TagProviderName, |
3717 | 3.38M | "Eagerly Assume True"), |
3718 | 3.38M | eagerlyAssumeBinOpBifurcationFalse(TagProviderName, |
3719 | 3.38M | "Eagerly Assume False"); |
3720 | 3.38M | return std::make_pair(&eagerlyAssumeBinOpBifurcationTrue, |
3721 | 3.38M | &eagerlyAssumeBinOpBifurcationFalse); |
3722 | 3.38M | } |
3723 | | |
3724 | | void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, |
3725 | | ExplodedNodeSet &Src, |
3726 | 15.3k | const Expr *Ex) { |
3727 | 15.3k | StmtNodeBuilder Bldr(Src, Dst, *currBldrCtx); |
3728 | | |
3729 | 15.3k | for (const auto Pred : Src) { |
3730 | | // Test if the previous node was as the same expression. This can happen |
3731 | | // when the expression fails to evaluate to anything meaningful and |
3732 | | // (as an optimization) we don't generate a node. |
3733 | 15.2k | ProgramPoint P = Pred->getLocation(); |
3734 | 15.2k | if (!P.getAs<PostStmt>() || P.castAs<PostStmt>().getStmt() != Ex) { |
3735 | 0 | continue; |
3736 | 0 | } |
3737 | | |
3738 | 15.2k | ProgramStateRef state = Pred->getState(); |
3739 | 15.2k | SVal V = state->getSVal(Ex, Pred->getLocationContext()); |
3740 | 15.2k | std::optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>(); |
3741 | 15.2k | if (SEV && SEV->isExpression()8.48k ) { |
3742 | 8.48k | const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags = |
3743 | 8.48k | geteagerlyAssumeBinOpBifurcationTags(); |
3744 | | |
3745 | 8.48k | ProgramStateRef StateTrue, StateFalse; |
3746 | 8.48k | std::tie(StateTrue, StateFalse) = state->assume(*SEV); |
3747 | | |
3748 | | // First assume that the condition is true. |
3749 | 8.48k | if (StateTrue) { |
3750 | 6.97k | SVal Val = svalBuilder.makeIntVal(1U, Ex->getType()); |
3751 | 6.97k | StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val); |
3752 | 6.97k | Bldr.generateNode(Ex, Pred, StateTrue, tags.first); |
3753 | 6.97k | } |
3754 | | |
3755 | | // Next, assume that the condition is false. |
3756 | 8.48k | if (StateFalse) { |
3757 | 7.57k | SVal Val = svalBuilder.makeIntVal(0U, Ex->getType()); |
3758 | 7.57k | StateFalse = StateFalse->BindExpr(Ex, Pred->getLocationContext(), Val); |
3759 | 7.57k | Bldr.generateNode(Ex, Pred, StateFalse, tags.second); |
3760 | 7.57k | } |
3761 | 8.48k | } |
3762 | 15.2k | } |
3763 | 15.3k | } |
3764 | | |
3765 | | void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, |
3766 | 6 | ExplodedNodeSet &Dst) { |
3767 | 6 | StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); |
3768 | | // We have processed both the inputs and the outputs. All of the outputs |
3769 | | // should evaluate to Locs. Nuke all of their values. |
3770 | | |
3771 | | // FIXME: Some day in the future it would be nice to allow a "plug-in" |
3772 | | // which interprets the inline asm and stores proper results in the |
3773 | | // outputs. |
3774 | | |
3775 | 6 | ProgramStateRef state = Pred->getState(); |
3776 | | |
3777 | 6 | for (const Expr *O : A->outputs()) { |
3778 | 6 | SVal X = state->getSVal(O, Pred->getLocationContext()); |
3779 | 6 | assert(!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef. |
3780 | | |
3781 | 6 | if (std::optional<Loc> LV = X.getAs<Loc>()) |
3782 | 6 | state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext()); |
3783 | 6 | } |
3784 | | |
3785 | 6 | Bldr.generateNode(A, Pred, state); |
3786 | 6 | } |
3787 | | |
3788 | | void ExprEngine::VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred, |
3789 | 0 | ExplodedNodeSet &Dst) { |
3790 | 0 | StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); |
3791 | 0 | Bldr.generateNode(A, Pred, Pred->getState()); |
3792 | 0 | } |
3793 | | |
3794 | | //===----------------------------------------------------------------------===// |
3795 | | // Visualization. |
3796 | | //===----------------------------------------------------------------------===// |
3797 | | |
3798 | | namespace llvm { |
3799 | | |
3800 | | template<> |
3801 | | struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { |
3802 | 18 | DOTGraphTraits (bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} |
3803 | | |
3804 | 218 | static bool nodeHasBugReport(const ExplodedNode *N) { |
3805 | 218 | BugReporter &BR = static_cast<ExprEngine &>( |
3806 | 218 | N->getState()->getStateManager().getOwningEngine()).getBugReporter(); |
3807 | | |
3808 | 218 | for (const auto &Class : BR.equivalenceClasses()) { |
3809 | 48 | for (const auto &Report : Class.getReports()) { |
3810 | 48 | const auto *PR = dyn_cast<PathSensitiveBugReport>(Report.get()); |
3811 | 48 | if (!PR) |
3812 | 0 | continue; |
3813 | 48 | const ExplodedNode *EN = PR->getErrorNode(); |
3814 | 48 | if (EN->getState() == N->getState() && |
3815 | 48 | EN->getLocation() == N->getLocation()4 ) |
3816 | 2 | return true; |
3817 | 48 | } |
3818 | 48 | } |
3819 | 216 | return false; |
3820 | 218 | } |
3821 | | |
3822 | | /// \p PreCallback: callback before break. |
3823 | | /// \p PostCallback: callback after break. |
3824 | | /// \p Stop: stop iteration if returns @c true |
3825 | | /// \return Whether @c Stop ever returned @c true. |
3826 | | static bool traverseHiddenNodes( |
3827 | | const ExplodedNode *N, |
3828 | | llvm::function_ref<void(const ExplodedNode *)> PreCallback, |
3829 | | llvm::function_ref<void(const ExplodedNode *)> PostCallback, |
3830 | 100 | llvm::function_ref<bool(const ExplodedNode *)> Stop) { |
3831 | 218 | while (true) { |
3832 | 218 | PreCallback(N); |
3833 | 218 | if (Stop(N)) |
3834 | 0 | return true; |
3835 | | |
3836 | 218 | if (N->succ_size() != 1 || !isNodeHidden(N->getFirstSucc(), nullptr)209 ) |
3837 | 100 | break; |
3838 | 118 | PostCallback(N); |
3839 | | |
3840 | 118 | N = N->getFirstSucc(); |
3841 | 118 | } |
3842 | 100 | return false; |
3843 | 100 | } |
3844 | | |
3845 | 400 | static bool isNodeHidden(const ExplodedNode *N, const ExplodedGraph *G) { |
3846 | 400 | return N->isTrivial(); |
3847 | 400 | } |
3848 | | |
3849 | 100 | static std::string getNodeLabel(const ExplodedNode *N, ExplodedGraph *G){ |
3850 | 100 | std::string Buf; |
3851 | 100 | llvm::raw_string_ostream Out(Buf); |
3852 | | |
3853 | 100 | const bool IsDot = true; |
3854 | 100 | const unsigned int Space = 1; |
3855 | 100 | ProgramStateRef State = N->getState(); |
3856 | | |
3857 | 100 | Out << "{ \"state_id\": " << State->getID() |
3858 | 100 | << ",\\l"; |
3859 | | |
3860 | 100 | Indent(Out, Space, IsDot) << "\"program_points\": [\\l"; |
3861 | | |
3862 | | // Dump program point for all the previously skipped nodes. |
3863 | 100 | traverseHiddenNodes( |
3864 | 100 | N, |
3865 | 218 | [&](const ExplodedNode *OtherNode) { |
3866 | 218 | Indent(Out, Space + 1, IsDot) << "{ "; |
3867 | 218 | OtherNode->getLocation().printJson(Out, /*NL=*/"\\l"); |
3868 | 218 | Out << ", \"tag\": "; |
3869 | 218 | if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag()) |
3870 | 64 | Out << '\"' << Tag->getTagDescription() << '\"'; |
3871 | 154 | else |
3872 | 154 | Out << "null"; |
3873 | 218 | Out << ", \"node_id\": " << OtherNode->getID() << |
3874 | 218 | ", \"is_sink\": " << OtherNode->isSink() << |
3875 | 218 | ", \"has_report\": " << nodeHasBugReport(OtherNode) << " }"; |
3876 | 218 | }, |
3877 | | // Adds a comma and a new-line between each program point. |
3878 | 118 | [&](const ExplodedNode *) { Out << ",\\l"; }, |
3879 | 218 | [&](const ExplodedNode *) { return false; }); |
3880 | | |
3881 | 100 | Out << "\\l"; // Adds a new-line to the last program point. |
3882 | 100 | Indent(Out, Space, IsDot) << "],\\l"; |
3883 | | |
3884 | 100 | State->printDOT(Out, N->getLocationContext(), Space); |
3885 | | |
3886 | 100 | Out << "\\l}\\l"; |
3887 | 100 | return Out.str(); |
3888 | 100 | } |
3889 | | }; |
3890 | | |
3891 | | } // namespace llvm |
3892 | | |
3893 | 0 | void ExprEngine::ViewGraph(bool trim) { |
3894 | 0 | std::string Filename = DumpGraph(trim); |
3895 | 0 | llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT); |
3896 | 0 | } |
3897 | | |
3898 | 0 | void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode *> Nodes) { |
3899 | 0 | std::string Filename = DumpGraph(Nodes); |
3900 | 0 | llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT); |
3901 | 0 | } |
3902 | | |
3903 | 9 | std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) { |
3904 | 9 | if (trim) { |
3905 | 1 | std::vector<const ExplodedNode *> Src; |
3906 | | |
3907 | | // Iterate through the reports and get their nodes. |
3908 | 1 | for (const auto &Class : BR.equivalenceClasses()) { |
3909 | 1 | const auto *R = |
3910 | 1 | dyn_cast<PathSensitiveBugReport>(Class.getReports()[0].get()); |
3911 | 1 | if (!R) |
3912 | 0 | continue; |
3913 | 1 | const auto *N = const_cast<ExplodedNode *>(R->getErrorNode()); |
3914 | 1 | Src.push_back(N); |
3915 | 1 | } |
3916 | 1 | return DumpGraph(Src, Filename); |
3917 | 1 | } |
3918 | | |
3919 | 8 | return llvm::WriteGraph(&G, "ExprEngine", /*ShortNames=*/false, |
3920 | 8 | /*Title=*/"Exploded Graph", |
3921 | 8 | /*Filename=*/std::string(Filename)); |
3922 | 9 | } |
3923 | | |
3924 | | std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode *> Nodes, |
3925 | 1 | StringRef Filename) { |
3926 | 1 | std::unique_ptr<ExplodedGraph> TrimmedG(G.trim(Nodes)); |
3927 | | |
3928 | 1 | if (!TrimmedG.get()) { |
3929 | 0 | llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; |
3930 | 0 | return ""; |
3931 | 0 | } |
3932 | | |
3933 | 1 | return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine", |
3934 | 1 | /*ShortNames=*/false, |
3935 | 1 | /*Title=*/"Trimmed Exploded Graph", |
3936 | 1 | /*Filename=*/std::string(Filename)); |
3937 | 1 | } |
3938 | | |
3939 | 70.5k | void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() { |
3940 | 70.5k | static int index = 0; |
3941 | 70.5k | return &index; |
3942 | 70.5k | } |
3943 | | |
3944 | 0 | void ExprEngine::anchor() { } |