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 31*5ffd83dbSDimitry 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; 50*5ffd83dbSDimitry 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>(); 179480093f4SDimitry Andric if (!ParamType || 180480093f4SDimitry Andric ParamType->getReplacedParameter()->getDecl() != TPDecl) 181480093f4SDimitry Andric continue; 182480093f4SDimitry Andric if (LHS.isUndef()) { 183480093f4SDimitry Andric LHS = Call.getArgSVal(J); 184480093f4SDimitry Andric } else { 185480093f4SDimitry Andric verifyMatch(C, LHS, Call.getArgSVal(J)); 186480093f4SDimitry Andric } 187480093f4SDimitry Andric } 188480093f4SDimitry Andric } 189480093f4SDimitry Andric } 190480093f4SDimitry Andric } 191480093f4SDimitry Andric 192*5ffd83dbSDimitry Andric void MismatchedIteratorChecker::checkPreStmt(const BinaryOperator *BO, 193*5ffd83dbSDimitry Andric CheckerContext &C) const { 194*5ffd83dbSDimitry Andric if (!BO->isComparisonOp()) 195*5ffd83dbSDimitry Andric return; 196*5ffd83dbSDimitry Andric 197*5ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 198*5ffd83dbSDimitry Andric SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext()); 199*5ffd83dbSDimitry Andric SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext()); 200*5ffd83dbSDimitry Andric verifyMatch(C, LVal, RVal); 201*5ffd83dbSDimitry Andric } 202*5ffd83dbSDimitry Andric 203480093f4SDimitry Andric void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter, 204480093f4SDimitry Andric const MemRegion *Cont) const { 205480093f4SDimitry Andric // Verify match between a container and the container of an iterator 206480093f4SDimitry Andric Cont = Cont->getMostDerivedObjectRegion(); 207480093f4SDimitry Andric 208480093f4SDimitry Andric if (const auto *ContSym = Cont->getSymbolicBase()) { 209480093f4SDimitry Andric if (isa<SymbolConjured>(ContSym->getSymbol())) 210480093f4SDimitry Andric return; 211480093f4SDimitry Andric } 212480093f4SDimitry Andric 213480093f4SDimitry Andric auto State = C.getState(); 214480093f4SDimitry Andric const auto *Pos = getIteratorPosition(State, Iter); 215480093f4SDimitry Andric if (!Pos) 216480093f4SDimitry Andric return; 217480093f4SDimitry Andric 218480093f4SDimitry Andric const auto *IterCont = Pos->getContainer(); 219480093f4SDimitry Andric 220480093f4SDimitry Andric // Skip symbolic regions based on conjured symbols. Two conjured symbols 221480093f4SDimitry Andric // may or may not be the same. For example, the same function can return 222480093f4SDimitry Andric // the same or a different container but we get different conjured symbols 223480093f4SDimitry Andric // for each call. This may cause false positives so omit them from the check. 224480093f4SDimitry Andric if (const auto *ContSym = IterCont->getSymbolicBase()) { 225480093f4SDimitry Andric if (isa<SymbolConjured>(ContSym->getSymbol())) 226480093f4SDimitry Andric return; 227480093f4SDimitry Andric } 228480093f4SDimitry Andric 229480093f4SDimitry Andric if (IterCont != Cont) { 230480093f4SDimitry Andric auto *N = C.generateNonFatalErrorNode(State); 231480093f4SDimitry Andric if (!N) { 232480093f4SDimitry Andric return; 233480093f4SDimitry Andric } 234480093f4SDimitry Andric reportBug("Container accessed using foreign iterator argument.", 235480093f4SDimitry Andric Iter, Cont, C, N); 236480093f4SDimitry Andric } 237480093f4SDimitry Andric } 238480093f4SDimitry Andric 239480093f4SDimitry Andric void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, 240480093f4SDimitry Andric const SVal &Iter1, 241480093f4SDimitry Andric const SVal &Iter2) const { 242480093f4SDimitry Andric // Verify match between the containers of two iterators 243480093f4SDimitry Andric auto State = C.getState(); 244480093f4SDimitry Andric const auto *Pos1 = getIteratorPosition(State, Iter1); 245480093f4SDimitry Andric if (!Pos1) 246480093f4SDimitry Andric return; 247480093f4SDimitry Andric 248480093f4SDimitry Andric const auto *IterCont1 = Pos1->getContainer(); 249480093f4SDimitry Andric 250480093f4SDimitry Andric // Skip symbolic regions based on conjured symbols. Two conjured symbols 251480093f4SDimitry Andric // may or may not be the same. For example, the same function can return 252480093f4SDimitry Andric // the same or a different container but we get different conjured symbols 253480093f4SDimitry Andric // for each call. This may cause false positives so omit them from the check. 254480093f4SDimitry Andric if (const auto *ContSym = IterCont1->getSymbolicBase()) { 255480093f4SDimitry Andric if (isa<SymbolConjured>(ContSym->getSymbol())) 256480093f4SDimitry Andric return; 257480093f4SDimitry Andric } 258480093f4SDimitry Andric 259480093f4SDimitry Andric const auto *Pos2 = getIteratorPosition(State, Iter2); 260480093f4SDimitry Andric if (!Pos2) 261480093f4SDimitry Andric return; 262480093f4SDimitry Andric 263480093f4SDimitry Andric const auto *IterCont2 = Pos2->getContainer(); 264480093f4SDimitry Andric if (const auto *ContSym = IterCont2->getSymbolicBase()) { 265480093f4SDimitry Andric if (isa<SymbolConjured>(ContSym->getSymbol())) 266480093f4SDimitry Andric return; 267480093f4SDimitry Andric } 268480093f4SDimitry Andric 269480093f4SDimitry Andric if (IterCont1 != IterCont2) { 270480093f4SDimitry Andric auto *N = C.generateNonFatalErrorNode(State); 271480093f4SDimitry Andric if (!N) 272480093f4SDimitry Andric return; 273480093f4SDimitry Andric reportBug("Iterators of different containers used where the " 274480093f4SDimitry Andric "same container is expected.", Iter1, Iter2, C, N); 275480093f4SDimitry Andric } 276480093f4SDimitry Andric } 277480093f4SDimitry Andric 278480093f4SDimitry Andric void MismatchedIteratorChecker::reportBug(const StringRef &Message, 279480093f4SDimitry Andric const SVal &Val1, 280480093f4SDimitry Andric const SVal &Val2, 281480093f4SDimitry Andric CheckerContext &C, 282480093f4SDimitry Andric ExplodedNode *ErrNode) const { 283480093f4SDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message, 284480093f4SDimitry Andric ErrNode); 285480093f4SDimitry Andric R->markInteresting(Val1); 286480093f4SDimitry Andric R->markInteresting(Val2); 287480093f4SDimitry Andric C.emitReport(std::move(R)); 288480093f4SDimitry Andric } 289480093f4SDimitry Andric 290480093f4SDimitry Andric void MismatchedIteratorChecker::reportBug(const StringRef &Message, 291480093f4SDimitry Andric const SVal &Val, const MemRegion *Reg, 292480093f4SDimitry Andric CheckerContext &C, 293480093f4SDimitry Andric ExplodedNode *ErrNode) const { 294480093f4SDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message, 295480093f4SDimitry Andric ErrNode); 296480093f4SDimitry Andric R->markInteresting(Val); 297480093f4SDimitry Andric R->markInteresting(Reg); 298480093f4SDimitry Andric C.emitReport(std::move(R)); 299480093f4SDimitry Andric } 300480093f4SDimitry Andric 301480093f4SDimitry Andric void ento::registerMismatchedIteratorChecker(CheckerManager &mgr) { 302480093f4SDimitry Andric mgr.registerChecker<MismatchedIteratorChecker>(); 303480093f4SDimitry Andric } 304480093f4SDimitry Andric 305*5ffd83dbSDimitry Andric bool ento::shouldRegisterMismatchedIteratorChecker(const CheckerManager &mgr) { 306480093f4SDimitry Andric return true; 307480093f4SDimitry Andric } 308