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