1480093f4SDimitry Andric //===-- MismatchedIteratorChecker.cpp -----------------------------*- C++ -*--// 2480093f4SDimitry Andric // 3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6480093f4SDimitry Andric // 7480093f4SDimitry Andric //===----------------------------------------------------------------------===// 8480093f4SDimitry Andric // 9480093f4SDimitry Andric // Defines a checker for mistakenly applying a foreign iterator on a container 10480093f4SDimitry Andric // and for using iterators of two different containers in a context where 11480093f4SDimitry Andric // iterators of the same container should be used. 12480093f4SDimitry Andric // 13480093f4SDimitry Andric //===----------------------------------------------------------------------===// 14480093f4SDimitry Andric 15480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 16480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 18480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 19480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20480093f4SDimitry Andric 21480093f4SDimitry Andric 22480093f4SDimitry Andric #include "Iterator.h" 23480093f4SDimitry Andric 24480093f4SDimitry Andric using namespace clang; 25480093f4SDimitry Andric using namespace ento; 26480093f4SDimitry Andric using namespace iterator; 27480093f4SDimitry Andric 28480093f4SDimitry Andric namespace { 29480093f4SDimitry Andric 30480093f4SDimitry Andric class MismatchedIteratorChecker 315ffd83dbSDimitry Andric : public Checker<check::PreCall, check::PreStmt<BinaryOperator>> { 32480093f4SDimitry Andric 33480093f4SDimitry Andric std::unique_ptr<BugType> MismatchedBugType; 34480093f4SDimitry Andric 35480093f4SDimitry Andric void verifyMatch(CheckerContext &C, const SVal &Iter, 36480093f4SDimitry Andric const MemRegion *Cont) const; 37480093f4SDimitry Andric void verifyMatch(CheckerContext &C, const SVal &Iter1, 38480093f4SDimitry Andric const SVal &Iter2) const; 39480093f4SDimitry Andric void reportBug(const StringRef &Message, const SVal &Val1, 40480093f4SDimitry Andric const SVal &Val2, CheckerContext &C, 41480093f4SDimitry Andric ExplodedNode *ErrNode) const; 42480093f4SDimitry Andric void reportBug(const StringRef &Message, const SVal &Val, 43480093f4SDimitry Andric const MemRegion *Reg, CheckerContext &C, 44480093f4SDimitry Andric ExplodedNode *ErrNode) const; 45480093f4SDimitry Andric 46480093f4SDimitry Andric public: 47480093f4SDimitry Andric MismatchedIteratorChecker(); 48480093f4SDimitry Andric 49480093f4SDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 505ffd83dbSDimitry Andric void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const; 51480093f4SDimitry Andric 52480093f4SDimitry Andric }; 53480093f4SDimitry Andric 54480093f4SDimitry Andric } // namespace 55480093f4SDimitry Andric 56480093f4SDimitry Andric MismatchedIteratorChecker::MismatchedIteratorChecker() { 57480093f4SDimitry Andric MismatchedBugType.reset( 58480093f4SDimitry Andric new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs", 59480093f4SDimitry Andric /*SuppressOnSink=*/true)); 60480093f4SDimitry Andric } 61480093f4SDimitry Andric 62480093f4SDimitry Andric void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call, 63480093f4SDimitry Andric CheckerContext &C) const { 64480093f4SDimitry Andric // Check for iterator mismatches 65480093f4SDimitry Andric const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 66480093f4SDimitry Andric if (!Func) 67480093f4SDimitry Andric return; 68480093f4SDimitry Andric 69480093f4SDimitry Andric if (Func->isOverloadedOperator() && 70480093f4SDimitry Andric isComparisonOperator(Func->getOverloadedOperator())) { 71480093f4SDimitry Andric // Check for comparisons of iterators of different containers 72480093f4SDimitry Andric if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 73480093f4SDimitry Andric if (Call.getNumArgs() < 1) 74480093f4SDimitry Andric return; 75480093f4SDimitry Andric 76480093f4SDimitry Andric if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) || 77480093f4SDimitry Andric !isIteratorType(Call.getArgExpr(0)->getType())) 78480093f4SDimitry Andric return; 79480093f4SDimitry Andric 80480093f4SDimitry Andric verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0)); 81480093f4SDimitry Andric } else { 82480093f4SDimitry Andric if (Call.getNumArgs() < 2) 83480093f4SDimitry Andric return; 84480093f4SDimitry Andric 85480093f4SDimitry Andric if (!isIteratorType(Call.getArgExpr(0)->getType()) || 86480093f4SDimitry Andric !isIteratorType(Call.getArgExpr(1)->getType())) 87480093f4SDimitry Andric return; 88480093f4SDimitry Andric 89480093f4SDimitry Andric verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1)); 90480093f4SDimitry Andric } 91480093f4SDimitry Andric } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { 92480093f4SDimitry Andric const auto *ContReg = InstCall->getCXXThisVal().getAsRegion(); 93480093f4SDimitry Andric if (!ContReg) 94480093f4SDimitry Andric return; 95480093f4SDimitry Andric // Check for erase, insert and emplace using iterator of another container 96480093f4SDimitry Andric if (isEraseCall(Func) || isEraseAfterCall(Func)) { 97480093f4SDimitry Andric verifyMatch(C, Call.getArgSVal(0), 98480093f4SDimitry Andric InstCall->getCXXThisVal().getAsRegion()); 99480093f4SDimitry Andric if (Call.getNumArgs() == 2) { 100480093f4SDimitry Andric verifyMatch(C, Call.getArgSVal(1), 101480093f4SDimitry Andric InstCall->getCXXThisVal().getAsRegion()); 102480093f4SDimitry Andric } 103480093f4SDimitry Andric } else if (isInsertCall(Func)) { 104480093f4SDimitry Andric verifyMatch(C, Call.getArgSVal(0), 105480093f4SDimitry Andric InstCall->getCXXThisVal().getAsRegion()); 106480093f4SDimitry Andric if (Call.getNumArgs() == 3 && 107480093f4SDimitry Andric isIteratorType(Call.getArgExpr(1)->getType()) && 108480093f4SDimitry Andric isIteratorType(Call.getArgExpr(2)->getType())) { 109480093f4SDimitry Andric verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2)); 110480093f4SDimitry Andric } 111480093f4SDimitry Andric } else if (isEmplaceCall(Func)) { 112480093f4SDimitry Andric verifyMatch(C, Call.getArgSVal(0), 113480093f4SDimitry Andric InstCall->getCXXThisVal().getAsRegion()); 114480093f4SDimitry Andric } 115480093f4SDimitry Andric } else if (isa<CXXConstructorCall>(&Call)) { 116480093f4SDimitry Andric // Check match of first-last iterator pair in a constructor of a container 117480093f4SDimitry Andric if (Call.getNumArgs() < 2) 118480093f4SDimitry Andric return; 119480093f4SDimitry Andric 120480093f4SDimitry Andric const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl()); 121480093f4SDimitry Andric if (Ctr->getNumParams() < 2) 122480093f4SDimitry Andric return; 123480093f4SDimitry Andric 124480093f4SDimitry Andric if (Ctr->getParamDecl(0)->getName() != "first" || 125480093f4SDimitry Andric Ctr->getParamDecl(1)->getName() != "last") 126480093f4SDimitry Andric return; 127480093f4SDimitry Andric 128480093f4SDimitry Andric if (!isIteratorType(Call.getArgExpr(0)->getType()) || 129480093f4SDimitry Andric !isIteratorType(Call.getArgExpr(1)->getType())) 130480093f4SDimitry Andric return; 131480093f4SDimitry Andric 132480093f4SDimitry Andric verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1)); 133480093f4SDimitry Andric } else { 134480093f4SDimitry Andric // The main purpose of iterators is to abstract away from different 135480093f4SDimitry Andric // containers and provide a (maybe limited) uniform access to them. 136480093f4SDimitry Andric // This implies that any correctly written template function that 137480093f4SDimitry Andric // works on multiple containers using iterators takes different 138480093f4SDimitry Andric // template parameters for different containers. So we can safely 139480093f4SDimitry Andric // assume that passing iterators of different containers as arguments 140480093f4SDimitry Andric // whose type replaces the same template parameter is a bug. 141480093f4SDimitry Andric // 142480093f4SDimitry Andric // Example: 143480093f4SDimitry Andric // template<typename I1, typename I2> 144480093f4SDimitry Andric // void f(I1 first1, I1 last1, I2 first2, I2 last2); 145480093f4SDimitry Andric // 146480093f4SDimitry Andric // In this case the first two arguments to f() must be iterators must belong 147480093f4SDimitry Andric // to the same container and the last to also to the same container but 148480093f4SDimitry Andric // not necessarily to the same as the first two. 149480093f4SDimitry Andric 150480093f4SDimitry Andric const auto *Templ = Func->getPrimaryTemplate(); 151480093f4SDimitry Andric if (!Templ) 152480093f4SDimitry Andric return; 153480093f4SDimitry Andric 154480093f4SDimitry Andric const auto *TParams = Templ->getTemplateParameters(); 155480093f4SDimitry Andric const auto *TArgs = Func->getTemplateSpecializationArgs(); 156480093f4SDimitry Andric 157480093f4SDimitry Andric // Iterate over all the template parameters 158480093f4SDimitry Andric for (size_t I = 0; I < TParams->size(); ++I) { 159480093f4SDimitry Andric const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I)); 160480093f4SDimitry Andric if (!TPDecl) 161480093f4SDimitry Andric continue; 162480093f4SDimitry Andric 163480093f4SDimitry Andric if (TPDecl->isParameterPack()) 164480093f4SDimitry Andric continue; 165480093f4SDimitry Andric 166480093f4SDimitry Andric const auto TAType = TArgs->get(I).getAsType(); 167480093f4SDimitry Andric if (!isIteratorType(TAType)) 168480093f4SDimitry Andric continue; 169480093f4SDimitry Andric 170480093f4SDimitry Andric SVal LHS = UndefinedVal(); 171480093f4SDimitry Andric 172480093f4SDimitry Andric // For every template parameter which is an iterator type in the 173480093f4SDimitry Andric // instantiation look for all functions' parameters' type by it and 174480093f4SDimitry Andric // check whether they belong to the same container 175480093f4SDimitry Andric for (auto J = 0U; J < Func->getNumParams(); ++J) { 176480093f4SDimitry Andric const auto *Param = Func->getParamDecl(J); 177480093f4SDimitry Andric const auto *ParamType = 178480093f4SDimitry Andric Param->getType()->getAs<SubstTemplateTypeParmType>(); 179*bdd1243dSDimitry Andric if (!ParamType) 180*bdd1243dSDimitry Andric continue; 181*bdd1243dSDimitry Andric const TemplateTypeParmDecl *D = ParamType->getReplacedParameter(); 182*bdd1243dSDimitry Andric if (D != TPDecl) 183480093f4SDimitry Andric continue; 184480093f4SDimitry Andric if (LHS.isUndef()) { 185480093f4SDimitry Andric LHS = Call.getArgSVal(J); 186480093f4SDimitry Andric } else { 187480093f4SDimitry Andric verifyMatch(C, LHS, Call.getArgSVal(J)); 188480093f4SDimitry Andric } 189480093f4SDimitry Andric } 190480093f4SDimitry Andric } 191480093f4SDimitry Andric } 192480093f4SDimitry Andric } 193480093f4SDimitry Andric 1945ffd83dbSDimitry Andric void MismatchedIteratorChecker::checkPreStmt(const BinaryOperator *BO, 1955ffd83dbSDimitry Andric CheckerContext &C) const { 1965ffd83dbSDimitry Andric if (!BO->isComparisonOp()) 1975ffd83dbSDimitry Andric return; 1985ffd83dbSDimitry Andric 1995ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 2005ffd83dbSDimitry Andric SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext()); 2015ffd83dbSDimitry Andric SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext()); 2025ffd83dbSDimitry Andric verifyMatch(C, LVal, RVal); 2035ffd83dbSDimitry Andric } 2045ffd83dbSDimitry Andric 205480093f4SDimitry Andric void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter, 206480093f4SDimitry Andric const MemRegion *Cont) const { 207480093f4SDimitry Andric // Verify match between a container and the container of an iterator 208480093f4SDimitry Andric Cont = Cont->getMostDerivedObjectRegion(); 209480093f4SDimitry Andric 210480093f4SDimitry Andric if (const auto *ContSym = Cont->getSymbolicBase()) { 211480093f4SDimitry Andric if (isa<SymbolConjured>(ContSym->getSymbol())) 212480093f4SDimitry Andric return; 213480093f4SDimitry Andric } 214480093f4SDimitry Andric 215480093f4SDimitry Andric auto State = C.getState(); 216480093f4SDimitry Andric const auto *Pos = getIteratorPosition(State, Iter); 217480093f4SDimitry Andric if (!Pos) 218480093f4SDimitry Andric return; 219480093f4SDimitry Andric 220480093f4SDimitry Andric const auto *IterCont = Pos->getContainer(); 221480093f4SDimitry Andric 222480093f4SDimitry Andric // Skip symbolic regions based on conjured symbols. Two conjured symbols 223480093f4SDimitry Andric // may or may not be the same. For example, the same function can return 224480093f4SDimitry Andric // the same or a different container but we get different conjured symbols 225480093f4SDimitry Andric // for each call. This may cause false positives so omit them from the check. 226480093f4SDimitry Andric if (const auto *ContSym = IterCont->getSymbolicBase()) { 227480093f4SDimitry Andric if (isa<SymbolConjured>(ContSym->getSymbol())) 228480093f4SDimitry Andric return; 229480093f4SDimitry Andric } 230480093f4SDimitry Andric 231480093f4SDimitry Andric if (IterCont != Cont) { 232480093f4SDimitry Andric auto *N = C.generateNonFatalErrorNode(State); 233480093f4SDimitry Andric if (!N) { 234480093f4SDimitry Andric return; 235480093f4SDimitry Andric } 236480093f4SDimitry Andric reportBug("Container accessed using foreign iterator argument.", 237480093f4SDimitry Andric Iter, Cont, C, N); 238480093f4SDimitry Andric } 239480093f4SDimitry Andric } 240480093f4SDimitry Andric 241480093f4SDimitry Andric void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, 242480093f4SDimitry Andric const SVal &Iter1, 243480093f4SDimitry Andric const SVal &Iter2) const { 244480093f4SDimitry Andric // Verify match between the containers of two iterators 245480093f4SDimitry Andric auto State = C.getState(); 246480093f4SDimitry Andric const auto *Pos1 = getIteratorPosition(State, Iter1); 247480093f4SDimitry Andric if (!Pos1) 248480093f4SDimitry Andric return; 249480093f4SDimitry Andric 250480093f4SDimitry Andric const auto *IterCont1 = Pos1->getContainer(); 251480093f4SDimitry Andric 252480093f4SDimitry Andric // Skip symbolic regions based on conjured symbols. Two conjured symbols 253480093f4SDimitry Andric // may or may not be the same. For example, the same function can return 254480093f4SDimitry Andric // the same or a different container but we get different conjured symbols 255480093f4SDimitry Andric // for each call. This may cause false positives so omit them from the check. 256480093f4SDimitry Andric if (const auto *ContSym = IterCont1->getSymbolicBase()) { 257480093f4SDimitry Andric if (isa<SymbolConjured>(ContSym->getSymbol())) 258480093f4SDimitry Andric return; 259480093f4SDimitry Andric } 260480093f4SDimitry Andric 261480093f4SDimitry Andric const auto *Pos2 = getIteratorPosition(State, Iter2); 262480093f4SDimitry Andric if (!Pos2) 263480093f4SDimitry Andric return; 264480093f4SDimitry Andric 265480093f4SDimitry Andric const auto *IterCont2 = Pos2->getContainer(); 266480093f4SDimitry Andric if (const auto *ContSym = IterCont2->getSymbolicBase()) { 267480093f4SDimitry Andric if (isa<SymbolConjured>(ContSym->getSymbol())) 268480093f4SDimitry Andric return; 269480093f4SDimitry Andric } 270480093f4SDimitry Andric 271480093f4SDimitry Andric if (IterCont1 != IterCont2) { 272480093f4SDimitry Andric auto *N = C.generateNonFatalErrorNode(State); 273480093f4SDimitry Andric if (!N) 274480093f4SDimitry Andric return; 275480093f4SDimitry Andric reportBug("Iterators of different containers used where the " 276480093f4SDimitry Andric "same container is expected.", Iter1, Iter2, C, N); 277480093f4SDimitry Andric } 278480093f4SDimitry Andric } 279480093f4SDimitry Andric 280480093f4SDimitry Andric void MismatchedIteratorChecker::reportBug(const StringRef &Message, 281480093f4SDimitry Andric const SVal &Val1, 282480093f4SDimitry Andric const SVal &Val2, 283480093f4SDimitry Andric CheckerContext &C, 284480093f4SDimitry Andric ExplodedNode *ErrNode) const { 285480093f4SDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message, 286480093f4SDimitry Andric ErrNode); 287480093f4SDimitry Andric R->markInteresting(Val1); 288480093f4SDimitry Andric R->markInteresting(Val2); 289480093f4SDimitry Andric C.emitReport(std::move(R)); 290480093f4SDimitry Andric } 291480093f4SDimitry Andric 292480093f4SDimitry Andric void MismatchedIteratorChecker::reportBug(const StringRef &Message, 293480093f4SDimitry Andric const SVal &Val, const MemRegion *Reg, 294480093f4SDimitry Andric CheckerContext &C, 295480093f4SDimitry Andric ExplodedNode *ErrNode) const { 296480093f4SDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message, 297480093f4SDimitry Andric ErrNode); 298480093f4SDimitry Andric R->markInteresting(Val); 299480093f4SDimitry Andric R->markInteresting(Reg); 300480093f4SDimitry Andric C.emitReport(std::move(R)); 301480093f4SDimitry Andric } 302480093f4SDimitry Andric 303480093f4SDimitry Andric void ento::registerMismatchedIteratorChecker(CheckerManager &mgr) { 304480093f4SDimitry Andric mgr.registerChecker<MismatchedIteratorChecker>(); 305480093f4SDimitry Andric } 306480093f4SDimitry Andric 3075ffd83dbSDimitry Andric bool ento::shouldRegisterMismatchedIteratorChecker(const CheckerManager &mgr) { 308480093f4SDimitry Andric return true; 309480093f4SDimitry Andric } 310