MismatchedIteratorChecker.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. //===-- MismatchedIteratorChecker.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 checker for mistakenly applying a foreign iterator on a container
  10. // and for using iterators of two different containers in a context where
  11. // iterators of the same container should be used.
  12. //
  13. //===----------------------------------------------------------------------===//
  14. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  15. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  16. #include "clang/StaticAnalyzer/Core/Checker.h"
  17. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  18. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  19. #include "Iterator.h"
  20. using namespace clang;
  21. using namespace ento;
  22. using namespace iterator;
  23. namespace {
  24. class MismatchedIteratorChecker
  25. : public Checker<check::PreCall, check::PreStmt<BinaryOperator>> {
  26. std::unique_ptr<BugType> MismatchedBugType;
  27. void verifyMatch(CheckerContext &C, const SVal &Iter,
  28. const MemRegion *Cont) const;
  29. void verifyMatch(CheckerContext &C, const SVal &Iter1,
  30. const SVal &Iter2) const;
  31. void reportBug(const StringRef &Message, const SVal &Val1,
  32. const SVal &Val2, CheckerContext &C,
  33. ExplodedNode *ErrNode) const;
  34. void reportBug(const StringRef &Message, const SVal &Val,
  35. const MemRegion *Reg, CheckerContext &C,
  36. ExplodedNode *ErrNode) const;
  37. public:
  38. MismatchedIteratorChecker();
  39. void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
  40. void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
  41. };
  42. } // namespace
  43. MismatchedIteratorChecker::MismatchedIteratorChecker() {
  44. MismatchedBugType.reset(
  45. new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
  46. /*SuppressOnSink=*/true));
  47. }
  48. void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call,
  49. CheckerContext &C) const {
  50. // Check for iterator mismatches
  51. const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
  52. if (!Func)
  53. return;
  54. if (Func->isOverloadedOperator() &&
  55. isComparisonOperator(Func->getOverloadedOperator())) {
  56. // Check for comparisons of iterators of different containers
  57. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  58. if (Call.getNumArgs() < 1)
  59. return;
  60. if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
  61. !isIteratorType(Call.getArgExpr(0)->getType()))
  62. return;
  63. verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
  64. } else {
  65. if (Call.getNumArgs() < 2)
  66. return;
  67. if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
  68. !isIteratorType(Call.getArgExpr(1)->getType()))
  69. return;
  70. verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
  71. }
  72. } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  73. const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
  74. if (!ContReg)
  75. return;
  76. // Check for erase, insert and emplace using iterator of another container
  77. if (isEraseCall(Func) || isEraseAfterCall(Func)) {
  78. verifyMatch(C, Call.getArgSVal(0),
  79. InstCall->getCXXThisVal().getAsRegion());
  80. if (Call.getNumArgs() == 2) {
  81. verifyMatch(C, Call.getArgSVal(1),
  82. InstCall->getCXXThisVal().getAsRegion());
  83. }
  84. } else if (isInsertCall(Func)) {
  85. verifyMatch(C, Call.getArgSVal(0),
  86. InstCall->getCXXThisVal().getAsRegion());
  87. if (Call.getNumArgs() == 3 &&
  88. isIteratorType(Call.getArgExpr(1)->getType()) &&
  89. isIteratorType(Call.getArgExpr(2)->getType())) {
  90. verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
  91. }
  92. } else if (isEmplaceCall(Func)) {
  93. verifyMatch(C, Call.getArgSVal(0),
  94. InstCall->getCXXThisVal().getAsRegion());
  95. }
  96. } else if (isa<CXXConstructorCall>(&Call)) {
  97. // Check match of first-last iterator pair in a constructor of a container
  98. if (Call.getNumArgs() < 2)
  99. return;
  100. const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
  101. if (Ctr->getNumParams() < 2)
  102. return;
  103. if (Ctr->getParamDecl(0)->getName() != "first" ||
  104. Ctr->getParamDecl(1)->getName() != "last")
  105. return;
  106. if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
  107. !isIteratorType(Call.getArgExpr(1)->getType()))
  108. return;
  109. verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
  110. } else {
  111. // The main purpose of iterators is to abstract away from different
  112. // containers and provide a (maybe limited) uniform access to them.
  113. // This implies that any correctly written template function that
  114. // works on multiple containers using iterators takes different
  115. // template parameters for different containers. So we can safely
  116. // assume that passing iterators of different containers as arguments
  117. // whose type replaces the same template parameter is a bug.
  118. //
  119. // Example:
  120. // template<typename I1, typename I2>
  121. // void f(I1 first1, I1 last1, I2 first2, I2 last2);
  122. //
  123. // In this case the first two arguments to f() must be iterators must belong
  124. // to the same container and the last to also to the same container but
  125. // not necessarily to the same as the first two.
  126. const auto *Templ = Func->getPrimaryTemplate();
  127. if (!Templ)
  128. return;
  129. const auto *TParams = Templ->getTemplateParameters();
  130. const auto *TArgs = Func->getTemplateSpecializationArgs();
  131. // Iterate over all the template parameters
  132. for (size_t I = 0; I < TParams->size(); ++I) {
  133. const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
  134. if (!TPDecl)
  135. continue;
  136. if (TPDecl->isParameterPack())
  137. continue;
  138. const auto TAType = TArgs->get(I).getAsType();
  139. if (!isIteratorType(TAType))
  140. continue;
  141. SVal LHS = UndefinedVal();
  142. // For every template parameter which is an iterator type in the
  143. // instantiation look for all functions' parameters' type by it and
  144. // check whether they belong to the same container
  145. for (auto J = 0U; J < Func->getNumParams(); ++J) {
  146. const auto *Param = Func->getParamDecl(J);
  147. const auto *ParamType =
  148. Param->getType()->getAs<SubstTemplateTypeParmType>();
  149. if (!ParamType ||
  150. ParamType->getReplacedParameter()->getDecl() != TPDecl)
  151. continue;
  152. if (LHS.isUndef()) {
  153. LHS = Call.getArgSVal(J);
  154. } else {
  155. verifyMatch(C, LHS, Call.getArgSVal(J));
  156. }
  157. }
  158. }
  159. }
  160. }
  161. void MismatchedIteratorChecker::checkPreStmt(const BinaryOperator *BO,
  162. CheckerContext &C) const {
  163. if (!BO->isComparisonOp())
  164. return;
  165. ProgramStateRef State = C.getState();
  166. SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
  167. SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
  168. verifyMatch(C, LVal, RVal);
  169. }
  170. void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
  171. const MemRegion *Cont) const {
  172. // Verify match between a container and the container of an iterator
  173. Cont = Cont->getMostDerivedObjectRegion();
  174. if (const auto *ContSym = Cont->getSymbolicBase()) {
  175. if (isa<SymbolConjured>(ContSym->getSymbol()))
  176. return;
  177. }
  178. auto State = C.getState();
  179. const auto *Pos = getIteratorPosition(State, Iter);
  180. if (!Pos)
  181. return;
  182. const auto *IterCont = Pos->getContainer();
  183. // Skip symbolic regions based on conjured symbols. Two conjured symbols
  184. // may or may not be the same. For example, the same function can return
  185. // the same or a different container but we get different conjured symbols
  186. // for each call. This may cause false positives so omit them from the check.
  187. if (const auto *ContSym = IterCont->getSymbolicBase()) {
  188. if (isa<SymbolConjured>(ContSym->getSymbol()))
  189. return;
  190. }
  191. if (IterCont != Cont) {
  192. auto *N = C.generateNonFatalErrorNode(State);
  193. if (!N) {
  194. return;
  195. }
  196. reportBug("Container accessed using foreign iterator argument.",
  197. Iter, Cont, C, N);
  198. }
  199. }
  200. void MismatchedIteratorChecker::verifyMatch(CheckerContext &C,
  201. const SVal &Iter1,
  202. const SVal &Iter2) const {
  203. // Verify match between the containers of two iterators
  204. auto State = C.getState();
  205. const auto *Pos1 = getIteratorPosition(State, Iter1);
  206. if (!Pos1)
  207. return;
  208. const auto *IterCont1 = Pos1->getContainer();
  209. // Skip symbolic regions based on conjured symbols. Two conjured symbols
  210. // may or may not be the same. For example, the same function can return
  211. // the same or a different container but we get different conjured symbols
  212. // for each call. This may cause false positives so omit them from the check.
  213. if (const auto *ContSym = IterCont1->getSymbolicBase()) {
  214. if (isa<SymbolConjured>(ContSym->getSymbol()))
  215. return;
  216. }
  217. const auto *Pos2 = getIteratorPosition(State, Iter2);
  218. if (!Pos2)
  219. return;
  220. const auto *IterCont2 = Pos2->getContainer();
  221. if (const auto *ContSym = IterCont2->getSymbolicBase()) {
  222. if (isa<SymbolConjured>(ContSym->getSymbol()))
  223. return;
  224. }
  225. if (IterCont1 != IterCont2) {
  226. auto *N = C.generateNonFatalErrorNode(State);
  227. if (!N)
  228. return;
  229. reportBug("Iterators of different containers used where the "
  230. "same container is expected.", Iter1, Iter2, C, N);
  231. }
  232. }
  233. void MismatchedIteratorChecker::reportBug(const StringRef &Message,
  234. const SVal &Val1,
  235. const SVal &Val2,
  236. CheckerContext &C,
  237. ExplodedNode *ErrNode) const {
  238. auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
  239. ErrNode);
  240. R->markInteresting(Val1);
  241. R->markInteresting(Val2);
  242. C.emitReport(std::move(R));
  243. }
  244. void MismatchedIteratorChecker::reportBug(const StringRef &Message,
  245. const SVal &Val, const MemRegion *Reg,
  246. CheckerContext &C,
  247. ExplodedNode *ErrNode) const {
  248. auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
  249. ErrNode);
  250. R->markInteresting(Val);
  251. R->markInteresting(Reg);
  252. C.emitReport(std::move(R));
  253. }
  254. void ento::registerMismatchedIteratorChecker(CheckerManager &mgr) {
  255. mgr.registerChecker<MismatchedIteratorChecker>();
  256. }
  257. bool ento::shouldRegisterMismatchedIteratorChecker(const CheckerManager &mgr) {
  258. return true;
  259. }