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