ContainerModeling.cpp 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. //===-- ContainerModeling.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 container-like containers.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "clang/AST/DeclTemplate.h"
  13. #include "clang/Driver/DriverDiagnostic.h"
  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/CallDescription.h"
  18. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  19. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  20. #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
  21. #include "Iterator.h"
  22. #include <utility>
  23. using namespace clang;
  24. using namespace ento;
  25. using namespace iterator;
  26. namespace {
  27. class ContainerModeling
  28. : public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> {
  29. void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal,
  30. SVal Cont) const;
  31. void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal,
  32. SVal Cont) const;
  33. void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr,
  34. SVal OldCont = UndefinedVal()) const;
  35. void handleAssign(CheckerContext &C, SVal Cont, const Expr *ContE) const;
  36. void handleClear(CheckerContext &C, SVal Cont, const Expr *ContE) const;
  37. void handlePushBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
  38. void handlePopBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
  39. void handlePushFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
  40. void handlePopFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
  41. void handleInsert(CheckerContext &C, SVal Cont, SVal Iter) const;
  42. void handleErase(CheckerContext &C, SVal Cont, SVal Iter) const;
  43. void handleErase(CheckerContext &C, SVal Cont, SVal Iter1, SVal Iter2) const;
  44. void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter) const;
  45. void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter1,
  46. SVal Iter2) const;
  47. const NoteTag *getChangeTag(CheckerContext &C, StringRef Text,
  48. const MemRegion *ContReg,
  49. const Expr *ContE) const;
  50. void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
  51. const char *Sep) const override;
  52. public:
  53. ContainerModeling() = default;
  54. void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
  55. void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
  56. void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
  57. using NoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
  58. const Expr *) const;
  59. using OneItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
  60. SVal) const;
  61. using TwoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, SVal,
  62. SVal) const;
  63. CallDescriptionMap<NoItParamFn> NoIterParamFunctions = {
  64. {{"clear", 0}, &ContainerModeling::handleClear},
  65. {{"assign", 2}, &ContainerModeling::handleAssign},
  66. {{"push_back", 1}, &ContainerModeling::handlePushBack},
  67. {{"emplace_back", 1}, &ContainerModeling::handlePushBack},
  68. {{"pop_back", 0}, &ContainerModeling::handlePopBack},
  69. {{"push_front", 1}, &ContainerModeling::handlePushFront},
  70. {{"emplace_front", 1}, &ContainerModeling::handlePushFront},
  71. {{"pop_front", 0}, &ContainerModeling::handlePopFront},
  72. };
  73. CallDescriptionMap<OneItParamFn> OneIterParamFunctions = {
  74. {{"insert", 2}, &ContainerModeling::handleInsert},
  75. {{"emplace", 2}, &ContainerModeling::handleInsert},
  76. {{"erase", 1}, &ContainerModeling::handleErase},
  77. {{"erase_after", 1}, &ContainerModeling::handleEraseAfter},
  78. };
  79. CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {
  80. {{"erase", 2}, &ContainerModeling::handleErase},
  81. {{"erase_after", 2}, &ContainerModeling::handleEraseAfter},
  82. };
  83. };
  84. bool isBeginCall(const FunctionDecl *Func);
  85. bool isEndCall(const FunctionDecl *Func);
  86. bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
  87. bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
  88. bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
  89. SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
  90. SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
  91. ProgramStateRef createContainerBegin(ProgramStateRef State,
  92. const MemRegion *Cont, const Expr *E,
  93. QualType T, const LocationContext *LCtx,
  94. unsigned BlockCount);
  95. ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
  96. const Expr *E, QualType T,
  97. const LocationContext *LCtx,
  98. unsigned BlockCount);
  99. ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
  100. const ContainerData &CData);
  101. ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
  102. const MemRegion *Cont);
  103. ProgramStateRef
  104. invalidateAllIteratorPositionsExcept(ProgramStateRef State,
  105. const MemRegion *Cont, SymbolRef Offset,
  106. BinaryOperator::Opcode Opc);
  107. ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
  108. SymbolRef Offset,
  109. BinaryOperator::Opcode Opc);
  110. ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
  111. SymbolRef Offset1,
  112. BinaryOperator::Opcode Opc1,
  113. SymbolRef Offset2,
  114. BinaryOperator::Opcode Opc2);
  115. ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
  116. const MemRegion *Cont,
  117. const MemRegion *NewCont);
  118. ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
  119. const MemRegion *Cont,
  120. const MemRegion *NewCont,
  121. SymbolRef Offset,
  122. BinaryOperator::Opcode Opc);
  123. ProgramStateRef rebaseSymbolInIteratorPositionsIf(
  124. ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
  125. SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
  126. SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
  127. SymbolRef OldSym, SymbolRef NewSym);
  128. bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
  129. } // namespace
  130. void ContainerModeling::checkPostCall(const CallEvent &Call,
  131. CheckerContext &C) const {
  132. const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
  133. if (!Func)
  134. return;
  135. if (Func->isOverloadedOperator()) {
  136. const auto Op = Func->getOverloadedOperator();
  137. if (Op == OO_Equal) {
  138. // Overloaded 'operator=' must be a non-static member function.
  139. const auto *InstCall = cast<CXXInstanceCall>(&Call);
  140. if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
  141. handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
  142. Call.getArgSVal(0));
  143. return;
  144. }
  145. handleAssignment(C, InstCall->getCXXThisVal());
  146. return;
  147. }
  148. } else {
  149. if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
  150. const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call);
  151. if (Handler0) {
  152. (this->**Handler0)(C, InstCall->getCXXThisVal(),
  153. InstCall->getCXXThisExpr());
  154. return;
  155. }
  156. const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);
  157. if (Handler1) {
  158. (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
  159. return;
  160. }
  161. const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call);
  162. if (Handler2) {
  163. (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0),
  164. Call.getArgSVal(1));
  165. return;
  166. }
  167. const auto *OrigExpr = Call.getOriginExpr();
  168. if (!OrigExpr)
  169. return;
  170. if (isBeginCall(Func)) {
  171. handleBegin(C, OrigExpr, Call.getReturnValue(),
  172. InstCall->getCXXThisVal());
  173. return;
  174. }
  175. if (isEndCall(Func)) {
  176. handleEnd(C, OrigExpr, Call.getReturnValue(),
  177. InstCall->getCXXThisVal());
  178. return;
  179. }
  180. }
  181. }
  182. }
  183. void ContainerModeling::checkLiveSymbols(ProgramStateRef State,
  184. SymbolReaper &SR) const {
  185. // Keep symbolic expressions of container begins and ends alive
  186. auto ContMap = State->get<ContainerMap>();
  187. for (const auto &Cont : ContMap) {
  188. const auto CData = Cont.second;
  189. if (CData.getBegin()) {
  190. SR.markLive(CData.getBegin());
  191. if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
  192. SR.markLive(SIE->getLHS());
  193. }
  194. if (CData.getEnd()) {
  195. SR.markLive(CData.getEnd());
  196. if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
  197. SR.markLive(SIE->getLHS());
  198. }
  199. }
  200. }
  201. void ContainerModeling::checkDeadSymbols(SymbolReaper &SR,
  202. CheckerContext &C) const {
  203. // Cleanup
  204. auto State = C.getState();
  205. auto ContMap = State->get<ContainerMap>();
  206. for (const auto &Cont : ContMap) {
  207. if (!SR.isLiveRegion(Cont.first)) {
  208. // We must keep the container data while it has live iterators to be able
  209. // to compare them to the begin and the end of the container.
  210. if (!hasLiveIterators(State, Cont.first)) {
  211. State = State->remove<ContainerMap>(Cont.first);
  212. }
  213. }
  214. }
  215. C.addTransition(State);
  216. }
  217. void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE,
  218. SVal RetVal, SVal Cont) const {
  219. const auto *ContReg = Cont.getAsRegion();
  220. if (!ContReg)
  221. return;
  222. ContReg = ContReg->getMostDerivedObjectRegion();
  223. // If the container already has a begin symbol then use it. Otherwise first
  224. // create a new one.
  225. auto State = C.getState();
  226. auto BeginSym = getContainerBegin(State, ContReg);
  227. if (!BeginSym) {
  228. State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
  229. C.getLocationContext(), C.blockCount());
  230. BeginSym = getContainerBegin(State, ContReg);
  231. }
  232. State = setIteratorPosition(State, RetVal,
  233. IteratorPosition::getPosition(ContReg, BeginSym));
  234. C.addTransition(State);
  235. }
  236. void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE,
  237. SVal RetVal, SVal Cont) const {
  238. const auto *ContReg = Cont.getAsRegion();
  239. if (!ContReg)
  240. return;
  241. ContReg = ContReg->getMostDerivedObjectRegion();
  242. // If the container already has an end symbol then use it. Otherwise first
  243. // create a new one.
  244. auto State = C.getState();
  245. auto EndSym = getContainerEnd(State, ContReg);
  246. if (!EndSym) {
  247. State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
  248. C.getLocationContext(), C.blockCount());
  249. EndSym = getContainerEnd(State, ContReg);
  250. }
  251. State = setIteratorPosition(State, RetVal,
  252. IteratorPosition::getPosition(ContReg, EndSym));
  253. C.addTransition(State);
  254. }
  255. void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont,
  256. const Expr *CE, SVal OldCont) const {
  257. const auto *ContReg = Cont.getAsRegion();
  258. if (!ContReg)
  259. return;
  260. ContReg = ContReg->getMostDerivedObjectRegion();
  261. // Assignment of a new value to a container always invalidates all its
  262. // iterators
  263. auto State = C.getState();
  264. const auto CData = getContainerData(State, ContReg);
  265. if (CData) {
  266. State = invalidateAllIteratorPositions(State, ContReg);
  267. }
  268. // In case of move, iterators of the old container (except the past-end
  269. // iterators) remain valid but refer to the new container
  270. if (!OldCont.isUndef()) {
  271. const auto *OldContReg = OldCont.getAsRegion();
  272. if (OldContReg) {
  273. OldContReg = OldContReg->getMostDerivedObjectRegion();
  274. const auto OldCData = getContainerData(State, OldContReg);
  275. if (OldCData) {
  276. if (const auto OldEndSym = OldCData->getEnd()) {
  277. // If we already assigned an "end" symbol to the old container, then
  278. // first reassign all iterator positions to the new container which
  279. // are not past the container (thus not greater or equal to the
  280. // current "end" symbol).
  281. State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
  282. OldEndSym, BO_GE);
  283. auto &SymMgr = C.getSymbolManager();
  284. auto &SVB = C.getSValBuilder();
  285. // Then generate and assign a new "end" symbol for the new container.
  286. auto NewEndSym =
  287. SymMgr.conjureSymbol(CE, C.getLocationContext(),
  288. C.getASTContext().LongTy, C.blockCount());
  289. State = assumeNoOverflow(State, NewEndSym, 4);
  290. if (CData) {
  291. State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
  292. } else {
  293. State = setContainerData(State, ContReg,
  294. ContainerData::fromEnd(NewEndSym));
  295. }
  296. // Finally, replace the old "end" symbol in the already reassigned
  297. // iterator positions with the new "end" symbol.
  298. State = rebaseSymbolInIteratorPositionsIf(
  299. State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
  300. } else {
  301. // There was no "end" symbol assigned yet to the old container,
  302. // so reassign all iterator positions to the new container.
  303. State = reassignAllIteratorPositions(State, OldContReg, ContReg);
  304. }
  305. if (const auto OldBeginSym = OldCData->getBegin()) {
  306. // If we already assigned a "begin" symbol to the old container, then
  307. // assign it to the new container and remove it from the old one.
  308. if (CData) {
  309. State =
  310. setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
  311. } else {
  312. State = setContainerData(State, ContReg,
  313. ContainerData::fromBegin(OldBeginSym));
  314. }
  315. State =
  316. setContainerData(State, OldContReg, OldCData->newBegin(nullptr));
  317. }
  318. } else {
  319. // There was neither "begin" nor "end" symbol assigned yet to the old
  320. // container, so reassign all iterator positions to the new container.
  321. State = reassignAllIteratorPositions(State, OldContReg, ContReg);
  322. }
  323. }
  324. }
  325. C.addTransition(State);
  326. }
  327. void ContainerModeling::handleAssign(CheckerContext &C, SVal Cont,
  328. const Expr *ContE) const {
  329. const auto *ContReg = Cont.getAsRegion();
  330. if (!ContReg)
  331. return;
  332. ContReg = ContReg->getMostDerivedObjectRegion();
  333. // The assign() operation invalidates all the iterators
  334. auto State = C.getState();
  335. State = invalidateAllIteratorPositions(State, ContReg);
  336. C.addTransition(State);
  337. }
  338. void ContainerModeling::handleClear(CheckerContext &C, SVal Cont,
  339. const Expr *ContE) const {
  340. const auto *ContReg = Cont.getAsRegion();
  341. if (!ContReg)
  342. return;
  343. ContReg = ContReg->getMostDerivedObjectRegion();
  344. // The clear() operation invalidates all the iterators, except the past-end
  345. // iterators of list-like containers
  346. auto State = C.getState();
  347. if (!hasSubscriptOperator(State, ContReg) ||
  348. !backModifiable(State, ContReg)) {
  349. const auto CData = getContainerData(State, ContReg);
  350. if (CData) {
  351. if (const auto EndSym = CData->getEnd()) {
  352. State =
  353. invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
  354. C.addTransition(State);
  355. return;
  356. }
  357. }
  358. }
  359. const NoteTag *ChangeTag =
  360. getChangeTag(C, "became empty", ContReg, ContE);
  361. State = invalidateAllIteratorPositions(State, ContReg);
  362. C.addTransition(State, ChangeTag);
  363. }
  364. void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont,
  365. const Expr *ContE) const {
  366. const auto *ContReg = Cont.getAsRegion();
  367. if (!ContReg)
  368. return;
  369. ContReg = ContReg->getMostDerivedObjectRegion();
  370. // For deque-like containers invalidate all iterator positions
  371. auto State = C.getState();
  372. if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
  373. State = invalidateAllIteratorPositions(State, ContReg);
  374. C.addTransition(State);
  375. return;
  376. }
  377. const auto CData = getContainerData(State, ContReg);
  378. if (!CData)
  379. return;
  380. // For vector-like containers invalidate the past-end iterator positions
  381. if (const auto EndSym = CData->getEnd()) {
  382. if (hasSubscriptOperator(State, ContReg)) {
  383. State = invalidateIteratorPositions(State, EndSym, BO_GE);
  384. }
  385. auto &SymMgr = C.getSymbolManager();
  386. auto &BVF = SymMgr.getBasicVals();
  387. auto &SVB = C.getSValBuilder();
  388. const auto newEndSym =
  389. SVB.evalBinOp(State, BO_Add,
  390. nonloc::SymbolVal(EndSym),
  391. nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
  392. SymMgr.getType(EndSym)).getAsSymbol();
  393. const NoteTag *ChangeTag =
  394. getChangeTag(C, "extended to the back by 1 position", ContReg, ContE);
  395. State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
  396. C.addTransition(State, ChangeTag);
  397. }
  398. }
  399. void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont,
  400. const Expr *ContE) const {
  401. const auto *ContReg = Cont.getAsRegion();
  402. if (!ContReg)
  403. return;
  404. ContReg = ContReg->getMostDerivedObjectRegion();
  405. auto State = C.getState();
  406. const auto CData = getContainerData(State, ContReg);
  407. if (!CData)
  408. return;
  409. if (const auto EndSym = CData->getEnd()) {
  410. auto &SymMgr = C.getSymbolManager();
  411. auto &BVF = SymMgr.getBasicVals();
  412. auto &SVB = C.getSValBuilder();
  413. const auto BackSym =
  414. SVB.evalBinOp(State, BO_Sub,
  415. nonloc::SymbolVal(EndSym),
  416. nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
  417. SymMgr.getType(EndSym)).getAsSymbol();
  418. const NoteTag *ChangeTag =
  419. getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE);
  420. // For vector-like and deque-like containers invalidate the last and the
  421. // past-end iterator positions. For list-like containers only invalidate
  422. // the last position
  423. if (hasSubscriptOperator(State, ContReg) &&
  424. backModifiable(State, ContReg)) {
  425. State = invalidateIteratorPositions(State, BackSym, BO_GE);
  426. State = setContainerData(State, ContReg, CData->newEnd(nullptr));
  427. } else {
  428. State = invalidateIteratorPositions(State, BackSym, BO_EQ);
  429. }
  430. auto newEndSym = BackSym;
  431. State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
  432. C.addTransition(State, ChangeTag);
  433. }
  434. }
  435. void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont,
  436. const Expr *ContE) const {
  437. const auto *ContReg = Cont.getAsRegion();
  438. if (!ContReg)
  439. return;
  440. ContReg = ContReg->getMostDerivedObjectRegion();
  441. // For deque-like containers invalidate all iterator positions
  442. auto State = C.getState();
  443. if (hasSubscriptOperator(State, ContReg)) {
  444. State = invalidateAllIteratorPositions(State, ContReg);
  445. C.addTransition(State);
  446. } else {
  447. const auto CData = getContainerData(State, ContReg);
  448. if (!CData)
  449. return;
  450. if (const auto BeginSym = CData->getBegin()) {
  451. auto &SymMgr = C.getSymbolManager();
  452. auto &BVF = SymMgr.getBasicVals();
  453. auto &SVB = C.getSValBuilder();
  454. const auto newBeginSym =
  455. SVB.evalBinOp(State, BO_Sub,
  456. nonloc::SymbolVal(BeginSym),
  457. nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
  458. SymMgr.getType(BeginSym)).getAsSymbol();
  459. const NoteTag *ChangeTag =
  460. getChangeTag(C, "extended to the front by 1 position", ContReg, ContE);
  461. State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
  462. C.addTransition(State, ChangeTag);
  463. }
  464. }
  465. }
  466. void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont,
  467. const Expr *ContE) const {
  468. const auto *ContReg = Cont.getAsRegion();
  469. if (!ContReg)
  470. return;
  471. ContReg = ContReg->getMostDerivedObjectRegion();
  472. auto State = C.getState();
  473. const auto CData = getContainerData(State, ContReg);
  474. if (!CData)
  475. return;
  476. // For deque-like containers invalidate all iterator positions. For list-like
  477. // iterators only invalidate the first position
  478. if (const auto BeginSym = CData->getBegin()) {
  479. if (hasSubscriptOperator(State, ContReg)) {
  480. State = invalidateIteratorPositions(State, BeginSym, BO_LE);
  481. } else {
  482. State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
  483. }
  484. auto &SymMgr = C.getSymbolManager();
  485. auto &BVF = SymMgr.getBasicVals();
  486. auto &SVB = C.getSValBuilder();
  487. const auto newBeginSym =
  488. SVB.evalBinOp(State, BO_Add,
  489. nonloc::SymbolVal(BeginSym),
  490. nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
  491. SymMgr.getType(BeginSym)).getAsSymbol();
  492. const NoteTag *ChangeTag =
  493. getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE);
  494. State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
  495. C.addTransition(State, ChangeTag);
  496. }
  497. }
  498. void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont,
  499. SVal Iter) const {
  500. const auto *ContReg = Cont.getAsRegion();
  501. if (!ContReg)
  502. return;
  503. ContReg = ContReg->getMostDerivedObjectRegion();
  504. auto State = C.getState();
  505. const auto *Pos = getIteratorPosition(State, Iter);
  506. if (!Pos)
  507. return;
  508. // For deque-like containers invalidate all iterator positions. For
  509. // vector-like containers invalidate iterator positions after the insertion.
  510. if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
  511. if (frontModifiable(State, ContReg)) {
  512. State = invalidateAllIteratorPositions(State, ContReg);
  513. } else {
  514. State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
  515. }
  516. if (const auto *CData = getContainerData(State, ContReg)) {
  517. if (const auto EndSym = CData->getEnd()) {
  518. State = invalidateIteratorPositions(State, EndSym, BO_GE);
  519. State = setContainerData(State, ContReg, CData->newEnd(nullptr));
  520. }
  521. }
  522. C.addTransition(State);
  523. }
  524. }
  525. void ContainerModeling::handleErase(CheckerContext &C, SVal Cont,
  526. SVal Iter) const {
  527. const auto *ContReg = Cont.getAsRegion();
  528. if (!ContReg)
  529. return;
  530. ContReg = ContReg->getMostDerivedObjectRegion();
  531. auto State = C.getState();
  532. const auto *Pos = getIteratorPosition(State, Iter);
  533. if (!Pos)
  534. return;
  535. // For deque-like containers invalidate all iterator positions. For
  536. // vector-like containers invalidate iterator positions at and after the
  537. // deletion. For list-like containers only invalidate the deleted position.
  538. if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
  539. if (frontModifiable(State, ContReg)) {
  540. State = invalidateAllIteratorPositions(State, ContReg);
  541. } else {
  542. State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
  543. }
  544. if (const auto *CData = getContainerData(State, ContReg)) {
  545. if (const auto EndSym = CData->getEnd()) {
  546. State = invalidateIteratorPositions(State, EndSym, BO_GE);
  547. State = setContainerData(State, ContReg, CData->newEnd(nullptr));
  548. }
  549. }
  550. } else {
  551. State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
  552. }
  553. C.addTransition(State);
  554. }
  555. void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1,
  556. SVal Iter2) const {
  557. const auto *ContReg = Cont.getAsRegion();
  558. if (!ContReg)
  559. return;
  560. ContReg = ContReg->getMostDerivedObjectRegion();
  561. auto State = C.getState();
  562. const auto *Pos1 = getIteratorPosition(State, Iter1);
  563. const auto *Pos2 = getIteratorPosition(State, Iter2);
  564. if (!Pos1 || !Pos2)
  565. return;
  566. // For deque-like containers invalidate all iterator positions. For
  567. // vector-like containers invalidate iterator positions at and after the
  568. // deletion range. For list-like containers only invalidate the deleted
  569. // position range [first..last].
  570. if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
  571. if (frontModifiable(State, ContReg)) {
  572. State = invalidateAllIteratorPositions(State, ContReg);
  573. } else {
  574. State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
  575. }
  576. if (const auto *CData = getContainerData(State, ContReg)) {
  577. if (const auto EndSym = CData->getEnd()) {
  578. State = invalidateIteratorPositions(State, EndSym, BO_GE);
  579. State = setContainerData(State, ContReg, CData->newEnd(nullptr));
  580. }
  581. }
  582. } else {
  583. State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
  584. Pos2->getOffset(), BO_LT);
  585. }
  586. C.addTransition(State);
  587. }
  588. void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
  589. SVal Iter) const {
  590. auto State = C.getState();
  591. const auto *Pos = getIteratorPosition(State, Iter);
  592. if (!Pos)
  593. return;
  594. // Invalidate the deleted iterator position, which is the position of the
  595. // parameter plus one.
  596. auto &SymMgr = C.getSymbolManager();
  597. auto &BVF = SymMgr.getBasicVals();
  598. auto &SVB = C.getSValBuilder();
  599. const auto NextSym =
  600. SVB.evalBinOp(State, BO_Add,
  601. nonloc::SymbolVal(Pos->getOffset()),
  602. nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
  603. SymMgr.getType(Pos->getOffset())).getAsSymbol();
  604. State = invalidateIteratorPositions(State, NextSym, BO_EQ);
  605. C.addTransition(State);
  606. }
  607. void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
  608. SVal Iter1, SVal Iter2) const {
  609. auto State = C.getState();
  610. const auto *Pos1 = getIteratorPosition(State, Iter1);
  611. const auto *Pos2 = getIteratorPosition(State, Iter2);
  612. if (!Pos1 || !Pos2)
  613. return;
  614. // Invalidate the deleted iterator position range (first..last)
  615. State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
  616. Pos2->getOffset(), BO_LT);
  617. C.addTransition(State);
  618. }
  619. const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C,
  620. StringRef Text,
  621. const MemRegion *ContReg,
  622. const Expr *ContE) const {
  623. StringRef Name;
  624. // First try to get the name of the variable from the region
  625. if (const auto *DR = dyn_cast<DeclRegion>(ContReg)) {
  626. Name = DR->getDecl()->getName();
  627. // If the region is not a `DeclRegion` then use the expression instead
  628. } else if (const auto *DRE =
  629. dyn_cast<DeclRefExpr>(ContE->IgnoreParenCasts())) {
  630. Name = DRE->getDecl()->getName();
  631. }
  632. return C.getNoteTag(
  633. [Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string {
  634. if (!BR.isInteresting(ContReg))
  635. return "";
  636. SmallString<256> Msg;
  637. llvm::raw_svector_ostream Out(Msg);
  638. Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" )
  639. << Text;
  640. return std::string(Out.str());
  641. });
  642. }
  643. void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State,
  644. const char *NL, const char *Sep) const {
  645. auto ContMap = State->get<ContainerMap>();
  646. if (!ContMap.isEmpty()) {
  647. Out << Sep << "Container Data :" << NL;
  648. for (const auto &Cont : ContMap) {
  649. Cont.first->dumpToStream(Out);
  650. Out << " : [ ";
  651. const auto CData = Cont.second;
  652. if (CData.getBegin())
  653. CData.getBegin()->dumpToStream(Out);
  654. else
  655. Out << "<Unknown>";
  656. Out << " .. ";
  657. if (CData.getEnd())
  658. CData.getEnd()->dumpToStream(Out);
  659. else
  660. Out << "<Unknown>";
  661. Out << " ]";
  662. }
  663. }
  664. }
  665. namespace {
  666. bool isBeginCall(const FunctionDecl *Func) {
  667. const auto *IdInfo = Func->getIdentifier();
  668. if (!IdInfo)
  669. return false;
  670. return IdInfo->getName().endswith_insensitive("begin");
  671. }
  672. bool isEndCall(const FunctionDecl *Func) {
  673. const auto *IdInfo = Func->getIdentifier();
  674. if (!IdInfo)
  675. return false;
  676. return IdInfo->getName().endswith_insensitive("end");
  677. }
  678. const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
  679. const MemRegion *Reg) {
  680. auto TI = getDynamicTypeInfo(State, Reg);
  681. if (!TI.isValid())
  682. return nullptr;
  683. auto Type = TI.getType();
  684. if (const auto *RefT = Type->getAs<ReferenceType>()) {
  685. Type = RefT->getPointeeType();
  686. }
  687. return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
  688. }
  689. bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
  690. const auto *CRD = getCXXRecordDecl(State, Reg);
  691. if (!CRD)
  692. return false;
  693. for (const auto *Method : CRD->methods()) {
  694. if (!Method->isOverloadedOperator())
  695. continue;
  696. const auto OPK = Method->getOverloadedOperator();
  697. if (OPK == OO_Subscript) {
  698. return true;
  699. }
  700. }
  701. return false;
  702. }
  703. bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
  704. const auto *CRD = getCXXRecordDecl(State, Reg);
  705. if (!CRD)
  706. return false;
  707. for (const auto *Method : CRD->methods()) {
  708. if (!Method->getDeclName().isIdentifier())
  709. continue;
  710. if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
  711. return true;
  712. }
  713. }
  714. return false;
  715. }
  716. bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
  717. const auto *CRD = getCXXRecordDecl(State, Reg);
  718. if (!CRD)
  719. return false;
  720. for (const auto *Method : CRD->methods()) {
  721. if (!Method->getDeclName().isIdentifier())
  722. continue;
  723. if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
  724. return true;
  725. }
  726. }
  727. return false;
  728. }
  729. SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
  730. const auto *CDataPtr = getContainerData(State, Cont);
  731. if (!CDataPtr)
  732. return nullptr;
  733. return CDataPtr->getBegin();
  734. }
  735. SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
  736. const auto *CDataPtr = getContainerData(State, Cont);
  737. if (!CDataPtr)
  738. return nullptr;
  739. return CDataPtr->getEnd();
  740. }
  741. ProgramStateRef createContainerBegin(ProgramStateRef State,
  742. const MemRegion *Cont, const Expr *E,
  743. QualType T, const LocationContext *LCtx,
  744. unsigned BlockCount) {
  745. // Only create if it does not exist
  746. const auto *CDataPtr = getContainerData(State, Cont);
  747. if (CDataPtr && CDataPtr->getBegin())
  748. return State;
  749. auto &SymMgr = State->getSymbolManager();
  750. const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
  751. "begin");
  752. State = assumeNoOverflow(State, Sym, 4);
  753. if (CDataPtr) {
  754. const auto CData = CDataPtr->newBegin(Sym);
  755. return setContainerData(State, Cont, CData);
  756. }
  757. const auto CData = ContainerData::fromBegin(Sym);
  758. return setContainerData(State, Cont, CData);
  759. }
  760. ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
  761. const Expr *E, QualType T,
  762. const LocationContext *LCtx,
  763. unsigned BlockCount) {
  764. // Only create if it does not exist
  765. const auto *CDataPtr = getContainerData(State, Cont);
  766. if (CDataPtr && CDataPtr->getEnd())
  767. return State;
  768. auto &SymMgr = State->getSymbolManager();
  769. const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
  770. "end");
  771. State = assumeNoOverflow(State, Sym, 4);
  772. if (CDataPtr) {
  773. const auto CData = CDataPtr->newEnd(Sym);
  774. return setContainerData(State, Cont, CData);
  775. }
  776. const auto CData = ContainerData::fromEnd(Sym);
  777. return setContainerData(State, Cont, CData);
  778. }
  779. ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
  780. const ContainerData &CData) {
  781. return State->set<ContainerMap>(Cont, CData);
  782. }
  783. template <typename Condition, typename Process>
  784. ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
  785. Process Proc) {
  786. auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
  787. auto RegionMap = State->get<IteratorRegionMap>();
  788. bool Changed = false;
  789. for (const auto &Reg : RegionMap) {
  790. if (Cond(Reg.second)) {
  791. RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
  792. Changed = true;
  793. }
  794. }
  795. if (Changed)
  796. State = State->set<IteratorRegionMap>(RegionMap);
  797. auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
  798. auto SymbolMap = State->get<IteratorSymbolMap>();
  799. Changed = false;
  800. for (const auto &Sym : SymbolMap) {
  801. if (Cond(Sym.second)) {
  802. SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
  803. Changed = true;
  804. }
  805. }
  806. if (Changed)
  807. State = State->set<IteratorSymbolMap>(SymbolMap);
  808. return State;
  809. }
  810. ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
  811. const MemRegion *Cont) {
  812. auto MatchCont = [&](const IteratorPosition &Pos) {
  813. return Pos.getContainer() == Cont;
  814. };
  815. auto Invalidate = [&](const IteratorPosition &Pos) {
  816. return Pos.invalidate();
  817. };
  818. return processIteratorPositions(State, MatchCont, Invalidate);
  819. }
  820. ProgramStateRef
  821. invalidateAllIteratorPositionsExcept(ProgramStateRef State,
  822. const MemRegion *Cont, SymbolRef Offset,
  823. BinaryOperator::Opcode Opc) {
  824. auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
  825. return Pos.getContainer() == Cont &&
  826. !compare(State, Pos.getOffset(), Offset, Opc);
  827. };
  828. auto Invalidate = [&](const IteratorPosition &Pos) {
  829. return Pos.invalidate();
  830. };
  831. return processIteratorPositions(State, MatchContAndCompare, Invalidate);
  832. }
  833. ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
  834. SymbolRef Offset,
  835. BinaryOperator::Opcode Opc) {
  836. auto Compare = [&](const IteratorPosition &Pos) {
  837. return compare(State, Pos.getOffset(), Offset, Opc);
  838. };
  839. auto Invalidate = [&](const IteratorPosition &Pos) {
  840. return Pos.invalidate();
  841. };
  842. return processIteratorPositions(State, Compare, Invalidate);
  843. }
  844. ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
  845. SymbolRef Offset1,
  846. BinaryOperator::Opcode Opc1,
  847. SymbolRef Offset2,
  848. BinaryOperator::Opcode Opc2) {
  849. auto Compare = [&](const IteratorPosition &Pos) {
  850. return compare(State, Pos.getOffset(), Offset1, Opc1) &&
  851. compare(State, Pos.getOffset(), Offset2, Opc2);
  852. };
  853. auto Invalidate = [&](const IteratorPosition &Pos) {
  854. return Pos.invalidate();
  855. };
  856. return processIteratorPositions(State, Compare, Invalidate);
  857. }
  858. ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
  859. const MemRegion *Cont,
  860. const MemRegion *NewCont) {
  861. auto MatchCont = [&](const IteratorPosition &Pos) {
  862. return Pos.getContainer() == Cont;
  863. };
  864. auto ReAssign = [&](const IteratorPosition &Pos) {
  865. return Pos.reAssign(NewCont);
  866. };
  867. return processIteratorPositions(State, MatchCont, ReAssign);
  868. }
  869. ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
  870. const MemRegion *Cont,
  871. const MemRegion *NewCont,
  872. SymbolRef Offset,
  873. BinaryOperator::Opcode Opc) {
  874. auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
  875. return Pos.getContainer() == Cont &&
  876. !compare(State, Pos.getOffset(), Offset, Opc);
  877. };
  878. auto ReAssign = [&](const IteratorPosition &Pos) {
  879. return Pos.reAssign(NewCont);
  880. };
  881. return processIteratorPositions(State, MatchContAndCompare, ReAssign);
  882. }
  883. // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
  884. // `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
  885. // position offsets where `CondSym` is true.
  886. ProgramStateRef rebaseSymbolInIteratorPositionsIf(
  887. ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
  888. SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
  889. auto LessThanEnd = [&](const IteratorPosition &Pos) {
  890. return compare(State, Pos.getOffset(), CondSym, Opc);
  891. };
  892. auto RebaseSymbol = [&](const IteratorPosition &Pos) {
  893. return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
  894. NewSym));
  895. };
  896. return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
  897. }
  898. // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
  899. // `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
  900. // `OrigExpr`.
  901. SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
  902. SymbolRef OrigExpr, SymbolRef OldExpr,
  903. SymbolRef NewSym) {
  904. auto &SymMgr = SVB.getSymbolManager();
  905. auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
  906. nonloc::SymbolVal(OldExpr),
  907. SymMgr.getType(OrigExpr));
  908. const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
  909. if (!DiffInt)
  910. return OrigExpr;
  911. return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
  912. SymMgr.getType(OrigExpr)).getAsSymbol();
  913. }
  914. bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
  915. auto RegionMap = State->get<IteratorRegionMap>();
  916. for (const auto &Reg : RegionMap) {
  917. if (Reg.second.getContainer() == Cont)
  918. return true;
  919. }
  920. auto SymbolMap = State->get<IteratorSymbolMap>();
  921. for (const auto &Sym : SymbolMap) {
  922. if (Sym.second.getContainer() == Cont)
  923. return true;
  924. }
  925. return false;
  926. }
  927. } // namespace
  928. void ento::registerContainerModeling(CheckerManager &mgr) {
  929. mgr.registerChecker<ContainerModeling>();
  930. }
  931. bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) {
  932. if (!mgr.getLangOpts().CPlusPlus)
  933. return false;
  934. if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) {
  935. mgr.getASTContext().getDiagnostics().Report(
  936. diag::err_analyzer_checker_incompatible_analyzer_option)
  937. << "aggressive-binary-operation-simplification" << "false";
  938. return false;
  939. }
  940. return true;
  941. }