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