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