IteratorChecker.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  1. //===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. //
  10. // Defines a checker for using iterators outside their range (past end). Usage
  11. // means here dereferencing, incrementing etc.
  12. //
  13. //===----------------------------------------------------------------------===//
  14. //
  15. // In the code, iterator can be represented as a:
  16. // * type-I: typedef-ed pointer. Operations over such iterator, such as
  17. // comparisons or increments, are modeled straightforwardly by the
  18. // analyzer.
  19. // * type-II: structure with its method bodies available. Operations over such
  20. // iterator are inlined by the analyzer, and results of modeling
  21. // these operations are exposing implementation details of the
  22. // iterators, which is not necessarily helping.
  23. // * type-III: completely opaque structure. Operations over such iterator are
  24. // modeled conservatively, producing conjured symbols everywhere.
  25. //
  26. // To handle all these types in a common way we introduce a structure called
  27. // IteratorPosition which is an abstraction of the position the iterator
  28. // represents using symbolic expressions. The checker handles all the
  29. // operations on this structure.
  30. //
  31. // Additionally, depending on the circumstances, operators of types II and III
  32. // can be represented as:
  33. // * type-IIa, type-IIIa: conjured structure symbols - when returned by value
  34. // from conservatively evaluated methods such as
  35. // `.begin()`.
  36. // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
  37. // variables or temporaries, when the iterator object is
  38. // currently treated as an lvalue.
  39. // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
  40. // iterator object is treated as an rvalue taken of a
  41. // particular lvalue, eg. a copy of "type-a" iterator
  42. // object, or an iterator that existed before the
  43. // analysis has started.
  44. //
  45. // To handle any of these three different representations stored in an SVal we
  46. // use setter and getters functions which separate the three cases. To store
  47. // them we use a pointer union of symbol and memory region.
  48. //
  49. // The checker works the following way: We record the past-end iterator for
  50. // all containers whenever their `.end()` is called. Since the Constraint
  51. // Manager cannot handle SVals we need to take over its role. We post-check
  52. // equality and non-equality comparisons and propagate the position of the
  53. // iterator to the other side of the comparison if it is past-end and we are in
  54. // the 'equal' branch (true-branch for `==` and false-branch for `!=`).
  55. //
  56. // In case of type-I or type-II iterators we get a concrete integer as a result
  57. // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
  58. // this latter case we record the symbol and reload it in evalAssume() and do
  59. // the propagation there. We also handle (maybe double) negated comparisons
  60. // which are represented in the form of (x == 0 or x !=0 ) where x is the
  61. // comparison itself.
  62. #include "ClangSACheckers.h"
  63. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  64. #include "clang/StaticAnalyzer/Core/Checker.h"
  65. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  66. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  67. using namespace clang;
  68. using namespace ento;
  69. namespace {
  70. // Abstract position of an iterator. This helps to handle all three kinds
  71. // of operators in a common way by using a symbolic position.
  72. struct IteratorPosition {
  73. private:
  74. // Container the iterator belongs to
  75. const MemRegion *Cont;
  76. // Abstract offset
  77. SymbolRef Offset;
  78. IteratorPosition(const MemRegion *C, SymbolRef Of)
  79. : Cont(C), Offset(Of) {}
  80. public:
  81. const MemRegion *getContainer() const { return Cont; }
  82. SymbolRef getOffset() const { return Offset; }
  83. static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
  84. return IteratorPosition(C, Of);
  85. }
  86. IteratorPosition setTo(SymbolRef NewOf) const {
  87. return IteratorPosition(Cont, NewOf);
  88. }
  89. bool operator==(const IteratorPosition &X) const {
  90. return Cont == X.Cont && Offset == X.Offset;
  91. }
  92. bool operator!=(const IteratorPosition &X) const {
  93. return Cont != X.Cont || Offset != X.Offset;
  94. }
  95. void Profile(llvm::FoldingSetNodeID &ID) const {
  96. ID.AddPointer(Cont);
  97. ID.Add(Offset);
  98. }
  99. };
  100. typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
  101. // Structure to record the symbolic end position of a container
  102. struct ContainerData {
  103. private:
  104. SymbolRef End;
  105. ContainerData(SymbolRef E) : End(E) {}
  106. public:
  107. static ContainerData fromEnd(SymbolRef E) {
  108. return ContainerData(E);
  109. }
  110. SymbolRef getEnd() const { return End; }
  111. ContainerData newEnd(SymbolRef E) const { return ContainerData(E); }
  112. bool operator==(const ContainerData &X) const {
  113. return End == X.End;
  114. }
  115. bool operator!=(const ContainerData &X) const {
  116. return End != X.End;
  117. }
  118. void Profile(llvm::FoldingSetNodeID &ID) const {
  119. ID.Add(End);
  120. }
  121. };
  122. // Structure fo recording iterator comparisons. We needed to retrieve the
  123. // original comparison expression in assumptions.
  124. struct IteratorComparison {
  125. private:
  126. RegionOrSymbol Left, Right;
  127. bool Equality;
  128. public:
  129. IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
  130. : Left(L), Right(R), Equality(Eq) {}
  131. RegionOrSymbol getLeft() const { return Left; }
  132. RegionOrSymbol getRight() const { return Right; }
  133. bool isEquality() const { return Equality; }
  134. bool operator==(const IteratorComparison &X) const {
  135. return Left == X.Left && Right == X.Right && Equality == X.Equality;
  136. }
  137. bool operator!=(const IteratorComparison &X) const {
  138. return Left != X.Left || Right != X.Right || Equality != X.Equality;
  139. }
  140. void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
  141. };
  142. class IteratorChecker
  143. : public Checker<check::PreCall, check::PostCall,
  144. check::PostStmt<MaterializeTemporaryExpr>,
  145. check::DeadSymbols,
  146. eval::Assume> {
  147. std::unique_ptr<BugType> OutOfRangeBugType;
  148. void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
  149. const SVal &RVal, OverloadedOperatorKind Op) const;
  150. void verifyDereference(CheckerContext &C, const SVal &Val) const;
  151. void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
  152. const SVal &Cont) const;
  153. void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
  154. const MemRegion *Cont) const;
  155. void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
  156. CheckerContext &C, ExplodedNode *ErrNode) const;
  157. public:
  158. IteratorChecker();
  159. enum CheckKind {
  160. CK_IteratorRangeChecker,
  161. CK_NumCheckKinds
  162. };
  163. DefaultBool ChecksEnabled[CK_NumCheckKinds];
  164. CheckName CheckNames[CK_NumCheckKinds];
  165. void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
  166. void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
  167. void checkPostStmt(const MaterializeTemporaryExpr *MTE,
  168. CheckerContext &C) const;
  169. void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
  170. ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
  171. bool Assumption) const;
  172. };
  173. } // namespace
  174. REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
  175. REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
  176. IteratorPosition)
  177. REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
  178. REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
  179. IteratorComparison)
  180. namespace {
  181. bool isIteratorType(const QualType &Type);
  182. bool isIterator(const CXXRecordDecl *CRD);
  183. bool isEndCall(const FunctionDecl *Func);
  184. bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
  185. bool isDereferenceOperator(OverloadedOperatorKind OK);
  186. BinaryOperator::Opcode getOpcode(const SymExpr *SE);
  187. const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
  188. const ProgramStateRef processComparison(ProgramStateRef State,
  189. RegionOrSymbol LVal,
  190. RegionOrSymbol RVal, bool Equal);
  191. const ProgramStateRef saveComparison(ProgramStateRef State,
  192. const SymExpr *Condition, const SVal &LVal,
  193. const SVal &RVal, bool Eq);
  194. const IteratorComparison *loadComparison(ProgramStateRef State,
  195. const SymExpr *Condition);
  196. SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
  197. ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
  198. const SymbolRef Sym);
  199. const IteratorPosition *getIteratorPosition(ProgramStateRef State,
  200. const SVal &Val);
  201. const IteratorPosition *getIteratorPosition(ProgramStateRef State,
  202. RegionOrSymbol RegOrSym);
  203. ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
  204. const IteratorPosition &Pos);
  205. ProgramStateRef setIteratorPosition(ProgramStateRef State,
  206. RegionOrSymbol RegOrSym,
  207. const IteratorPosition &Pos);
  208. ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
  209. ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
  210. RegionOrSymbol RegOrSym,
  211. const IteratorPosition &Pos, bool Equal);
  212. ProgramStateRef relateIteratorPositions(ProgramStateRef State,
  213. const IteratorPosition &Pos1,
  214. const IteratorPosition &Pos2,
  215. bool Equal);
  216. const ContainerData *getContainerData(ProgramStateRef State,
  217. const MemRegion *Cont);
  218. ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
  219. const ContainerData &CData);
  220. bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
  221. } // namespace
  222. IteratorChecker::IteratorChecker() {
  223. OutOfRangeBugType.reset(
  224. new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
  225. OutOfRangeBugType->setSuppressOnSink(true);
  226. }
  227. void IteratorChecker::checkPreCall(const CallEvent &Call,
  228. CheckerContext &C) const {
  229. // Check for out of range access
  230. const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
  231. if (!Func)
  232. return;
  233. if (Func->isOverloadedOperator()) {
  234. if (ChecksEnabled[CK_IteratorRangeChecker] &&
  235. isDereferenceOperator(Func->getOverloadedOperator())) {
  236. // Check for dereference of out-of-range iterators
  237. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  238. verifyDereference(C, InstCall->getCXXThisVal());
  239. } else {
  240. verifyDereference(C, Call.getArgSVal(0));
  241. }
  242. }
  243. }
  244. }
  245. void IteratorChecker::checkPostCall(const CallEvent &Call,
  246. CheckerContext &C) const {
  247. // Record new iterator positions and iterator position changes
  248. const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
  249. if (!Func)
  250. return;
  251. if (Func->isOverloadedOperator()) {
  252. const auto Op = Func->getOverloadedOperator();
  253. if (isSimpleComparisonOperator(Op)) {
  254. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  255. handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
  256. Call.getArgSVal(0), Op);
  257. } else {
  258. handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
  259. Call.getArgSVal(1), Op);
  260. }
  261. }
  262. } else {
  263. const auto *OrigExpr = Call.getOriginExpr();
  264. if (!OrigExpr)
  265. return;
  266. if (!isIteratorType(Call.getResultType()))
  267. return;
  268. auto State = C.getState();
  269. // Already bound to container?
  270. if (getIteratorPosition(State, Call.getReturnValue()))
  271. return;
  272. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  273. if (isEndCall(Func)) {
  274. handleEnd(C, OrigExpr, Call.getReturnValue(),
  275. InstCall->getCXXThisVal());
  276. return;
  277. }
  278. }
  279. // Copy-like and move constructors
  280. if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
  281. if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
  282. State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
  283. if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
  284. State = removeIteratorPosition(State, Call.getArgSVal(0));
  285. }
  286. C.addTransition(State);
  287. return;
  288. }
  289. }
  290. // Assumption: if return value is an iterator which is not yet bound to a
  291. // container, then look for the first iterator argument, and
  292. // bind the return value to the same container. This approach
  293. // works for STL algorithms.
  294. // FIXME: Add a more conservative mode
  295. for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
  296. if (isIteratorType(Call.getArgExpr(i)->getType())) {
  297. if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
  298. assignToContainer(C, OrigExpr, Call.getReturnValue(),
  299. Pos->getContainer());
  300. return;
  301. }
  302. }
  303. }
  304. }
  305. }
  306. void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
  307. CheckerContext &C) const {
  308. /* Transfer iterator state to temporary objects */
  309. auto State = C.getState();
  310. const auto *Pos =
  311. getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
  312. if (!Pos)
  313. return;
  314. State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
  315. C.addTransition(State);
  316. }
  317. void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
  318. CheckerContext &C) const {
  319. // Cleanup
  320. auto State = C.getState();
  321. auto RegionMap = State->get<IteratorRegionMap>();
  322. for (const auto Reg : RegionMap) {
  323. if (!SR.isLiveRegion(Reg.first)) {
  324. State = State->remove<IteratorRegionMap>(Reg.first);
  325. }
  326. }
  327. auto SymbolMap = State->get<IteratorSymbolMap>();
  328. for (const auto Sym : SymbolMap) {
  329. if (!SR.isLive(Sym.first)) {
  330. State = State->remove<IteratorSymbolMap>(Sym.first);
  331. }
  332. }
  333. auto ContMap = State->get<ContainerMap>();
  334. for (const auto Cont : ContMap) {
  335. if (!SR.isLiveRegion(Cont.first)) {
  336. State = State->remove<ContainerMap>(Cont.first);
  337. }
  338. }
  339. auto ComparisonMap = State->get<IteratorComparisonMap>();
  340. for (const auto Comp : ComparisonMap) {
  341. if (!SR.isLive(Comp.first)) {
  342. State = State->remove<IteratorComparisonMap>(Comp.first);
  343. }
  344. }
  345. }
  346. ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
  347. bool Assumption) const {
  348. // Load recorded comparison and transfer iterator state between sides
  349. // according to comparison operator and assumption
  350. const auto *SE = Cond.getAsSymExpr();
  351. if (!SE)
  352. return State;
  353. auto Opc = getOpcode(SE);
  354. if (Opc != BO_EQ && Opc != BO_NE)
  355. return State;
  356. bool Negated = false;
  357. const auto *Comp = loadComparison(State, SE);
  358. if (!Comp) {
  359. // Try negated comparison, which is a SymExpr to 0 integer comparison
  360. const auto *SIE = dyn_cast<SymIntExpr>(SE);
  361. if (!SIE)
  362. return State;
  363. if (SIE->getRHS() != 0)
  364. return State;
  365. SE = SIE->getLHS();
  366. Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
  367. Opc = getOpcode(SE);
  368. if (Opc != BO_EQ && Opc != BO_NE)
  369. return State;
  370. Comp = loadComparison(State, SE);
  371. if (!Comp)
  372. return State;
  373. }
  374. return processComparison(State, Comp->getLeft(), Comp->getRight(),
  375. (Comp->isEquality() == Assumption) != Negated);
  376. }
  377. void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal,
  378. const SVal &LVal, const SVal &RVal,
  379. OverloadedOperatorKind Op) const {
  380. // Record the operands and the operator of the comparison for the next
  381. // evalAssume, if the result is a symbolic expression. If it is a concrete
  382. // value (only one branch is possible), then transfer the state between
  383. // the operands according to the operator and the result
  384. auto State = C.getState();
  385. if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
  386. const auto *LPos = getIteratorPosition(State, LVal);
  387. const auto *RPos = getIteratorPosition(State, RVal);
  388. if (!LPos && !RPos)
  389. return;
  390. State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
  391. C.addTransition(State);
  392. } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
  393. if ((State = processComparison(
  394. State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
  395. (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
  396. C.addTransition(State);
  397. } else {
  398. C.generateSink(State, C.getPredecessor());
  399. }
  400. }
  401. }
  402. void IteratorChecker::verifyDereference(CheckerContext &C,
  403. const SVal &Val) const {
  404. auto State = C.getState();
  405. const auto *Pos = getIteratorPosition(State, Val);
  406. if (Pos && isOutOfRange(State, *Pos)) {
  407. // If I do not put a tag here, some range tests will fail
  408. static CheckerProgramPointTag Tag("IteratorRangeChecker",
  409. "IteratorOutOfRange");
  410. auto *N = C.generateNonFatalErrorNode(State, &Tag);
  411. if (!N) {
  412. return;
  413. }
  414. reportOutOfRangeBug("Iterator accessed outside of its range.", Val, C, N);
  415. }
  416. }
  417. void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
  418. const SVal &RetVal, const SVal &Cont) const {
  419. const auto *ContReg = Cont.getAsRegion();
  420. if (!ContReg)
  421. return;
  422. while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
  423. ContReg = CBOR->getSuperRegion();
  424. }
  425. // If the container already has an end symbol then use it. Otherwise first
  426. // create a new one.
  427. auto State = C.getState();
  428. auto EndSym = getContainerEnd(State, ContReg);
  429. if (!EndSym) {
  430. auto &SymMgr = C.getSymbolManager();
  431. EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
  432. C.getASTContext().LongTy, C.blockCount());
  433. State = createContainerEnd(State, ContReg, EndSym);
  434. }
  435. State = setIteratorPosition(State, RetVal,
  436. IteratorPosition::getPosition(ContReg, EndSym));
  437. C.addTransition(State);
  438. }
  439. void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
  440. const SVal &RetVal,
  441. const MemRegion *Cont) const {
  442. while (const auto *CBOR = Cont->getAs<CXXBaseObjectRegion>()) {
  443. Cont = CBOR->getSuperRegion();
  444. }
  445. auto State = C.getState();
  446. auto &SymMgr = C.getSymbolManager();
  447. auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
  448. C.getASTContext().LongTy, C.blockCount());
  449. State = setIteratorPosition(State, RetVal,
  450. IteratorPosition::getPosition(Cont, Sym));
  451. C.addTransition(State);
  452. }
  453. void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
  454. const SVal &Val, CheckerContext &C,
  455. ExplodedNode *ErrNode) const {
  456. auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
  457. R->markInteresting(Val);
  458. C.emitReport(std::move(R));
  459. }
  460. namespace {
  461. bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
  462. bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
  463. BinaryOperator::Opcode Opc);
  464. bool isIteratorType(const QualType &Type) {
  465. if (Type->isPointerType())
  466. return true;
  467. const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
  468. return isIterator(CRD);
  469. }
  470. bool isIterator(const CXXRecordDecl *CRD) {
  471. if (!CRD)
  472. return false;
  473. const auto Name = CRD->getName();
  474. if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
  475. Name.endswith_lower("it")))
  476. return false;
  477. bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
  478. HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
  479. for (const auto *Method : CRD->methods()) {
  480. if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
  481. if (Ctor->isCopyConstructor()) {
  482. HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
  483. }
  484. continue;
  485. }
  486. if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
  487. HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
  488. continue;
  489. }
  490. if (Method->isCopyAssignmentOperator()) {
  491. HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
  492. continue;
  493. }
  494. if (!Method->isOverloadedOperator())
  495. continue;
  496. const auto OPK = Method->getOverloadedOperator();
  497. if (OPK == OO_PlusPlus) {
  498. HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
  499. HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
  500. continue;
  501. }
  502. if (OPK == OO_Star) {
  503. HasDerefOp = (Method->getNumParams() == 0);
  504. continue;
  505. }
  506. }
  507. return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
  508. HasPostIncrOp && HasDerefOp;
  509. }
  510. bool isEndCall(const FunctionDecl *Func) {
  511. const auto *IdInfo = Func->getIdentifier();
  512. if (!IdInfo)
  513. return false;
  514. return IdInfo->getName().endswith_lower("end");
  515. }
  516. bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
  517. return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
  518. }
  519. bool isDereferenceOperator(OverloadedOperatorKind OK) {
  520. return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
  521. OK == OO_Subscript;
  522. }
  523. BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
  524. if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
  525. return BSE->getOpcode();
  526. } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
  527. const auto *COE = dyn_cast<CXXOperatorCallExpr>(SC->getStmt());
  528. if (!COE)
  529. return BO_Comma; // Extremal value, neither EQ nor NE
  530. if (COE->getOperator() == OO_EqualEqual) {
  531. return BO_EQ;
  532. } else if (COE->getOperator() == OO_ExclaimEqual) {
  533. return BO_NE;
  534. }
  535. return BO_Comma; // Extremal value, neither EQ nor NE
  536. }
  537. return BO_Comma; // Extremal value, neither EQ nor NE
  538. }
  539. const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
  540. if (const auto Reg = Val.getAsRegion()) {
  541. return Reg;
  542. } else if (const auto Sym = Val.getAsSymbol()) {
  543. return Sym;
  544. } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
  545. return LCVal->getRegion();
  546. }
  547. return RegionOrSymbol();
  548. }
  549. const ProgramStateRef processComparison(ProgramStateRef State,
  550. RegionOrSymbol LVal,
  551. RegionOrSymbol RVal, bool Equal) {
  552. const auto *LPos = getIteratorPosition(State, LVal);
  553. const auto *RPos = getIteratorPosition(State, RVal);
  554. if (LPos && !RPos) {
  555. State = adjustIteratorPosition(State, RVal, *LPos, Equal);
  556. } else if (!LPos && RPos) {
  557. State = adjustIteratorPosition(State, LVal, *RPos, Equal);
  558. } else if (LPos && RPos) {
  559. State = relateIteratorPositions(State, *LPos, *RPos, Equal);
  560. }
  561. return State;
  562. }
  563. const ProgramStateRef saveComparison(ProgramStateRef State,
  564. const SymExpr *Condition, const SVal &LVal,
  565. const SVal &RVal, bool Eq) {
  566. const auto Left = getRegionOrSymbol(LVal);
  567. const auto Right = getRegionOrSymbol(RVal);
  568. if (!Left || !Right)
  569. return State;
  570. return State->set<IteratorComparisonMap>(Condition,
  571. IteratorComparison(Left, Right, Eq));
  572. }
  573. const IteratorComparison *loadComparison(ProgramStateRef State,
  574. const SymExpr *Condition) {
  575. return State->get<IteratorComparisonMap>(Condition);
  576. }
  577. SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
  578. const auto *CDataPtr = getContainerData(State, Cont);
  579. if (!CDataPtr)
  580. return nullptr;
  581. return CDataPtr->getEnd();
  582. }
  583. ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
  584. const SymbolRef Sym) {
  585. // Only create if it does not exist
  586. const auto *CDataPtr = getContainerData(State, Cont);
  587. if (CDataPtr) {
  588. if (CDataPtr->getEnd()) {
  589. return State;
  590. } else {
  591. const auto CData = CDataPtr->newEnd(Sym);
  592. return setContainerData(State, Cont, CData);
  593. }
  594. } else {
  595. const auto CData = ContainerData::fromEnd(Sym);
  596. return setContainerData(State, Cont, CData);
  597. }
  598. }
  599. const ContainerData *getContainerData(ProgramStateRef State,
  600. const MemRegion *Cont) {
  601. return State->get<ContainerMap>(Cont);
  602. }
  603. ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
  604. const ContainerData &CData) {
  605. return State->set<ContainerMap>(Cont, CData);
  606. }
  607. const IteratorPosition *getIteratorPosition(ProgramStateRef State,
  608. const SVal &Val) {
  609. if (const auto Reg = Val.getAsRegion()) {
  610. return State->get<IteratorRegionMap>(Reg);
  611. } else if (const auto Sym = Val.getAsSymbol()) {
  612. return State->get<IteratorSymbolMap>(Sym);
  613. } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
  614. return State->get<IteratorRegionMap>(LCVal->getRegion());
  615. }
  616. return nullptr;
  617. }
  618. const IteratorPosition *getIteratorPosition(ProgramStateRef State,
  619. RegionOrSymbol RegOrSym) {
  620. if (RegOrSym.is<const MemRegion *>()) {
  621. return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
  622. } else if (RegOrSym.is<SymbolRef>()) {
  623. return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
  624. }
  625. return nullptr;
  626. }
  627. ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
  628. const IteratorPosition &Pos) {
  629. if (const auto Reg = Val.getAsRegion()) {
  630. return State->set<IteratorRegionMap>(Reg, Pos);
  631. } else if (const auto Sym = Val.getAsSymbol()) {
  632. return State->set<IteratorSymbolMap>(Sym, Pos);
  633. } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
  634. return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
  635. }
  636. return nullptr;
  637. }
  638. ProgramStateRef setIteratorPosition(ProgramStateRef State,
  639. RegionOrSymbol RegOrSym,
  640. const IteratorPosition &Pos) {
  641. if (RegOrSym.is<const MemRegion *>()) {
  642. return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
  643. Pos);
  644. } else if (RegOrSym.is<SymbolRef>()) {
  645. return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
  646. }
  647. return nullptr;
  648. }
  649. ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
  650. if (const auto Reg = Val.getAsRegion()) {
  651. return State->remove<IteratorRegionMap>(Reg);
  652. } else if (const auto Sym = Val.getAsSymbol()) {
  653. return State->remove<IteratorSymbolMap>(Sym);
  654. } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
  655. return State->remove<IteratorRegionMap>(LCVal->getRegion());
  656. }
  657. return nullptr;
  658. }
  659. ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
  660. RegionOrSymbol RegOrSym,
  661. const IteratorPosition &Pos,
  662. bool Equal) {
  663. if (Equal) {
  664. return setIteratorPosition(State, RegOrSym, Pos);
  665. } else {
  666. return State;
  667. }
  668. }
  669. ProgramStateRef relateIteratorPositions(ProgramStateRef State,
  670. const IteratorPosition &Pos1,
  671. const IteratorPosition &Pos2,
  672. bool Equal) {
  673. // Try to compare them and get a defined value
  674. auto &SVB = State->getStateManager().getSValBuilder();
  675. const auto comparison =
  676. SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
  677. nonloc::SymbolVal(Pos2.getOffset()), SVB.getConditionType())
  678. .getAs<DefinedSVal>();
  679. if (comparison) {
  680. return State->assume(*comparison, Equal);
  681. }
  682. return State;
  683. }
  684. bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
  685. const auto *Cont = Pos.getContainer();
  686. const auto *CData = getContainerData(State, Cont);
  687. if (!CData)
  688. return false;
  689. // Out of range means less than the begin symbol or greater or equal to the
  690. // end symbol.
  691. const auto End = CData->getEnd();
  692. if (End) {
  693. if (isGreaterOrEqual(State, Pos.getOffset(), End)) {
  694. return true;
  695. }
  696. }
  697. return false;
  698. }
  699. bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
  700. return compare(State, Sym1, Sym2, BO_GE);
  701. }
  702. bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
  703. BinaryOperator::Opcode Opc) {
  704. auto &SMgr = State->getStateManager();
  705. auto &SVB = SMgr.getSValBuilder();
  706. const auto comparison =
  707. SVB.evalBinOp(State, Opc, nonloc::SymbolVal(Sym1),
  708. nonloc::SymbolVal(Sym2), SVB.getConditionType())
  709. .getAs<DefinedSVal>();
  710. if(comparison) {
  711. return !!State->assume(*comparison, true);
  712. }
  713. return false;
  714. }
  715. } // namespace
  716. #define REGISTER_CHECKER(name) \
  717. void ento::register##name(CheckerManager &Mgr) { \
  718. auto *checker = Mgr.registerChecker<IteratorChecker>(); \
  719. checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
  720. checker->CheckNames[IteratorChecker::CK_##name] = \
  721. Mgr.getCurrentCheckName(); \
  722. }
  723. REGISTER_CHECKER(IteratorRangeChecker)