IteratorModeling.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  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. #include "clang/AST/DeclTemplate.h"
  67. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  68. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  69. #include "clang/StaticAnalyzer/Core/Checker.h"
  70. #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
  71. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  72. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  73. #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
  74. #include "Iterator.h"
  75. #include <utility>
  76. using namespace clang;
  77. using namespace ento;
  78. using namespace iterator;
  79. namespace {
  80. class IteratorModeling
  81. : public Checker<check::PostCall, check::PostStmt<UnaryOperator>,
  82. check::PostStmt<BinaryOperator>,
  83. check::PostStmt<MaterializeTemporaryExpr>,
  84. check::Bind, check::LiveSymbols, check::DeadSymbols> {
  85. using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *,
  86. SVal, SVal, SVal) const;
  87. void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call,
  88. OverloadedOperatorKind Op) const;
  89. void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call,
  90. const Expr *OrigExpr,
  91. const AdvanceFn *Handler) const;
  92. void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
  93. const SVal &LVal, const SVal &RVal,
  94. OverloadedOperatorKind Op) const;
  95. void processComparison(CheckerContext &C, ProgramStateRef State,
  96. SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
  97. OverloadedOperatorKind Op) const;
  98. void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
  99. bool Postfix) const;
  100. void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
  101. bool Postfix) const;
  102. void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
  103. OverloadedOperatorKind Op, const SVal &RetVal,
  104. const SVal &Iterator, const SVal &Amount) const;
  105. void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator,
  106. OverloadedOperatorKind OK, SVal Offset) const;
  107. void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
  108. SVal Amount) const;
  109. void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
  110. SVal Amount) const;
  111. void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
  112. SVal Amount) const;
  113. void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
  114. const MemRegion *Cont) const;
  115. bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const;
  116. void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
  117. const char *Sep) const override;
  118. // std::advance, std::prev & std::next
  119. CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = {
  120. // template<class InputIt, class Distance>
  121. // void advance(InputIt& it, Distance n);
  122. {{{"std", "advance"}, 2}, &IteratorModeling::handleAdvance},
  123. // template<class BidirIt>
  124. // BidirIt prev(
  125. // BidirIt it,
  126. // typename std::iterator_traits<BidirIt>::difference_type n = 1);
  127. {{{"std", "prev"}, 2}, &IteratorModeling::handlePrev},
  128. // template<class ForwardIt>
  129. // ForwardIt next(
  130. // ForwardIt it,
  131. // typename std::iterator_traits<ForwardIt>::difference_type n = 1);
  132. {{{"std", "next"}, 2}, &IteratorModeling::handleNext},
  133. };
  134. public:
  135. IteratorModeling() = default;
  136. void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
  137. void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
  138. void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const;
  139. void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const;
  140. void checkPostStmt(const MaterializeTemporaryExpr *MTE,
  141. CheckerContext &C) const;
  142. void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
  143. void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
  144. };
  145. bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
  146. bool isSimpleComparisonOperator(BinaryOperatorKind OK);
  147. ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
  148. ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
  149. SymbolRef Sym2, bool Equal);
  150. bool isBoundThroughLazyCompoundVal(const Environment &Env,
  151. const MemRegion *Reg);
  152. const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call);
  153. } // namespace
  154. void IteratorModeling::checkPostCall(const CallEvent &Call,
  155. CheckerContext &C) const {
  156. // Record new iterator positions and iterator position changes
  157. const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
  158. if (!Func)
  159. return;
  160. if (Func->isOverloadedOperator()) {
  161. const auto Op = Func->getOverloadedOperator();
  162. handleOverloadedOperator(C, Call, Op);
  163. return;
  164. }
  165. const auto *OrigExpr = Call.getOriginExpr();
  166. if (!OrigExpr)
  167. return;
  168. const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(Call);
  169. if (Handler) {
  170. handleAdvanceLikeFunction(C, Call, OrigExpr, Handler);
  171. return;
  172. }
  173. if (!isIteratorType(Call.getResultType()))
  174. return;
  175. auto State = C.getState();
  176. // Already bound to container?
  177. if (getIteratorPosition(State, Call.getReturnValue()))
  178. return;
  179. // Copy-like and move constructors
  180. if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
  181. if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
  182. State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
  183. if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
  184. State = removeIteratorPosition(State, Call.getArgSVal(0));
  185. }
  186. C.addTransition(State);
  187. return;
  188. }
  189. }
  190. // Assumption: if return value is an iterator which is not yet bound to a
  191. // container, then look for the first iterator argument of the
  192. // same type as the return value and bind the return value to
  193. // the same container. This approach works for STL algorithms.
  194. // FIXME: Add a more conservative mode
  195. for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
  196. if (isIteratorType(Call.getArgExpr(i)->getType()) &&
  197. Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType(
  198. C.getASTContext()).getTypePtr() ==
  199. Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) {
  200. if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
  201. assignToContainer(C, OrigExpr, Call.getReturnValue(),
  202. Pos->getContainer());
  203. return;
  204. }
  205. }
  206. }
  207. }
  208. void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
  209. CheckerContext &C) const {
  210. auto State = C.getState();
  211. const auto *Pos = getIteratorPosition(State, Val);
  212. if (Pos) {
  213. State = setIteratorPosition(State, Loc, *Pos);
  214. C.addTransition(State);
  215. } else {
  216. const auto *OldPos = getIteratorPosition(State, Loc);
  217. if (OldPos) {
  218. State = removeIteratorPosition(State, Loc);
  219. C.addTransition(State);
  220. }
  221. }
  222. }
  223. void IteratorModeling::checkPostStmt(const UnaryOperator *UO,
  224. CheckerContext &C) const {
  225. UnaryOperatorKind OK = UO->getOpcode();
  226. if (!isIncrementOperator(OK) && !isDecrementOperator(OK))
  227. return;
  228. auto &SVB = C.getSValBuilder();
  229. handlePtrIncrOrDecr(C, UO->getSubExpr(),
  230. isIncrementOperator(OK) ? OO_Plus : OO_Minus,
  231. SVB.makeArrayIndex(1));
  232. }
  233. void IteratorModeling::checkPostStmt(const BinaryOperator *BO,
  234. CheckerContext &C) const {
  235. const ProgramStateRef State = C.getState();
  236. const BinaryOperatorKind OK = BO->getOpcode();
  237. const Expr *const LHS = BO->getLHS();
  238. const Expr *const RHS = BO->getRHS();
  239. const SVal LVal = State->getSVal(LHS, C.getLocationContext());
  240. const SVal RVal = State->getSVal(RHS, C.getLocationContext());
  241. if (isSimpleComparisonOperator(BO->getOpcode())) {
  242. SVal Result = State->getSVal(BO, C.getLocationContext());
  243. handleComparison(C, BO, Result, LVal, RVal,
  244. BinaryOperator::getOverloadedOperator(OK));
  245. } else if (isRandomIncrOrDecrOperator(OK)) {
  246. // In case of operator+ the iterator can be either on the LHS (eg.: it + 1),
  247. // or on the RHS (eg.: 1 + it). Both cases are modeled.
  248. const bool IsIterOnLHS = BO->getLHS()->getType()->isPointerType();
  249. const Expr *const &IterExpr = IsIterOnLHS ? LHS : RHS;
  250. const Expr *const &AmountExpr = IsIterOnLHS ? RHS : LHS;
  251. // The non-iterator side must have an integral or enumeration type.
  252. if (!AmountExpr->getType()->isIntegralOrEnumerationType())
  253. return;
  254. const SVal &AmountVal = IsIterOnLHS ? RVal : LVal;
  255. handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK),
  256. AmountVal);
  257. }
  258. }
  259. void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
  260. CheckerContext &C) const {
  261. /* Transfer iterator state to temporary objects */
  262. auto State = C.getState();
  263. const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
  264. if (!Pos)
  265. return;
  266. State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
  267. C.addTransition(State);
  268. }
  269. void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
  270. SymbolReaper &SR) const {
  271. // Keep symbolic expressions of iterator positions alive
  272. auto RegionMap = State->get<IteratorRegionMap>();
  273. for (const auto &Reg : RegionMap) {
  274. const auto Offset = Reg.second.getOffset();
  275. for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
  276. if (isa<SymbolData>(*i))
  277. SR.markLive(*i);
  278. }
  279. auto SymbolMap = State->get<IteratorSymbolMap>();
  280. for (const auto &Sym : SymbolMap) {
  281. const auto Offset = Sym.second.getOffset();
  282. for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
  283. if (isa<SymbolData>(*i))
  284. SR.markLive(*i);
  285. }
  286. }
  287. void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
  288. CheckerContext &C) const {
  289. // Cleanup
  290. auto State = C.getState();
  291. auto RegionMap = State->get<IteratorRegionMap>();
  292. for (const auto &Reg : RegionMap) {
  293. if (!SR.isLiveRegion(Reg.first)) {
  294. // The region behind the `LazyCompoundVal` is often cleaned up before
  295. // the `LazyCompoundVal` itself. If there are iterator positions keyed
  296. // by these regions their cleanup must be deferred.
  297. if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
  298. State = State->remove<IteratorRegionMap>(Reg.first);
  299. }
  300. }
  301. }
  302. auto SymbolMap = State->get<IteratorSymbolMap>();
  303. for (const auto &Sym : SymbolMap) {
  304. if (!SR.isLive(Sym.first)) {
  305. State = State->remove<IteratorSymbolMap>(Sym.first);
  306. }
  307. }
  308. C.addTransition(State);
  309. }
  310. void
  311. IteratorModeling::handleOverloadedOperator(CheckerContext &C,
  312. const CallEvent &Call,
  313. OverloadedOperatorKind Op) const {
  314. if (isSimpleComparisonOperator(Op)) {
  315. const auto *OrigExpr = Call.getOriginExpr();
  316. if (!OrigExpr)
  317. return;
  318. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  319. handleComparison(C, OrigExpr, Call.getReturnValue(),
  320. InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
  321. return;
  322. }
  323. handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
  324. Call.getArgSVal(1), Op);
  325. return;
  326. } else if (isRandomIncrOrDecrOperator(Op)) {
  327. const auto *OrigExpr = Call.getOriginExpr();
  328. if (!OrigExpr)
  329. return;
  330. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  331. if (Call.getNumArgs() >= 1 &&
  332. Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
  333. handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
  334. InstCall->getCXXThisVal(), Call.getArgSVal(0));
  335. return;
  336. }
  337. } else if (Call.getNumArgs() >= 2) {
  338. const Expr *FirstArg = Call.getArgExpr(0);
  339. const Expr *SecondArg = Call.getArgExpr(1);
  340. const QualType FirstType = FirstArg->getType();
  341. const QualType SecondType = SecondArg->getType();
  342. if (FirstType->isIntegralOrEnumerationType() ||
  343. SecondType->isIntegralOrEnumerationType()) {
  344. // In case of operator+ the iterator can be either on the LHS (eg.:
  345. // it + 1), or on the RHS (eg.: 1 + it). Both cases are modeled.
  346. const bool IsIterFirst = FirstType->isStructureOrClassType();
  347. const SVal FirstArg = Call.getArgSVal(0);
  348. const SVal SecondArg = Call.getArgSVal(1);
  349. const SVal &Iterator = IsIterFirst ? FirstArg : SecondArg;
  350. const SVal &Amount = IsIterFirst ? SecondArg : FirstArg;
  351. handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
  352. Iterator, Amount);
  353. return;
  354. }
  355. }
  356. } else if (isIncrementOperator(Op)) {
  357. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  358. handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
  359. Call.getNumArgs());
  360. return;
  361. }
  362. handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
  363. Call.getNumArgs());
  364. return;
  365. } else if (isDecrementOperator(Op)) {
  366. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  367. handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
  368. Call.getNumArgs());
  369. return;
  370. }
  371. handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
  372. Call.getNumArgs());
  373. return;
  374. }
  375. }
  376. void
  377. IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C,
  378. const CallEvent &Call,
  379. const Expr *OrigExpr,
  380. const AdvanceFn *Handler) const {
  381. if (!C.wasInlined) {
  382. (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
  383. Call.getArgSVal(0), Call.getArgSVal(1));
  384. return;
  385. }
  386. // If std::advance() was inlined, but a non-standard function it calls inside
  387. // was not, then we have to model it explicitly
  388. const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier();
  389. if (IdInfo) {
  390. if (IdInfo->getName() == "advance") {
  391. if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) {
  392. (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
  393. Call.getArgSVal(0), Call.getArgSVal(1));
  394. }
  395. }
  396. }
  397. }
  398. void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
  399. SVal RetVal, const SVal &LVal,
  400. const SVal &RVal,
  401. OverloadedOperatorKind Op) const {
  402. // Record the operands and the operator of the comparison for the next
  403. // evalAssume, if the result is a symbolic expression. If it is a concrete
  404. // value (only one branch is possible), then transfer the state between
  405. // the operands according to the operator and the result
  406. auto State = C.getState();
  407. const auto *LPos = getIteratorPosition(State, LVal);
  408. const auto *RPos = getIteratorPosition(State, RVal);
  409. const MemRegion *Cont = nullptr;
  410. if (LPos) {
  411. Cont = LPos->getContainer();
  412. } else if (RPos) {
  413. Cont = RPos->getContainer();
  414. }
  415. if (!Cont)
  416. return;
  417. // At least one of the iterators has recorded positions. If one of them does
  418. // not then create a new symbol for the offset.
  419. SymbolRef Sym;
  420. if (!LPos || !RPos) {
  421. auto &SymMgr = C.getSymbolManager();
  422. Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
  423. C.getASTContext().LongTy, C.blockCount());
  424. State = assumeNoOverflow(State, Sym, 4);
  425. }
  426. if (!LPos) {
  427. State = setIteratorPosition(State, LVal,
  428. IteratorPosition::getPosition(Cont, Sym));
  429. LPos = getIteratorPosition(State, LVal);
  430. } else if (!RPos) {
  431. State = setIteratorPosition(State, RVal,
  432. IteratorPosition::getPosition(Cont, Sym));
  433. RPos = getIteratorPosition(State, RVal);
  434. }
  435. // If the value for which we just tried to set a new iterator position is
  436. // an `SVal`for which no iterator position can be set then the setting was
  437. // unsuccessful. We cannot handle the comparison in this case.
  438. if (!LPos || !RPos)
  439. return;
  440. // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol
  441. // instead.
  442. if (RetVal.isUnknown()) {
  443. auto &SymMgr = C.getSymbolManager();
  444. auto *LCtx = C.getLocationContext();
  445. RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol(
  446. CE, LCtx, C.getASTContext().BoolTy, C.blockCount()));
  447. State = State->BindExpr(CE, LCtx, RetVal);
  448. }
  449. processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
  450. }
  451. void IteratorModeling::processComparison(CheckerContext &C,
  452. ProgramStateRef State, SymbolRef Sym1,
  453. SymbolRef Sym2, const SVal &RetVal,
  454. OverloadedOperatorKind Op) const {
  455. if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
  456. if ((State = relateSymbols(State, Sym1, Sym2,
  457. (Op == OO_EqualEqual) ==
  458. (TruthVal->getValue() != 0)))) {
  459. C.addTransition(State);
  460. } else {
  461. C.generateSink(State, C.getPredecessor());
  462. }
  463. return;
  464. }
  465. const auto ConditionVal = RetVal.getAs<DefinedSVal>();
  466. if (!ConditionVal)
  467. return;
  468. if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
  469. StateTrue = StateTrue->assume(*ConditionVal, true);
  470. C.addTransition(StateTrue);
  471. }
  472. if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
  473. StateFalse = StateFalse->assume(*ConditionVal, false);
  474. C.addTransition(StateFalse);
  475. }
  476. }
  477. void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
  478. const SVal &Iter, bool Postfix) const {
  479. // Increment the symbolic expressions which represents the position of the
  480. // iterator
  481. auto State = C.getState();
  482. auto &BVF = C.getSymbolManager().getBasicVals();
  483. const auto *Pos = getIteratorPosition(State, Iter);
  484. if (!Pos)
  485. return;
  486. auto NewState =
  487. advancePosition(State, Iter, OO_Plus,
  488. nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
  489. assert(NewState &&
  490. "Advancing position by concrete int should always be successful");
  491. const auto *NewPos = getIteratorPosition(NewState, Iter);
  492. assert(NewPos &&
  493. "Iterator should have position after successful advancement");
  494. State = setIteratorPosition(State, Iter, *NewPos);
  495. State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
  496. C.addTransition(State);
  497. }
  498. void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
  499. const SVal &Iter, bool Postfix) const {
  500. // Decrement the symbolic expressions which represents the position of the
  501. // iterator
  502. auto State = C.getState();
  503. auto &BVF = C.getSymbolManager().getBasicVals();
  504. const auto *Pos = getIteratorPosition(State, Iter);
  505. if (!Pos)
  506. return;
  507. auto NewState =
  508. advancePosition(State, Iter, OO_Minus,
  509. nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
  510. assert(NewState &&
  511. "Advancing position by concrete int should always be successful");
  512. const auto *NewPos = getIteratorPosition(NewState, Iter);
  513. assert(NewPos &&
  514. "Iterator should have position after successful advancement");
  515. State = setIteratorPosition(State, Iter, *NewPos);
  516. State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
  517. C.addTransition(State);
  518. }
  519. void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
  520. OverloadedOperatorKind Op,
  521. const SVal &RetVal,
  522. const SVal &Iterator,
  523. const SVal &Amount) const {
  524. // Increment or decrement the symbolic expressions which represents the
  525. // position of the iterator
  526. auto State = C.getState();
  527. const auto *Pos = getIteratorPosition(State, Iterator);
  528. if (!Pos)
  529. return;
  530. const auto *Value = &Amount;
  531. SVal Val;
  532. if (auto LocAmount = Amount.getAs<Loc>()) {
  533. Val = State->getRawSVal(*LocAmount);
  534. Value = &Val;
  535. }
  536. const auto &TgtVal =
  537. (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal;
  538. // `AdvancedState` is a state where the position of `LHS` is advanced. We
  539. // only need this state to retrieve the new position, but we do not want
  540. // to change the position of `LHS` (in every case).
  541. auto AdvancedState = advancePosition(State, Iterator, Op, *Value);
  542. if (AdvancedState) {
  543. const auto *NewPos = getIteratorPosition(AdvancedState, Iterator);
  544. assert(NewPos &&
  545. "Iterator should have position after successful advancement");
  546. State = setIteratorPosition(State, TgtVal, *NewPos);
  547. C.addTransition(State);
  548. } else {
  549. assignToContainer(C, CE, TgtVal, Pos->getContainer());
  550. }
  551. }
  552. void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
  553. const Expr *Iterator,
  554. OverloadedOperatorKind OK,
  555. SVal Offset) const {
  556. if (!isa<DefinedSVal>(Offset))
  557. return;
  558. QualType PtrType = Iterator->getType();
  559. if (!PtrType->isPointerType())
  560. return;
  561. QualType ElementType = PtrType->getPointeeType();
  562. ProgramStateRef State = C.getState();
  563. SVal OldVal = State->getSVal(Iterator, C.getLocationContext());
  564. const IteratorPosition *OldPos = getIteratorPosition(State, OldVal);
  565. if (!OldPos)
  566. return;
  567. SVal NewVal;
  568. if (OK == OO_Plus || OK == OO_PlusEqual) {
  569. NewVal = State->getLValue(ElementType, Offset, OldVal);
  570. } else {
  571. auto &SVB = C.getSValBuilder();
  572. SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>());
  573. NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
  574. }
  575. // `AdvancedState` is a state where the position of `Old` is advanced. We
  576. // only need this state to retrieve the new position, but we do not want
  577. // ever to change the position of `OldVal`.
  578. auto AdvancedState = advancePosition(State, OldVal, OK, Offset);
  579. if (AdvancedState) {
  580. const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal);
  581. assert(NewPos &&
  582. "Iterator should have position after successful advancement");
  583. ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos);
  584. C.addTransition(NewState);
  585. } else {
  586. assignToContainer(C, Iterator, NewVal, OldPos->getContainer());
  587. }
  588. }
  589. void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE,
  590. SVal RetVal, SVal Iter,
  591. SVal Amount) const {
  592. handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount);
  593. }
  594. void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE,
  595. SVal RetVal, SVal Iter, SVal Amount) const {
  596. handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount);
  597. }
  598. void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE,
  599. SVal RetVal, SVal Iter, SVal Amount) const {
  600. handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount);
  601. }
  602. void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
  603. const SVal &RetVal,
  604. const MemRegion *Cont) const {
  605. Cont = Cont->getMostDerivedObjectRegion();
  606. auto State = C.getState();
  607. const auto *LCtx = C.getLocationContext();
  608. State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount());
  609. C.addTransition(State);
  610. }
  611. bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter,
  612. const Expr *CE) const {
  613. // Compare the iterator position before and after the call. (To be called
  614. // from `checkPostCall()`.)
  615. const auto StateAfter = C.getState();
  616. const auto *PosAfter = getIteratorPosition(StateAfter, Iter);
  617. // If we have no position after the call of `std::advance`, then we are not
  618. // interested. (Modeling of an inlined `std::advance()` should not remove the
  619. // position in any case.)
  620. if (!PosAfter)
  621. return false;
  622. const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE);
  623. assert(N && "Any call should have a `CallEnter` node.");
  624. const auto StateBefore = N->getState();
  625. const auto *PosBefore = getIteratorPosition(StateBefore, Iter);
  626. // FIXME: `std::advance()` should not create a new iterator position but
  627. // change existing ones. However, in case of iterators implemented as
  628. // pointers the handling of parameters in `std::advance()`-like
  629. // functions is still incomplete which may result in cases where
  630. // the new position is assigned to the wrong pointer. This causes
  631. // crash if we use an assertion here.
  632. if (!PosBefore)
  633. return false;
  634. return PosBefore->getOffset() == PosAfter->getOffset();
  635. }
  636. void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
  637. const char *NL, const char *Sep) const {
  638. auto SymbolMap = State->get<IteratorSymbolMap>();
  639. auto RegionMap = State->get<IteratorRegionMap>();
  640. // Use a counter to add newlines before every line except the first one.
  641. unsigned Count = 0;
  642. if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
  643. Out << Sep << "Iterator Positions :" << NL;
  644. for (const auto &Sym : SymbolMap) {
  645. if (Count++)
  646. Out << NL;
  647. Sym.first->dumpToStream(Out);
  648. Out << " : ";
  649. const auto Pos = Sym.second;
  650. Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
  651. Pos.getContainer()->dumpToStream(Out);
  652. Out<<" ; Offset == ";
  653. Pos.getOffset()->dumpToStream(Out);
  654. }
  655. for (const auto &Reg : RegionMap) {
  656. if (Count++)
  657. Out << NL;
  658. Reg.first->dumpToStream(Out);
  659. Out << " : ";
  660. const auto Pos = Reg.second;
  661. Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
  662. Pos.getContainer()->dumpToStream(Out);
  663. Out<<" ; Offset == ";
  664. Pos.getOffset()->dumpToStream(Out);
  665. }
  666. }
  667. }
  668. namespace {
  669. bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
  670. return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
  671. }
  672. bool isSimpleComparisonOperator(BinaryOperatorKind OK) {
  673. return OK == BO_EQ || OK == BO_NE;
  674. }
  675. ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
  676. if (auto Reg = Val.getAsRegion()) {
  677. Reg = Reg->getMostDerivedObjectRegion();
  678. return State->remove<IteratorRegionMap>(Reg);
  679. } else if (const auto Sym = Val.getAsSymbol()) {
  680. return State->remove<IteratorSymbolMap>(Sym);
  681. } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
  682. return State->remove<IteratorRegionMap>(LCVal->getRegion());
  683. }
  684. return nullptr;
  685. }
  686. ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
  687. SymbolRef Sym2, bool Equal) {
  688. auto &SVB = State->getStateManager().getSValBuilder();
  689. // FIXME: This code should be reworked as follows:
  690. // 1. Subtract the operands using evalBinOp().
  691. // 2. Assume that the result doesn't overflow.
  692. // 3. Compare the result to 0.
  693. // 4. Assume the result of the comparison.
  694. const auto comparison =
  695. SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
  696. nonloc::SymbolVal(Sym2), SVB.getConditionType());
  697. assert(isa<DefinedSVal>(comparison) &&
  698. "Symbol comparison must be a `DefinedSVal`");
  699. auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
  700. if (!NewState)
  701. return nullptr;
  702. if (const auto CompSym = comparison.getAsSymbol()) {
  703. assert(isa<SymIntExpr>(CompSym) &&
  704. "Symbol comparison must be a `SymIntExpr`");
  705. assert(BinaryOperator::isComparisonOp(
  706. cast<SymIntExpr>(CompSym)->getOpcode()) &&
  707. "Symbol comparison must be a comparison");
  708. return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
  709. }
  710. return NewState;
  711. }
  712. bool isBoundThroughLazyCompoundVal(const Environment &Env,
  713. const MemRegion *Reg) {
  714. for (const auto &Binding : Env) {
  715. if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
  716. if (LCVal->getRegion() == Reg)
  717. return true;
  718. }
  719. }
  720. return false;
  721. }
  722. const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) {
  723. while (Node) {
  724. ProgramPoint PP = Node->getLocation();
  725. if (auto Enter = PP.getAs<CallEnter>()) {
  726. if (Enter->getCallExpr() == Call)
  727. break;
  728. }
  729. Node = Node->getFirstPred();
  730. }
  731. return Node;
  732. }
  733. } // namespace
  734. void ento::registerIteratorModeling(CheckerManager &mgr) {
  735. mgr.registerChecker<IteratorModeling>();
  736. }
  737. bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) {
  738. return true;
  739. }