1*b198f16eSAdam Balogh //===-- STLAlgorithmModeling.cpp -----------------------------------*- C++ -*--// 2*b198f16eSAdam Balogh // 3*b198f16eSAdam Balogh // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*b198f16eSAdam Balogh // See https://llvm.org/LICENSE.txt for license information. 5*b198f16eSAdam Balogh // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*b198f16eSAdam Balogh // 7*b198f16eSAdam Balogh //===----------------------------------------------------------------------===// 8*b198f16eSAdam Balogh // 9*b198f16eSAdam Balogh // Models STL algorithms. 10*b198f16eSAdam Balogh // 11*b198f16eSAdam Balogh //===----------------------------------------------------------------------===// 12*b198f16eSAdam Balogh 13*b198f16eSAdam Balogh #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 14*b198f16eSAdam Balogh #include "clang/StaticAnalyzer/Core/Checker.h" 15*b198f16eSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 16*b198f16eSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 17*b198f16eSAdam Balogh 18*b198f16eSAdam Balogh #include "Iterator.h" 19*b198f16eSAdam Balogh 20*b198f16eSAdam Balogh using namespace clang; 21*b198f16eSAdam Balogh using namespace ento; 22*b198f16eSAdam Balogh using namespace iterator; 23*b198f16eSAdam Balogh 24*b198f16eSAdam Balogh namespace { 25*b198f16eSAdam Balogh 26*b198f16eSAdam Balogh class STLAlgorithmModeling : public Checker<eval::Call> { 27*b198f16eSAdam Balogh bool evalFind(CheckerContext &C, const CallExpr *CE) const; 28*b198f16eSAdam Balogh 29*b198f16eSAdam Balogh void Find(CheckerContext &C, const CallExpr *CE, unsigned paramNum) const; 30*b198f16eSAdam Balogh 31*b198f16eSAdam Balogh using FnCheck = bool (STLAlgorithmModeling::*)(CheckerContext &, 32*b198f16eSAdam Balogh const CallExpr *) const; 33*b198f16eSAdam Balogh 34*b198f16eSAdam Balogh const CallDescriptionMap<FnCheck> Callbacks = { 35*b198f16eSAdam Balogh {{{"std", "find"}, 3}, &STLAlgorithmModeling::evalFind}, 36*b198f16eSAdam Balogh {{{"std", "find"}, 4}, &STLAlgorithmModeling::evalFind}, 37*b198f16eSAdam Balogh {{{"std", "find_if"}, 3}, &STLAlgorithmModeling::evalFind}, 38*b198f16eSAdam Balogh {{{"std", "find_if"}, 4}, &STLAlgorithmModeling::evalFind}, 39*b198f16eSAdam Balogh {{{"std", "find_if_not"}, 3}, &STLAlgorithmModeling::evalFind}, 40*b198f16eSAdam Balogh {{{"std", "find_if_not"}, 4}, &STLAlgorithmModeling::evalFind}, 41*b198f16eSAdam Balogh {{{"std", "find_first_of"}, 4}, &STLAlgorithmModeling::evalFind}, 42*b198f16eSAdam Balogh {{{"std", "find_first_of"}, 5}, &STLAlgorithmModeling::evalFind}, 43*b198f16eSAdam Balogh {{{"std", "find_first_of"}, 6}, &STLAlgorithmModeling::evalFind}, 44*b198f16eSAdam Balogh {{{"std", "find_end"}, 4}, &STLAlgorithmModeling::evalFind}, 45*b198f16eSAdam Balogh {{{"std", "find_end"}, 5}, &STLAlgorithmModeling::evalFind}, 46*b198f16eSAdam Balogh {{{"std", "find_end"}, 6}, &STLAlgorithmModeling::evalFind}, 47*b198f16eSAdam Balogh {{{"std", "lower_bound"}, 3}, &STLAlgorithmModeling::evalFind}, 48*b198f16eSAdam Balogh {{{"std", "lower_bound"}, 4}, &STLAlgorithmModeling::evalFind}, 49*b198f16eSAdam Balogh {{{"std", "upper_bound"}, 3}, &STLAlgorithmModeling::evalFind}, 50*b198f16eSAdam Balogh {{{"std", "upper_bound"}, 4}, &STLAlgorithmModeling::evalFind}, 51*b198f16eSAdam Balogh {{{"std", "search"}, 3}, &STLAlgorithmModeling::evalFind}, 52*b198f16eSAdam Balogh {{{"std", "search"}, 4}, &STLAlgorithmModeling::evalFind}, 53*b198f16eSAdam Balogh {{{"std", "search"}, 5}, &STLAlgorithmModeling::evalFind}, 54*b198f16eSAdam Balogh {{{"std", "search"}, 6}, &STLAlgorithmModeling::evalFind}, 55*b198f16eSAdam Balogh {{{"std", "search_n"}, 4}, &STLAlgorithmModeling::evalFind}, 56*b198f16eSAdam Balogh {{{"std", "search_n"}, 5}, &STLAlgorithmModeling::evalFind}, 57*b198f16eSAdam Balogh {{{"std", "search_n"}, 6}, &STLAlgorithmModeling::evalFind}, 58*b198f16eSAdam Balogh }; 59*b198f16eSAdam Balogh 60*b198f16eSAdam Balogh public: 61*b198f16eSAdam Balogh STLAlgorithmModeling() = default; 62*b198f16eSAdam Balogh 63*b198f16eSAdam Balogh bool AggressiveStdFindModeling; 64*b198f16eSAdam Balogh 65*b198f16eSAdam Balogh bool evalCall(const CallEvent &Call, CheckerContext &C) const; 66*b198f16eSAdam Balogh }; // 67*b198f16eSAdam Balogh 68*b198f16eSAdam Balogh bool STLAlgorithmModeling::evalCall(const CallEvent &Call, 69*b198f16eSAdam Balogh CheckerContext &C) const { 70*b198f16eSAdam Balogh const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 71*b198f16eSAdam Balogh if (!CE) 72*b198f16eSAdam Balogh return false; 73*b198f16eSAdam Balogh 74*b198f16eSAdam Balogh const FnCheck *Handler = Callbacks.lookup(Call); 75*b198f16eSAdam Balogh if (!Handler) 76*b198f16eSAdam Balogh return false; 77*b198f16eSAdam Balogh 78*b198f16eSAdam Balogh return (this->**Handler)(C, CE); 79*b198f16eSAdam Balogh } 80*b198f16eSAdam Balogh 81*b198f16eSAdam Balogh bool STLAlgorithmModeling::evalFind(CheckerContext &C, 82*b198f16eSAdam Balogh const CallExpr *CE) const { 83*b198f16eSAdam Balogh // std::find()-like functions either take their primary range in the first 84*b198f16eSAdam Balogh // two parameters, or if the first parameter is "execution policy" then in 85*b198f16eSAdam Balogh // the second and third. This means that the second parameter must always be 86*b198f16eSAdam Balogh // an iterator. 87*b198f16eSAdam Balogh if (!isIteratorType(CE->getArg(1)->getType())) 88*b198f16eSAdam Balogh return false; 89*b198f16eSAdam Balogh 90*b198f16eSAdam Balogh // If no "execution policy" parameter is used then the first argument is the 91*b198f16eSAdam Balogh // beginning of the range. 92*b198f16eSAdam Balogh if (isIteratorType(CE->getArg(0)->getType())) { 93*b198f16eSAdam Balogh Find(C, CE, 0); 94*b198f16eSAdam Balogh return true; 95*b198f16eSAdam Balogh } 96*b198f16eSAdam Balogh 97*b198f16eSAdam Balogh // If "execution policy" parameter is used then the second argument is the 98*b198f16eSAdam Balogh // beginning of the range. 99*b198f16eSAdam Balogh if (isIteratorType(CE->getArg(2)->getType())) { 100*b198f16eSAdam Balogh Find(C, CE, 1); 101*b198f16eSAdam Balogh return true; 102*b198f16eSAdam Balogh } 103*b198f16eSAdam Balogh 104*b198f16eSAdam Balogh return false; 105*b198f16eSAdam Balogh } 106*b198f16eSAdam Balogh 107*b198f16eSAdam Balogh void STLAlgorithmModeling::Find(CheckerContext &C, const CallExpr *CE, 108*b198f16eSAdam Balogh unsigned paramNum) const { 109*b198f16eSAdam Balogh auto State = C.getState(); 110*b198f16eSAdam Balogh auto &SVB = C.getSValBuilder(); 111*b198f16eSAdam Balogh const auto *LCtx = C.getLocationContext(); 112*b198f16eSAdam Balogh 113*b198f16eSAdam Balogh SVal RetVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); 114*b198f16eSAdam Balogh SVal Param = State->getSVal(CE->getArg(paramNum), LCtx); 115*b198f16eSAdam Balogh 116*b198f16eSAdam Balogh auto StateFound = State->BindExpr(CE, LCtx, RetVal); 117*b198f16eSAdam Balogh 118*b198f16eSAdam Balogh // If we have an iterator position for the range-begin argument then we can 119*b198f16eSAdam Balogh // assume that in case of successful search the position of the found element 120*b198f16eSAdam Balogh // is not ahead of it. 121*b198f16eSAdam Balogh // FIXME: Reverse iterators 122*b198f16eSAdam Balogh const auto *Pos = getIteratorPosition(State, Param); 123*b198f16eSAdam Balogh if (Pos) { 124*b198f16eSAdam Balogh StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(), 125*b198f16eSAdam Balogh CE, LCtx, C.blockCount()); 126*b198f16eSAdam Balogh const auto *NewPos = getIteratorPosition(StateFound, RetVal); 127*b198f16eSAdam Balogh assert(NewPos && "Failed to create new iterator position."); 128*b198f16eSAdam Balogh 129*b198f16eSAdam Balogh SVal GreaterOrEqual = SVB.evalBinOp(StateFound, BO_GE, 130*b198f16eSAdam Balogh nonloc::SymbolVal(NewPos->getOffset()), 131*b198f16eSAdam Balogh nonloc::SymbolVal(Pos->getOffset()), 132*b198f16eSAdam Balogh SVB.getConditionType()); 133*b198f16eSAdam Balogh assert(GreaterOrEqual.getAs<DefinedSVal>() && 134*b198f16eSAdam Balogh "Symbol comparison must be a `DefinedSVal`"); 135*b198f16eSAdam Balogh StateFound = StateFound->assume(GreaterOrEqual.castAs<DefinedSVal>(), true); 136*b198f16eSAdam Balogh } 137*b198f16eSAdam Balogh 138*b198f16eSAdam Balogh Param = State->getSVal(CE->getArg(paramNum + 1), LCtx); 139*b198f16eSAdam Balogh 140*b198f16eSAdam Balogh // If we have an iterator position for the range-end argument then we can 141*b198f16eSAdam Balogh // assume that in case of successful search the position of the found element 142*b198f16eSAdam Balogh // is ahead of it. 143*b198f16eSAdam Balogh // FIXME: Reverse iterators 144*b198f16eSAdam Balogh Pos = getIteratorPosition(State, Param); 145*b198f16eSAdam Balogh if (Pos) { 146*b198f16eSAdam Balogh StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(), 147*b198f16eSAdam Balogh CE, LCtx, C.blockCount()); 148*b198f16eSAdam Balogh const auto *NewPos = getIteratorPosition(StateFound, RetVal); 149*b198f16eSAdam Balogh assert(NewPos && "Failed to create new iterator position."); 150*b198f16eSAdam Balogh 151*b198f16eSAdam Balogh SVal Less = SVB.evalBinOp(StateFound, BO_LT, 152*b198f16eSAdam Balogh nonloc::SymbolVal(NewPos->getOffset()), 153*b198f16eSAdam Balogh nonloc::SymbolVal(Pos->getOffset()), 154*b198f16eSAdam Balogh SVB.getConditionType()); 155*b198f16eSAdam Balogh assert(Less.getAs<DefinedSVal>() && 156*b198f16eSAdam Balogh "Symbol comparison must be a `DefinedSVal`"); 157*b198f16eSAdam Balogh StateFound = StateFound->assume(Less.castAs<DefinedSVal>(), true); 158*b198f16eSAdam Balogh } 159*b198f16eSAdam Balogh 160*b198f16eSAdam Balogh C.addTransition(StateFound); 161*b198f16eSAdam Balogh 162*b198f16eSAdam Balogh if (AggressiveStdFindModeling) { 163*b198f16eSAdam Balogh auto StateNotFound = State->BindExpr(CE, LCtx, Param); 164*b198f16eSAdam Balogh C.addTransition(StateNotFound); 165*b198f16eSAdam Balogh } 166*b198f16eSAdam Balogh } 167*b198f16eSAdam Balogh 168*b198f16eSAdam Balogh } // namespace 169*b198f16eSAdam Balogh 170*b198f16eSAdam Balogh void ento::registerSTLAlgorithmModeling(CheckerManager &Mgr) { 171*b198f16eSAdam Balogh auto *Checker = Mgr.registerChecker<STLAlgorithmModeling>(); 172*b198f16eSAdam Balogh Checker->AggressiveStdFindModeling = 173*b198f16eSAdam Balogh Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, 174*b198f16eSAdam Balogh "AggressiveStdFindModeling"); 175*b198f16eSAdam Balogh } 176*b198f16eSAdam Balogh 177*b198f16eSAdam Balogh bool ento::shouldRegisterSTLAlgorithmModeling(const LangOptions &LO) { 178*b198f16eSAdam Balogh return true; 179*b198f16eSAdam Balogh } 180*b198f16eSAdam Balogh 181