/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- IteratorModeling.cpp --------------------------------------*- C++ -*--// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // Defines a modeling-checker for modeling STL iterator-like iterators. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | // |
13 | | // In the code, iterator can be represented as a: |
14 | | // * type-I: typedef-ed pointer. Operations over such iterator, such as |
15 | | // comparisons or increments, are modeled straightforwardly by the |
16 | | // analyzer. |
17 | | // * type-II: structure with its method bodies available. Operations over such |
18 | | // iterator are inlined by the analyzer, and results of modeling |
19 | | // these operations are exposing implementation details of the |
20 | | // iterators, which is not necessarily helping. |
21 | | // * type-III: completely opaque structure. Operations over such iterator are |
22 | | // modeled conservatively, producing conjured symbols everywhere. |
23 | | // |
24 | | // To handle all these types in a common way we introduce a structure called |
25 | | // IteratorPosition which is an abstraction of the position the iterator |
26 | | // represents using symbolic expressions. The checker handles all the |
27 | | // operations on this structure. |
28 | | // |
29 | | // Additionally, depending on the circumstances, operators of types II and III |
30 | | // can be represented as: |
31 | | // * type-IIa, type-IIIa: conjured structure symbols - when returned by value |
32 | | // from conservatively evaluated methods such as |
33 | | // `.begin()`. |
34 | | // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as |
35 | | // variables or temporaries, when the iterator object is |
36 | | // currently treated as an lvalue. |
37 | | // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the |
38 | | // iterator object is treated as an rvalue taken of a |
39 | | // particular lvalue, eg. a copy of "type-a" iterator |
40 | | // object, or an iterator that existed before the |
41 | | // analysis has started. |
42 | | // |
43 | | // To handle any of these three different representations stored in an SVal we |
44 | | // use setter and getters functions which separate the three cases. To store |
45 | | // them we use a pointer union of symbol and memory region. |
46 | | // |
47 | | // The checker works the following way: We record the begin and the |
48 | | // past-end iterator for all containers whenever their `.begin()` and `.end()` |
49 | | // are called. Since the Constraint Manager cannot handle such SVals we need |
50 | | // to take over its role. We post-check equality and non-equality comparisons |
51 | | // and record that the two sides are equal if we are in the 'equal' branch |
52 | | // (true-branch for `==` and false-branch for `!=`). |
53 | | // |
54 | | // In case of type-I or type-II iterators we get a concrete integer as a result |
55 | | // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In |
56 | | // this latter case we record the symbol and reload it in evalAssume() and do |
57 | | // the propagation there. We also handle (maybe double) negated comparisons |
58 | | // which are represented in the form of (x == 0 or x != 0) where x is the |
59 | | // comparison itself. |
60 | | // |
61 | | // Since `SimpleConstraintManager` cannot handle complex symbolic expressions |
62 | | // we only use expressions of the format S, S+n or S-n for iterator positions |
63 | | // where S is a conjured symbol and n is an unsigned concrete integer. When |
64 | | // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as |
65 | | // a constraint which we later retrieve when doing an actual comparison. |
66 | | |
67 | | #include "clang/AST/DeclTemplate.h" |
68 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
69 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
70 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
71 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" |
72 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
73 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
74 | | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" |
75 | | #include "llvm/ADT/STLExtras.h" |
76 | | |
77 | | #include "Iterator.h" |
78 | | |
79 | | #include <utility> |
80 | | |
81 | | using namespace clang; |
82 | | using namespace ento; |
83 | | using namespace iterator; |
84 | | |
85 | | namespace { |
86 | | |
87 | | class IteratorModeling |
88 | | : public Checker<check::PostCall, check::PostStmt<UnaryOperator>, |
89 | | check::PostStmt<BinaryOperator>, |
90 | | check::PostStmt<MaterializeTemporaryExpr>, |
91 | | check::Bind, check::LiveSymbols, check::DeadSymbols> { |
92 | | |
93 | | using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *, |
94 | | SVal, SVal, SVal) const; |
95 | | |
96 | | void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call, |
97 | | OverloadedOperatorKind Op) const; |
98 | | void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call, |
99 | | const Expr *OrigExpr, |
100 | | const AdvanceFn *Handler) const; |
101 | | |
102 | | void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal, |
103 | | const SVal &LVal, const SVal &RVal, |
104 | | OverloadedOperatorKind Op) const; |
105 | | void processComparison(CheckerContext &C, ProgramStateRef State, |
106 | | SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal, |
107 | | OverloadedOperatorKind Op) const; |
108 | | void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, |
109 | | bool Postfix) const; |
110 | | void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, |
111 | | bool Postfix) const; |
112 | | void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, |
113 | | OverloadedOperatorKind Op, const SVal &RetVal, |
114 | | const SVal &Iterator, const SVal &Amount) const; |
115 | | void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator, |
116 | | OverloadedOperatorKind OK, SVal Offset) const; |
117 | | void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, |
118 | | SVal Amount) const; |
119 | | void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, |
120 | | SVal Amount) const; |
121 | | void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, |
122 | | SVal Amount) const; |
123 | | void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal, |
124 | | const MemRegion *Cont) const; |
125 | | bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const; |
126 | | void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, |
127 | | const char *Sep) const override; |
128 | | |
129 | | // std::advance, std::prev & std::next |
130 | | CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = { |
131 | | // template<class InputIt, class Distance> |
132 | | // void advance(InputIt& it, Distance n); |
133 | | {{{"std", "advance"}, 2}, &IteratorModeling::handleAdvance}, |
134 | | |
135 | | // template<class BidirIt> |
136 | | // BidirIt prev( |
137 | | // BidirIt it, |
138 | | // typename std::iterator_traits<BidirIt>::difference_type n = 1); |
139 | | {{{"std", "prev"}, 2}, &IteratorModeling::handlePrev}, |
140 | | |
141 | | // template<class ForwardIt> |
142 | | // ForwardIt next( |
143 | | // ForwardIt it, |
144 | | // typename std::iterator_traits<ForwardIt>::difference_type n = 1); |
145 | | {{{"std", "next"}, 2}, &IteratorModeling::handleNext}, |
146 | | }; |
147 | | |
148 | | public: |
149 | 19 | IteratorModeling() = default; |
150 | | |
151 | | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
152 | | void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; |
153 | | void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const; |
154 | | void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const; |
155 | | void checkPostStmt(const MaterializeTemporaryExpr *MTE, |
156 | | CheckerContext &C) const; |
157 | | void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; |
158 | | void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; |
159 | | }; |
160 | | |
161 | | bool isSimpleComparisonOperator(OverloadedOperatorKind OK); |
162 | | bool isSimpleComparisonOperator(BinaryOperatorKind OK); |
163 | | ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val); |
164 | | ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, |
165 | | SymbolRef Sym2, bool Equal); |
166 | | bool isBoundThroughLazyCompoundVal(const Environment &Env, |
167 | | const MemRegion *Reg); |
168 | | const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call); |
169 | | |
170 | | } // namespace |
171 | | |
172 | | void IteratorModeling::checkPostCall(const CallEvent &Call, |
173 | 17.4k | CheckerContext &C) const { |
174 | | // Record new iterator positions and iterator position changes |
175 | 17.4k | const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); |
176 | 17.4k | if (!Func) |
177 | 0 | return; |
178 | | |
179 | 17.4k | if (Func->isOverloadedOperator()) { |
180 | 1.29k | const auto Op = Func->getOverloadedOperator(); |
181 | 1.29k | handleOverloadedOperator(C, Call, Op); |
182 | 1.29k | return; |
183 | 1.29k | } |
184 | | |
185 | 16.1k | const auto *OrigExpr = Call.getOriginExpr(); |
186 | 16.1k | if (!OrigExpr) |
187 | 31 | return; |
188 | | |
189 | 16.0k | const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(Call); |
190 | 16.0k | if (Handler) { |
191 | 250 | handleAdvanceLikeFunction(C, Call, OrigExpr, Handler); |
192 | 250 | return; |
193 | 250 | } |
194 | | |
195 | 15.8k | if (!isIteratorType(Call.getResultType())) |
196 | 9.32k | return; |
197 | | |
198 | 6.51k | auto State = C.getState(); |
199 | | |
200 | | // Already bound to container? |
201 | 6.51k | if (getIteratorPosition(State, Call.getReturnValue())) |
202 | 3.66k | return; |
203 | | |
204 | | // Copy-like and move constructors |
205 | 2.84k | if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 11.99k ) { |
206 | 1.99k | if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) { |
207 | 349 | State = setIteratorPosition(State, Call.getReturnValue(), *Pos); |
208 | 349 | if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) { |
209 | 0 | State = removeIteratorPosition(State, Call.getArgSVal(0)); |
210 | 0 | } |
211 | 349 | C.addTransition(State); |
212 | 349 | return; |
213 | 349 | } |
214 | 1.99k | } |
215 | | |
216 | | // Assumption: if return value is an iterator which is not yet bound to a |
217 | | // container, then look for the first iterator argument of the |
218 | | // same type as the return value and bind the return value to |
219 | | // the same container. This approach works for STL algorithms. |
220 | | // FIXME: Add a more conservative mode |
221 | 4.89k | for (unsigned i = 0; 2.49k i < Call.getNumArgs(); ++i2.39k ) { |
222 | 2.76k | if (isIteratorType(Call.getArgExpr(i)->getType()) && |
223 | 2.76k | Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType( |
224 | 2.52k | C.getASTContext()).getTypePtr() == |
225 | 2.52k | Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) { |
226 | 481 | if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) { |
227 | 363 | assignToContainer(C, OrigExpr, Call.getReturnValue(), |
228 | 363 | Pos->getContainer()); |
229 | 363 | return; |
230 | 363 | } |
231 | 481 | } |
232 | 2.76k | } |
233 | 2.49k | } |
234 | | |
235 | | void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S, |
236 | 3.53k | CheckerContext &C) const { |
237 | 3.53k | auto State = C.getState(); |
238 | 3.53k | const auto *Pos = getIteratorPosition(State, Val); |
239 | 3.53k | if (Pos) { |
240 | 1.17k | State = setIteratorPosition(State, Loc, *Pos); |
241 | 1.17k | C.addTransition(State); |
242 | 2.36k | } else { |
243 | 2.36k | const auto *OldPos = getIteratorPosition(State, Loc); |
244 | 2.36k | if (OldPos) { |
245 | 48 | State = removeIteratorPosition(State, Loc); |
246 | 48 | C.addTransition(State); |
247 | 48 | } |
248 | 2.36k | } |
249 | 3.53k | } |
250 | | |
251 | | void IteratorModeling::checkPostStmt(const UnaryOperator *UO, |
252 | 971 | CheckerContext &C) const { |
253 | 971 | UnaryOperatorKind OK = UO->getOpcode(); |
254 | 971 | if (!isIncrementOperator(OK) && !isDecrementOperator(OK)831 ) |
255 | 681 | return; |
256 | | |
257 | 290 | auto &SVB = C.getSValBuilder(); |
258 | 290 | handlePtrIncrOrDecr(C, UO->getSubExpr(), |
259 | 290 | isIncrementOperator(OK) ? OO_Plus140 : OO_Minus150 , |
260 | 290 | SVB.makeArrayIndex(1)); |
261 | 290 | } |
262 | | |
263 | | void IteratorModeling::checkPostStmt(const BinaryOperator *BO, |
264 | 912 | CheckerContext &C) const { |
265 | 912 | const ProgramStateRef State = C.getState(); |
266 | 912 | const BinaryOperatorKind OK = BO->getOpcode(); |
267 | 912 | const Expr *const LHS = BO->getLHS(); |
268 | 912 | const Expr *const RHS = BO->getRHS(); |
269 | 912 | const SVal LVal = State->getSVal(LHS, C.getLocationContext()); |
270 | 912 | const SVal RVal = State->getSVal(RHS, C.getLocationContext()); |
271 | | |
272 | 912 | if (isSimpleComparisonOperator(BO->getOpcode())) { |
273 | 400 | SVal Result = State->getSVal(BO, C.getLocationContext()); |
274 | 400 | handleComparison(C, BO, Result, LVal, RVal, |
275 | 400 | BinaryOperator::getOverloadedOperator(OK)); |
276 | 512 | } else if (isRandomIncrOrDecrOperator(OK)) { |
277 | | // In case of operator+ the iterator can be either on the LHS (eg.: it + 1), |
278 | | // or on the RHS (eg.: 1 + it). Both cases are modeled. |
279 | 198 | const bool IsIterOnLHS = BO->getLHS()->getType()->isPointerType(); |
280 | 198 | const Expr *const &IterExpr = IsIterOnLHS ? LHS170 : RHS28 ; |
281 | 198 | const Expr *const &AmountExpr = IsIterOnLHS ? RHS170 : LHS28 ; |
282 | | |
283 | | // The non-iterator side must have an integral or enumeration type. |
284 | 198 | if (!AmountExpr->getType()->isIntegralOrEnumerationType()) |
285 | 8 | return; |
286 | 190 | const SVal &AmountVal = IsIterOnLHS ? RVal162 : LVal28 ; |
287 | 190 | handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK), |
288 | 190 | AmountVal); |
289 | 190 | } |
290 | 912 | } |
291 | | |
292 | | void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE, |
293 | 5.02k | CheckerContext &C) const { |
294 | | /* Transfer iterator state to temporary objects */ |
295 | 5.02k | auto State = C.getState(); |
296 | 5.02k | const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr())); |
297 | 5.02k | if (!Pos) |
298 | 1.99k | return; |
299 | 3.03k | State = setIteratorPosition(State, C.getSVal(MTE), *Pos); |
300 | 3.03k | C.addTransition(State); |
301 | 3.03k | } |
302 | | |
303 | | void IteratorModeling::checkLiveSymbols(ProgramStateRef State, |
304 | 40.0k | SymbolReaper &SR) const { |
305 | | // Keep symbolic expressions of iterator positions alive |
306 | 40.0k | auto RegionMap = State->get<IteratorRegionMap>(); |
307 | 61.8k | for (const IteratorPosition &Pos : llvm::make_second_range(RegionMap)) { |
308 | 61.8k | for (SymbolRef Sym : Pos.getOffset()->symbols()) |
309 | 74.9k | if (isa<SymbolData>(Sym)) |
310 | 61.8k | SR.markLive(Sym); |
311 | 61.8k | } |
312 | | |
313 | 40.0k | auto SymbolMap = State->get<IteratorSymbolMap>(); |
314 | 40.0k | for (const IteratorPosition &Pos : llvm::make_second_range(SymbolMap)) { |
315 | 899 | for (SymbolRef Sym : Pos.getOffset()->symbols()) |
316 | 913 | if (isa<SymbolData>(Sym)) |
317 | 899 | SR.markLive(Sym); |
318 | 899 | } |
319 | 40.0k | } |
320 | | |
321 | | void IteratorModeling::checkDeadSymbols(SymbolReaper &SR, |
322 | 40.0k | CheckerContext &C) const { |
323 | | // Cleanup |
324 | 40.0k | auto State = C.getState(); |
325 | | |
326 | 40.0k | auto RegionMap = State->get<IteratorRegionMap>(); |
327 | 61.8k | for (const auto &Reg : RegionMap) { |
328 | 61.8k | if (!SR.isLiveRegion(Reg.first)) { |
329 | | // The region behind the `LazyCompoundVal` is often cleaned up before |
330 | | // the `LazyCompoundVal` itself. If there are iterator positions keyed |
331 | | // by these regions their cleanup must be deferred. |
332 | 6.38k | if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) { |
333 | 4.84k | State = State->remove<IteratorRegionMap>(Reg.first); |
334 | 4.84k | } |
335 | 6.38k | } |
336 | 61.8k | } |
337 | | |
338 | 40.0k | auto SymbolMap = State->get<IteratorSymbolMap>(); |
339 | 40.0k | for (const auto &Sym : SymbolMap) { |
340 | 899 | if (!SR.isLive(Sym.first)) { |
341 | 143 | State = State->remove<IteratorSymbolMap>(Sym.first); |
342 | 143 | } |
343 | 899 | } |
344 | | |
345 | 40.0k | C.addTransition(State); |
346 | 40.0k | } |
347 | | |
348 | | void |
349 | | IteratorModeling::handleOverloadedOperator(CheckerContext &C, |
350 | | const CallEvent &Call, |
351 | 1.29k | OverloadedOperatorKind Op) const { |
352 | 1.29k | if (isSimpleComparisonOperator(Op)) { |
353 | 350 | const auto *OrigExpr = Call.getOriginExpr(); |
354 | 350 | if (!OrigExpr) |
355 | 0 | return; |
356 | | |
357 | 350 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { |
358 | 344 | handleComparison(C, OrigExpr, Call.getReturnValue(), |
359 | 344 | InstCall->getCXXThisVal(), Call.getArgSVal(0), Op); |
360 | 344 | return; |
361 | 344 | } |
362 | | |
363 | 6 | handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0), |
364 | 6 | Call.getArgSVal(1), Op); |
365 | 6 | return; |
366 | 949 | } else if (isRandomIncrOrDecrOperator(Op)) { |
367 | 238 | const auto *OrigExpr = Call.getOriginExpr(); |
368 | 238 | if (!OrigExpr) |
369 | 0 | return; |
370 | | |
371 | 238 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { |
372 | 226 | if (Call.getNumArgs() >= 1 && |
373 | 226 | Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { |
374 | 220 | handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), |
375 | 220 | InstCall->getCXXThisVal(), Call.getArgSVal(0)); |
376 | 220 | return; |
377 | 220 | } |
378 | 226 | } else if (12 Call.getNumArgs() >= 212 ) { |
379 | 12 | const Expr *FirstArg = Call.getArgExpr(0); |
380 | 12 | const Expr *SecondArg = Call.getArgExpr(1); |
381 | 12 | const QualType FirstType = FirstArg->getType(); |
382 | 12 | const QualType SecondType = SecondArg->getType(); |
383 | | |
384 | 12 | if (FirstType->isIntegralOrEnumerationType() || |
385 | 12 | SecondType->isIntegralOrEnumerationType()0 ) { |
386 | | // In case of operator+ the iterator can be either on the LHS (eg.: |
387 | | // it + 1), or on the RHS (eg.: 1 + it). Both cases are modeled. |
388 | 12 | const bool IsIterFirst = FirstType->isStructureOrClassType(); |
389 | 12 | const SVal FirstArg = Call.getArgSVal(0); |
390 | 12 | const SVal SecondArg = Call.getArgSVal(1); |
391 | 12 | const SVal &Iterator = IsIterFirst ? FirstArg0 : SecondArg; |
392 | 12 | const SVal &Amount = IsIterFirst ? SecondArg0 : FirstArg; |
393 | | |
394 | 12 | handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), |
395 | 12 | Iterator, Amount); |
396 | 12 | return; |
397 | 12 | } |
398 | 12 | } |
399 | 711 | } else if (isIncrementOperator(Op)) { |
400 | 288 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { |
401 | 288 | handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), |
402 | 288 | Call.getNumArgs()); |
403 | 288 | return; |
404 | 288 | } |
405 | | |
406 | 0 | handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0), |
407 | 0 | Call.getNumArgs()); |
408 | 0 | return; |
409 | 423 | } else if (isDecrementOperator(Op)) { |
410 | 282 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { |
411 | 282 | handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), |
412 | 282 | Call.getNumArgs()); |
413 | 282 | return; |
414 | 282 | } |
415 | | |
416 | 0 | handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0), |
417 | 0 | Call.getNumArgs()); |
418 | 0 | return; |
419 | 282 | } |
420 | 1.29k | } |
421 | | |
422 | | void |
423 | | IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C, |
424 | | const CallEvent &Call, |
425 | | const Expr *OrigExpr, |
426 | 250 | const AdvanceFn *Handler) const { |
427 | 250 | if (!C.wasInlined) { |
428 | 16 | (this->**Handler)(C, OrigExpr, Call.getReturnValue(), |
429 | 16 | Call.getArgSVal(0), Call.getArgSVal(1)); |
430 | 16 | return; |
431 | 16 | } |
432 | | |
433 | | // If std::advance() was inlined, but a non-standard function it calls inside |
434 | | // was not, then we have to model it explicitly |
435 | 234 | const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier(); |
436 | 234 | if (IdInfo) { |
437 | 234 | if (IdInfo->getName() == "advance") { |
438 | 138 | if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) { |
439 | 36 | (this->**Handler)(C, OrigExpr, Call.getReturnValue(), |
440 | 36 | Call.getArgSVal(0), Call.getArgSVal(1)); |
441 | 36 | } |
442 | 138 | } |
443 | 234 | } |
444 | 234 | } |
445 | | |
446 | | void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE, |
447 | | SVal RetVal, const SVal &LVal, |
448 | | const SVal &RVal, |
449 | 750 | OverloadedOperatorKind Op) const { |
450 | | // Record the operands and the operator of the comparison for the next |
451 | | // evalAssume, if the result is a symbolic expression. If it is a concrete |
452 | | // value (only one branch is possible), then transfer the state between |
453 | | // the operands according to the operator and the result |
454 | 750 | auto State = C.getState(); |
455 | 750 | const auto *LPos = getIteratorPosition(State, LVal); |
456 | 750 | const auto *RPos = getIteratorPosition(State, RVal); |
457 | 750 | const MemRegion *Cont = nullptr; |
458 | 750 | if (LPos) { |
459 | 363 | Cont = LPos->getContainer(); |
460 | 387 | } else if (RPos) { |
461 | 2 | Cont = RPos->getContainer(); |
462 | 2 | } |
463 | 750 | if (!Cont) |
464 | 385 | return; |
465 | | |
466 | | // At least one of the iterators has recorded positions. If one of them does |
467 | | // not then create a new symbol for the offset. |
468 | 365 | SymbolRef Sym; |
469 | 365 | if (!LPos || !RPos363 ) { |
470 | 8 | auto &SymMgr = C.getSymbolManager(); |
471 | 8 | Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(), |
472 | 8 | C.getASTContext().LongTy, C.blockCount()); |
473 | 8 | State = assumeNoOverflow(State, Sym, 4); |
474 | 8 | } |
475 | | |
476 | 365 | if (!LPos) { |
477 | 2 | State = setIteratorPosition(State, LVal, |
478 | 2 | IteratorPosition::getPosition(Cont, Sym)); |
479 | 2 | LPos = getIteratorPosition(State, LVal); |
480 | 363 | } else if (!RPos) { |
481 | 6 | State = setIteratorPosition(State, RVal, |
482 | 6 | IteratorPosition::getPosition(Cont, Sym)); |
483 | 6 | RPos = getIteratorPosition(State, RVal); |
484 | 6 | } |
485 | | |
486 | | // If the value for which we just tried to set a new iterator position is |
487 | | // an `SVal`for which no iterator position can be set then the setting was |
488 | | // unsuccessful. We cannot handle the comparison in this case. |
489 | 365 | if (!LPos || !RPos) |
490 | 6 | return; |
491 | | |
492 | | // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol |
493 | | // instead. |
494 | 359 | if (RetVal.isUnknown()) { |
495 | 27 | auto &SymMgr = C.getSymbolManager(); |
496 | 27 | auto *LCtx = C.getLocationContext(); |
497 | 27 | RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol( |
498 | 27 | CE, LCtx, C.getASTContext().BoolTy, C.blockCount())); |
499 | 27 | State = State->BindExpr(CE, LCtx, RetVal); |
500 | 27 | } |
501 | | |
502 | 359 | processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op); |
503 | 359 | } |
504 | | |
505 | | void IteratorModeling::processComparison(CheckerContext &C, |
506 | | ProgramStateRef State, SymbolRef Sym1, |
507 | | SymbolRef Sym2, const SVal &RetVal, |
508 | 359 | OverloadedOperatorKind Op) const { |
509 | 359 | if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { |
510 | 108 | if ((State = relateSymbols(State, Sym1, Sym2, |
511 | 108 | (Op == OO_EqualEqual) == |
512 | 108 | (TruthVal->getValue() != 0)))) { |
513 | 100 | C.addTransition(State); |
514 | 100 | } else { |
515 | 8 | C.generateSink(State, C.getPredecessor()); |
516 | 8 | } |
517 | 108 | return; |
518 | 108 | } |
519 | | |
520 | 251 | const auto ConditionVal = RetVal.getAs<DefinedSVal>(); |
521 | 251 | if (!ConditionVal) |
522 | 0 | return; |
523 | | |
524 | 251 | if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) { |
525 | 138 | StateTrue = StateTrue->assume(*ConditionVal, true); |
526 | 138 | C.addTransition(StateTrue); |
527 | 138 | } |
528 | | |
529 | 251 | if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) { |
530 | 170 | StateFalse = StateFalse->assume(*ConditionVal, false); |
531 | 170 | C.addTransition(StateFalse); |
532 | 170 | } |
533 | 251 | } |
534 | | |
535 | | void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal, |
536 | 288 | const SVal &Iter, bool Postfix) const { |
537 | | // Increment the symbolic expressions which represents the position of the |
538 | | // iterator |
539 | 288 | auto State = C.getState(); |
540 | 288 | auto &BVF = C.getSymbolManager().getBasicVals(); |
541 | | |
542 | 288 | const auto *Pos = getIteratorPosition(State, Iter); |
543 | 288 | if (!Pos) |
544 | 0 | return; |
545 | | |
546 | 288 | auto NewState = |
547 | 288 | advancePosition(State, Iter, OO_Plus, |
548 | 288 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); |
549 | 288 | assert(NewState && |
550 | 288 | "Advancing position by concrete int should always be successful"); |
551 | | |
552 | 288 | const auto *NewPos = getIteratorPosition(NewState, Iter); |
553 | 288 | assert(NewPos && |
554 | 288 | "Iterator should have position after successful advancement"); |
555 | | |
556 | 288 | State = setIteratorPosition(State, Iter, *NewPos); |
557 | 288 | State = setIteratorPosition(State, RetVal, Postfix ? *Pos32 : *NewPos256 ); |
558 | 288 | C.addTransition(State); |
559 | 288 | } |
560 | | |
561 | | void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal, |
562 | 282 | const SVal &Iter, bool Postfix) const { |
563 | | // Decrement the symbolic expressions which represents the position of the |
564 | | // iterator |
565 | 282 | auto State = C.getState(); |
566 | 282 | auto &BVF = C.getSymbolManager().getBasicVals(); |
567 | | |
568 | 282 | const auto *Pos = getIteratorPosition(State, Iter); |
569 | 282 | if (!Pos) |
570 | 0 | return; |
571 | | |
572 | 282 | auto NewState = |
573 | 282 | advancePosition(State, Iter, OO_Minus, |
574 | 282 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); |
575 | 282 | assert(NewState && |
576 | 282 | "Advancing position by concrete int should always be successful"); |
577 | | |
578 | 282 | const auto *NewPos = getIteratorPosition(NewState, Iter); |
579 | 282 | assert(NewPos && |
580 | 282 | "Iterator should have position after successful advancement"); |
581 | | |
582 | 282 | State = setIteratorPosition(State, Iter, *NewPos); |
583 | 282 | State = setIteratorPosition(State, RetVal, Postfix ? *Pos16 : *NewPos266 ); |
584 | 282 | C.addTransition(State); |
585 | 282 | } |
586 | | |
587 | | void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, |
588 | | OverloadedOperatorKind Op, |
589 | | const SVal &RetVal, |
590 | | const SVal &Iterator, |
591 | 284 | const SVal &Amount) const { |
592 | | // Increment or decrement the symbolic expressions which represents the |
593 | | // position of the iterator |
594 | 284 | auto State = C.getState(); |
595 | | |
596 | 284 | const auto *Pos = getIteratorPosition(State, Iterator); |
597 | 284 | if (!Pos) |
598 | 2 | return; |
599 | | |
600 | 282 | const auto *Value = &Amount; |
601 | 282 | SVal Val; |
602 | 282 | if (auto LocAmount = Amount.getAs<Loc>()) { |
603 | 2 | Val = State->getRawSVal(*LocAmount); |
604 | 2 | Value = &Val; |
605 | 2 | } |
606 | | |
607 | 282 | const auto &TgtVal = |
608 | 282 | (Op == OO_PlusEqual || Op == OO_MinusEqual84 ) ? Iterator220 : RetVal62 ; |
609 | | |
610 | | // `AdvancedState` is a state where the position of `LHS` is advanced. We |
611 | | // only need this state to retrieve the new position, but we do not want |
612 | | // to change the position of `LHS` (in every case). |
613 | 282 | auto AdvancedState = advancePosition(State, Iterator, Op, *Value); |
614 | 282 | if (AdvancedState) { |
615 | 280 | const auto *NewPos = getIteratorPosition(AdvancedState, Iterator); |
616 | 280 | assert(NewPos && |
617 | 280 | "Iterator should have position after successful advancement"); |
618 | | |
619 | 280 | State = setIteratorPosition(State, TgtVal, *NewPos); |
620 | 280 | C.addTransition(State); |
621 | 280 | } else { |
622 | 2 | assignToContainer(C, CE, TgtVal, Pos->getContainer()); |
623 | 2 | } |
624 | 282 | } |
625 | | |
626 | | void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C, |
627 | | const Expr *Iterator, |
628 | | OverloadedOperatorKind OK, |
629 | 480 | SVal Offset) const { |
630 | 480 | if (!isa<DefinedSVal>(Offset)) |
631 | 0 | return; |
632 | | |
633 | 480 | QualType PtrType = Iterator->getType(); |
634 | 480 | if (!PtrType->isPointerType()) |
635 | 10 | return; |
636 | 470 | QualType ElementType = PtrType->getPointeeType(); |
637 | | |
638 | 470 | ProgramStateRef State = C.getState(); |
639 | 470 | SVal OldVal = State->getSVal(Iterator, C.getLocationContext()); |
640 | | |
641 | 470 | const IteratorPosition *OldPos = getIteratorPosition(State, OldVal); |
642 | 470 | if (!OldPos) |
643 | 400 | return; |
644 | | |
645 | 70 | SVal NewVal; |
646 | 70 | if (OK == OO_Plus || OK == OO_PlusEqual36 ) { |
647 | 40 | NewVal = State->getLValue(ElementType, Offset, OldVal); |
648 | 40 | } else { |
649 | 30 | auto &SVB = C.getSValBuilder(); |
650 | 30 | SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>()); |
651 | 30 | NewVal = State->getLValue(ElementType, NegatedOffset, OldVal); |
652 | 30 | } |
653 | | |
654 | | // `AdvancedState` is a state where the position of `Old` is advanced. We |
655 | | // only need this state to retrieve the new position, but we do not want |
656 | | // ever to change the position of `OldVal`. |
657 | 70 | auto AdvancedState = advancePosition(State, OldVal, OK, Offset); |
658 | 70 | if (AdvancedState) { |
659 | 64 | const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal); |
660 | 64 | assert(NewPos && |
661 | 64 | "Iterator should have position after successful advancement"); |
662 | | |
663 | 64 | ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos); |
664 | 64 | C.addTransition(NewState); |
665 | 64 | } else { |
666 | 6 | assignToContainer(C, Iterator, NewVal, OldPos->getContainer()); |
667 | 6 | } |
668 | 70 | } |
669 | | |
670 | | void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE, |
671 | | SVal RetVal, SVal Iter, |
672 | 44 | SVal Amount) const { |
673 | 44 | handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount); |
674 | 44 | } |
675 | | |
676 | | void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE, |
677 | 4 | SVal RetVal, SVal Iter, SVal Amount) const { |
678 | 4 | handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount); |
679 | 4 | } |
680 | | |
681 | | void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE, |
682 | 4 | SVal RetVal, SVal Iter, SVal Amount) const { |
683 | 4 | handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount); |
684 | 4 | } |
685 | | |
686 | | void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE, |
687 | | const SVal &RetVal, |
688 | 371 | const MemRegion *Cont) const { |
689 | 371 | Cont = Cont->getMostDerivedObjectRegion(); |
690 | | |
691 | 371 | auto State = C.getState(); |
692 | 371 | const auto *LCtx = C.getLocationContext(); |
693 | 371 | State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount()); |
694 | | |
695 | 371 | C.addTransition(State); |
696 | 371 | } |
697 | | |
698 | | bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter, |
699 | 138 | const Expr *CE) const { |
700 | | // Compare the iterator position before and after the call. (To be called |
701 | | // from `checkPostCall()`.) |
702 | 138 | const auto StateAfter = C.getState(); |
703 | | |
704 | 138 | const auto *PosAfter = getIteratorPosition(StateAfter, Iter); |
705 | | // If we have no position after the call of `std::advance`, then we are not |
706 | | // interested. (Modeling of an inlined `std::advance()` should not remove the |
707 | | // position in any case.) |
708 | 138 | if (!PosAfter) |
709 | 0 | return false; |
710 | | |
711 | 138 | const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE); |
712 | 138 | assert(N && "Any call should have a `CallEnter` node."); |
713 | | |
714 | 138 | const auto StateBefore = N->getState(); |
715 | 138 | const auto *PosBefore = getIteratorPosition(StateBefore, Iter); |
716 | | // FIXME: `std::advance()` should not create a new iterator position but |
717 | | // change existing ones. However, in case of iterators implemented as |
718 | | // pointers the handling of parameters in `std::advance()`-like |
719 | | // functions is still incomplete which may result in cases where |
720 | | // the new position is assigned to the wrong pointer. This causes |
721 | | // crash if we use an assertion here. |
722 | 138 | if (!PosBefore) |
723 | 0 | return false; |
724 | | |
725 | 138 | return PosBefore->getOffset() == PosAfter->getOffset(); |
726 | 138 | } |
727 | | |
728 | | void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State, |
729 | 36 | const char *NL, const char *Sep) const { |
730 | 36 | auto SymbolMap = State->get<IteratorSymbolMap>(); |
731 | 36 | auto RegionMap = State->get<IteratorRegionMap>(); |
732 | | // Use a counter to add newlines before every line except the first one. |
733 | 36 | unsigned Count = 0; |
734 | | |
735 | 36 | if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) { |
736 | 28 | Out << Sep << "Iterator Positions :" << NL; |
737 | 28 | for (const auto &Sym : SymbolMap) { |
738 | 0 | if (Count++) |
739 | 0 | Out << NL; |
740 | |
|
741 | 0 | Sym.first->dumpToStream(Out); |
742 | 0 | Out << " : "; |
743 | 0 | const auto Pos = Sym.second; |
744 | 0 | Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == "; |
745 | 0 | Pos.getContainer()->dumpToStream(Out); |
746 | 0 | Out<<" ; Offset == "; |
747 | 0 | Pos.getOffset()->dumpToStream(Out); |
748 | 0 | } |
749 | | |
750 | 28 | for (const auto &Reg : RegionMap) { |
751 | 28 | if (Count++) |
752 | 0 | Out << NL; |
753 | | |
754 | 28 | Reg.first->dumpToStream(Out); |
755 | 28 | Out << " : "; |
756 | 28 | const auto Pos = Reg.second; |
757 | 28 | Out << (Pos.isValid() ? "Valid" : "Invalid"0 ) << " ; Container == "; |
758 | 28 | Pos.getContainer()->dumpToStream(Out); |
759 | 28 | Out<<" ; Offset == "; |
760 | 28 | Pos.getOffset()->dumpToStream(Out); |
761 | 28 | } |
762 | 28 | } |
763 | 36 | } |
764 | | |
765 | | namespace { |
766 | | |
767 | 1.29k | bool isSimpleComparisonOperator(OverloadedOperatorKind OK) { |
768 | 1.29k | return OK == OO_EqualEqual || OK == OO_ExclaimEqual1.14k ; |
769 | 1.29k | } |
770 | | |
771 | 912 | bool isSimpleComparisonOperator(BinaryOperatorKind OK) { |
772 | 912 | return OK == BO_EQ || OK == BO_NE600 ; |
773 | 912 | } |
774 | | |
775 | 48 | ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) { |
776 | 48 | if (auto Reg = Val.getAsRegion()) { |
777 | 48 | Reg = Reg->getMostDerivedObjectRegion(); |
778 | 48 | return State->remove<IteratorRegionMap>(Reg); |
779 | 48 | } else if (const auto 0 Sym0 = Val.getAsSymbol()) { |
780 | 0 | return State->remove<IteratorSymbolMap>(Sym); |
781 | 0 | } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { |
782 | 0 | return State->remove<IteratorRegionMap>(LCVal->getRegion()); |
783 | 0 | } |
784 | 0 | return nullptr; |
785 | 48 | } |
786 | | |
787 | | ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, |
788 | 610 | SymbolRef Sym2, bool Equal) { |
789 | 610 | auto &SVB = State->getStateManager().getSValBuilder(); |
790 | | |
791 | | // FIXME: This code should be reworked as follows: |
792 | | // 1. Subtract the operands using evalBinOp(). |
793 | | // 2. Assume that the result doesn't overflow. |
794 | | // 3. Compare the result to 0. |
795 | | // 4. Assume the result of the comparison. |
796 | 610 | const auto comparison = |
797 | 610 | SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1), |
798 | 610 | nonloc::SymbolVal(Sym2), SVB.getConditionType()); |
799 | | |
800 | 610 | assert(isa<DefinedSVal>(comparison) && |
801 | 610 | "Symbol comparison must be a `DefinedSVal`"); |
802 | | |
803 | 610 | auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal); |
804 | 610 | if (!NewState) |
805 | 202 | return nullptr; |
806 | | |
807 | 408 | if (const auto CompSym = comparison.getAsSymbol()) { |
808 | 301 | assert(isa<SymIntExpr>(CompSym) && |
809 | 301 | "Symbol comparison must be a `SymIntExpr`"); |
810 | 301 | assert(BinaryOperator::isComparisonOp( |
811 | 301 | cast<SymIntExpr>(CompSym)->getOpcode()) && |
812 | 301 | "Symbol comparison must be a comparison"); |
813 | 301 | return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2); |
814 | 301 | } |
815 | | |
816 | 107 | return NewState; |
817 | 408 | } |
818 | | |
819 | | bool isBoundThroughLazyCompoundVal(const Environment &Env, |
820 | 6.38k | const MemRegion *Reg) { |
821 | 25.8k | for (const auto &Binding : Env) { |
822 | 25.8k | if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) { |
823 | 5.62k | if (LCVal->getRegion() == Reg) |
824 | 1.53k | return true; |
825 | 5.62k | } |
826 | 25.8k | } |
827 | | |
828 | 4.84k | return false; |
829 | 6.38k | } |
830 | | |
831 | 138 | const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) { |
832 | 9.67k | while (Node) { |
833 | 9.67k | ProgramPoint PP = Node->getLocation(); |
834 | 9.67k | if (auto Enter = PP.getAs<CallEnter>()) { |
835 | 396 | if (Enter->getCallExpr() == Call) |
836 | 138 | break; |
837 | 396 | } |
838 | | |
839 | 9.53k | Node = Node->getFirstPred(); |
840 | 9.53k | } |
841 | | |
842 | 138 | return Node; |
843 | 138 | } |
844 | | |
845 | | } // namespace |
846 | | |
847 | 19 | void ento::registerIteratorModeling(CheckerManager &mgr) { |
848 | 19 | mgr.registerChecker<IteratorModeling>(); |
849 | 19 | } |
850 | | |
851 | 40 | bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) { |
852 | 40 | return true; |
853 | 40 | } |