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