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