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