IteratorRangeChecker.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. //===-- IteratorRangeChecker.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 dereference of the past-the-end iterator and
  10. // out-of-range increments and decrements.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  14. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  15. #include "clang/StaticAnalyzer/Core/Checker.h"
  16. #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.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 IteratorRangeChecker
  25. : public Checker<check::PreCall, check::PreStmt<UnaryOperator>,
  26. check::PreStmt<BinaryOperator>,
  27. check::PreStmt<ArraySubscriptExpr>,
  28. check::PreStmt<MemberExpr>> {
  29. std::unique_ptr<BugType> OutOfRangeBugType;
  30. void verifyDereference(CheckerContext &C, SVal Val) const;
  31. void verifyIncrement(CheckerContext &C, SVal Iter) const;
  32. void verifyDecrement(CheckerContext &C, SVal Iter) const;
  33. void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
  34. SVal LHS, SVal RHS) const;
  35. void verifyAdvance(CheckerContext &C, SVal LHS, SVal RHS) const;
  36. void verifyPrev(CheckerContext &C, SVal LHS, SVal RHS) const;
  37. void verifyNext(CheckerContext &C, SVal LHS, SVal RHS) const;
  38. void reportBug(const StringRef &Message, SVal Val, CheckerContext &C,
  39. ExplodedNode *ErrNode) const;
  40. public:
  41. IteratorRangeChecker();
  42. void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
  43. void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const;
  44. void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
  45. void checkPreStmt(const ArraySubscriptExpr *ASE, CheckerContext &C) const;
  46. void checkPreStmt(const MemberExpr *ME, CheckerContext &C) const;
  47. using AdvanceFn = void (IteratorRangeChecker::*)(CheckerContext &, SVal,
  48. SVal) const;
  49. CallDescriptionMap<AdvanceFn> AdvanceFunctions = {
  50. {{{"std", "advance"}, 2}, &IteratorRangeChecker::verifyAdvance},
  51. {{{"std", "prev"}, 2}, &IteratorRangeChecker::verifyPrev},
  52. {{{"std", "next"}, 2}, &IteratorRangeChecker::verifyNext},
  53. };
  54. };
  55. bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
  56. bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos);
  57. bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
  58. bool isZero(ProgramStateRef State, const NonLoc &Val);
  59. } //namespace
  60. IteratorRangeChecker::IteratorRangeChecker() {
  61. OutOfRangeBugType.reset(
  62. new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
  63. }
  64. void IteratorRangeChecker::checkPreCall(const CallEvent &Call,
  65. CheckerContext &C) const {
  66. // Check for out of range access
  67. const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
  68. if (!Func)
  69. return;
  70. if (Func->isOverloadedOperator()) {
  71. if (isIncrementOperator(Func->getOverloadedOperator())) {
  72. // Check for out-of-range incrementions
  73. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  74. verifyIncrement(C, InstCall->getCXXThisVal());
  75. } else {
  76. if (Call.getNumArgs() >= 1) {
  77. verifyIncrement(C, Call.getArgSVal(0));
  78. }
  79. }
  80. } else if (isDecrementOperator(Func->getOverloadedOperator())) {
  81. // Check for out-of-range decrementions
  82. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  83. verifyDecrement(C, InstCall->getCXXThisVal());
  84. } else {
  85. if (Call.getNumArgs() >= 1) {
  86. verifyDecrement(C, Call.getArgSVal(0));
  87. }
  88. }
  89. } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
  90. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  91. // Check for out-of-range incrementions and decrementions
  92. if (Call.getNumArgs() >= 1 &&
  93. Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
  94. verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
  95. InstCall->getCXXThisVal(),
  96. Call.getArgSVal(0));
  97. }
  98. } else {
  99. if (Call.getNumArgs() >= 2 &&
  100. Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
  101. verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
  102. Call.getArgSVal(0), Call.getArgSVal(1));
  103. }
  104. }
  105. } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
  106. // Check for dereference of out-of-range iterators
  107. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  108. verifyDereference(C, InstCall->getCXXThisVal());
  109. } else {
  110. verifyDereference(C, Call.getArgSVal(0));
  111. }
  112. }
  113. } else {
  114. const AdvanceFn *Verifier = AdvanceFunctions.lookup(Call);
  115. if (Verifier) {
  116. if (Call.getNumArgs() > 1) {
  117. (this->**Verifier)(C, Call.getArgSVal(0), Call.getArgSVal(1));
  118. } else {
  119. auto &BVF = C.getSValBuilder().getBasicValueFactory();
  120. (this->**Verifier)(
  121. C, Call.getArgSVal(0),
  122. nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
  123. }
  124. }
  125. }
  126. }
  127. void IteratorRangeChecker::checkPreStmt(const UnaryOperator *UO,
  128. CheckerContext &C) const {
  129. if (isa<CXXThisExpr>(UO->getSubExpr()))
  130. return;
  131. ProgramStateRef State = C.getState();
  132. UnaryOperatorKind OK = UO->getOpcode();
  133. SVal SubVal = State->getSVal(UO->getSubExpr(), C.getLocationContext());
  134. if (isDereferenceOperator(OK)) {
  135. verifyDereference(C, SubVal);
  136. } else if (isIncrementOperator(OK)) {
  137. verifyIncrement(C, SubVal);
  138. } else if (isDecrementOperator(OK)) {
  139. verifyDecrement(C, SubVal);
  140. }
  141. }
  142. void IteratorRangeChecker::checkPreStmt(const BinaryOperator *BO,
  143. CheckerContext &C) const {
  144. ProgramStateRef State = C.getState();
  145. BinaryOperatorKind OK = BO->getOpcode();
  146. SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
  147. if (isDereferenceOperator(OK)) {
  148. verifyDereference(C, LVal);
  149. } else if (isRandomIncrOrDecrOperator(OK)) {
  150. SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
  151. if (!BO->getRHS()->getType()->isIntegralOrEnumerationType())
  152. return;
  153. verifyRandomIncrOrDecr(C, BinaryOperator::getOverloadedOperator(OK), LVal,
  154. RVal);
  155. }
  156. }
  157. void IteratorRangeChecker::checkPreStmt(const ArraySubscriptExpr *ASE,
  158. CheckerContext &C) const {
  159. ProgramStateRef State = C.getState();
  160. SVal LVal = State->getSVal(ASE->getLHS(), C.getLocationContext());
  161. verifyDereference(C, LVal);
  162. }
  163. void IteratorRangeChecker::checkPreStmt(const MemberExpr *ME,
  164. CheckerContext &C) const {
  165. if (!ME->isArrow() || ME->isImplicitAccess())
  166. return;
  167. ProgramStateRef State = C.getState();
  168. SVal BaseVal = State->getSVal(ME->getBase(), C.getLocationContext());
  169. verifyDereference(C, BaseVal);
  170. }
  171. void IteratorRangeChecker::verifyDereference(CheckerContext &C,
  172. SVal Val) const {
  173. auto State = C.getState();
  174. const auto *Pos = getIteratorPosition(State, Val);
  175. if (Pos && isPastTheEnd(State, *Pos)) {
  176. auto *N = C.generateErrorNode(State);
  177. if (!N)
  178. return;
  179. reportBug("Past-the-end iterator dereferenced.", Val, C, N);
  180. return;
  181. }
  182. }
  183. void IteratorRangeChecker::verifyIncrement(CheckerContext &C, SVal Iter) const {
  184. auto &BVF = C.getSValBuilder().getBasicValueFactory();
  185. verifyRandomIncrOrDecr(C, OO_Plus, Iter,
  186. nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
  187. }
  188. void IteratorRangeChecker::verifyDecrement(CheckerContext &C, SVal Iter) const {
  189. auto &BVF = C.getSValBuilder().getBasicValueFactory();
  190. verifyRandomIncrOrDecr(C, OO_Minus, Iter,
  191. nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
  192. }
  193. void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &C,
  194. OverloadedOperatorKind Op,
  195. SVal LHS, SVal RHS) const {
  196. auto State = C.getState();
  197. auto Value = RHS;
  198. if (auto ValAsLoc = RHS.getAs<Loc>()) {
  199. Value = State->getRawSVal(*ValAsLoc);
  200. }
  201. if (Value.isUnknownOrUndef())
  202. return;
  203. // Incremention or decremention by 0 is never a bug.
  204. if (isZero(State, Value.castAs<NonLoc>()))
  205. return;
  206. // The result may be the past-end iterator of the container, but any other
  207. // out of range position is undefined behaviour
  208. auto StateAfter = advancePosition(State, LHS, Op, Value);
  209. if (!StateAfter)
  210. return;
  211. const auto *PosAfter = getIteratorPosition(StateAfter, LHS);
  212. assert(PosAfter &&
  213. "Iterator should have position after successful advancement");
  214. if (isAheadOfRange(State, *PosAfter)) {
  215. auto *N = C.generateErrorNode(State);
  216. if (!N)
  217. return;
  218. reportBug("Iterator decremented ahead of its valid range.", LHS,
  219. C, N);
  220. }
  221. if (isBehindPastTheEnd(State, *PosAfter)) {
  222. auto *N = C.generateErrorNode(State);
  223. if (!N)
  224. return;
  225. reportBug("Iterator incremented behind the past-the-end "
  226. "iterator.", LHS, C, N);
  227. }
  228. }
  229. void IteratorRangeChecker::verifyAdvance(CheckerContext &C, SVal LHS,
  230. SVal RHS) const {
  231. verifyRandomIncrOrDecr(C, OO_PlusEqual, LHS, RHS);
  232. }
  233. void IteratorRangeChecker::verifyPrev(CheckerContext &C, SVal LHS,
  234. SVal RHS) const {
  235. verifyRandomIncrOrDecr(C, OO_Minus, LHS, RHS);
  236. }
  237. void IteratorRangeChecker::verifyNext(CheckerContext &C, SVal LHS,
  238. SVal RHS) const {
  239. verifyRandomIncrOrDecr(C, OO_Plus, LHS, RHS);
  240. }
  241. void IteratorRangeChecker::reportBug(const StringRef &Message, SVal Val,
  242. CheckerContext &C,
  243. ExplodedNode *ErrNode) const {
  244. auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
  245. ErrNode);
  246. const auto *Pos = getIteratorPosition(C.getState(), Val);
  247. assert(Pos && "Iterator without known position cannot be out-of-range.");
  248. R->markInteresting(Val);
  249. R->markInteresting(Pos->getContainer());
  250. C.emitReport(std::move(R));
  251. }
  252. namespace {
  253. bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
  254. bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
  255. bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
  256. bool isZero(ProgramStateRef State, const NonLoc &Val) {
  257. auto &BVF = State->getBasicVals();
  258. return compare(State, Val,
  259. nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
  260. BO_EQ);
  261. }
  262. bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
  263. const auto *Cont = Pos.getContainer();
  264. const auto *CData = getContainerData(State, Cont);
  265. if (!CData)
  266. return false;
  267. const auto End = CData->getEnd();
  268. if (End) {
  269. if (isEqual(State, Pos.getOffset(), End)) {
  270. return true;
  271. }
  272. }
  273. return false;
  274. }
  275. bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
  276. const auto *Cont = Pos.getContainer();
  277. const auto *CData = getContainerData(State, Cont);
  278. if (!CData)
  279. return false;
  280. const auto Beg = CData->getBegin();
  281. if (Beg) {
  282. if (isLess(State, Pos.getOffset(), Beg)) {
  283. return true;
  284. }
  285. }
  286. return false;
  287. }
  288. bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
  289. const auto *Cont = Pos.getContainer();
  290. const auto *CData = getContainerData(State, Cont);
  291. if (!CData)
  292. return false;
  293. const auto End = CData->getEnd();
  294. if (End) {
  295. if (isGreater(State, Pos.getOffset(), End)) {
  296. return true;
  297. }
  298. }
  299. return false;
  300. }
  301. bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
  302. return compare(State, Sym1, Sym2, BO_LT);
  303. }
  304. bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
  305. return compare(State, Sym1, Sym2, BO_GT);
  306. }
  307. bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
  308. return compare(State, Sym1, Sym2, BO_EQ);
  309. }
  310. } // namespace
  311. void ento::registerIteratorRangeChecker(CheckerManager &mgr) {
  312. mgr.registerChecker<IteratorRangeChecker>();
  313. }
  314. bool ento::shouldRegisterIteratorRangeChecker(const CheckerManager &mgr) {
  315. return true;
  316. }