1afb13afcSAdam Balogh //===-- MismatchedIteratorChecker.cpp -----------------------------*- C++ -*--//
2afb13afcSAdam Balogh //
3afb13afcSAdam Balogh // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4afb13afcSAdam Balogh // See https://llvm.org/LICENSE.txt for license information.
5afb13afcSAdam Balogh // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6afb13afcSAdam Balogh //
7afb13afcSAdam Balogh //===----------------------------------------------------------------------===//
8afb13afcSAdam Balogh //
9afb13afcSAdam Balogh // Defines a checker for mistakenly applying a foreign iterator on a container
10afb13afcSAdam Balogh // and for using iterators of two different containers in a context where
11afb13afcSAdam Balogh // iterators of the same container should be used.
12afb13afcSAdam Balogh //
13afb13afcSAdam Balogh //===----------------------------------------------------------------------===//
14afb13afcSAdam Balogh
15afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/Checker.h"
18afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20afb13afcSAdam Balogh
21afb13afcSAdam Balogh
22afb13afcSAdam Balogh #include "Iterator.h"
23afb13afcSAdam Balogh
24afb13afcSAdam Balogh using namespace clang;
25afb13afcSAdam Balogh using namespace ento;
26afb13afcSAdam Balogh using namespace iterator;
27afb13afcSAdam Balogh
28afb13afcSAdam Balogh namespace {
29afb13afcSAdam Balogh
30afb13afcSAdam Balogh class MismatchedIteratorChecker
319e63b190SAdam Balogh : public Checker<check::PreCall, check::PreStmt<BinaryOperator>> {
32afb13afcSAdam Balogh
3318f219c5SBalazs Benics const BugType MismatchedBugType{this, "Iterator(s) mismatched",
3418f219c5SBalazs Benics "Misuse of STL APIs",
3518f219c5SBalazs Benics /*SuppressOnSink=*/true};
36afb13afcSAdam Balogh
37*8ee3dfd7SBalazs Benics void verifyMatch(CheckerContext &C, SVal Iter, const MemRegion *Cont) const;
38*8ee3dfd7SBalazs Benics void verifyMatch(CheckerContext &C, SVal Iter1, SVal Iter2) const;
39*8ee3dfd7SBalazs Benics void reportBug(StringRef Message, SVal Val1, SVal Val2, CheckerContext &C,
40*8ee3dfd7SBalazs Benics ExplodedNode *ErrNode) const;
41*8ee3dfd7SBalazs Benics void reportBug(StringRef Message, SVal Val, const MemRegion *Reg,
427619050cSBalazs Benics CheckerContext &C, ExplodedNode *ErrNode) const;
43afb13afcSAdam Balogh
44afb13afcSAdam Balogh public:
45afb13afcSAdam Balogh void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
469e63b190SAdam Balogh void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
47afb13afcSAdam Balogh
48afb13afcSAdam Balogh };
49afb13afcSAdam Balogh
50afb13afcSAdam Balogh } // namespace
51afb13afcSAdam Balogh
checkPreCall(const CallEvent & Call,CheckerContext & C) const52afb13afcSAdam Balogh void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call,
53afb13afcSAdam Balogh CheckerContext &C) const {
54afb13afcSAdam Balogh // Check for iterator mismatches
55afb13afcSAdam Balogh const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
56afb13afcSAdam Balogh if (!Func)
57afb13afcSAdam Balogh return;
58afb13afcSAdam Balogh
59afb13afcSAdam Balogh if (Func->isOverloadedOperator() &&
60afb13afcSAdam Balogh isComparisonOperator(Func->getOverloadedOperator())) {
61afb13afcSAdam Balogh // Check for comparisons of iterators of different containers
62afb13afcSAdam Balogh if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
63afb13afcSAdam Balogh if (Call.getNumArgs() < 1)
64afb13afcSAdam Balogh return;
65afb13afcSAdam Balogh
66afb13afcSAdam Balogh if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
67afb13afcSAdam Balogh !isIteratorType(Call.getArgExpr(0)->getType()))
68afb13afcSAdam Balogh return;
69afb13afcSAdam Balogh
70afb13afcSAdam Balogh verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
71afb13afcSAdam Balogh } else {
72afb13afcSAdam Balogh if (Call.getNumArgs() < 2)
73afb13afcSAdam Balogh return;
74afb13afcSAdam Balogh
75afb13afcSAdam Balogh if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
76afb13afcSAdam Balogh !isIteratorType(Call.getArgExpr(1)->getType()))
77afb13afcSAdam Balogh return;
78afb13afcSAdam Balogh
79afb13afcSAdam Balogh verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
80afb13afcSAdam Balogh }
81afb13afcSAdam Balogh } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
82afb13afcSAdam Balogh const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
83afb13afcSAdam Balogh if (!ContReg)
84afb13afcSAdam Balogh return;
85afb13afcSAdam Balogh // Check for erase, insert and emplace using iterator of another container
86afb13afcSAdam Balogh if (isEraseCall(Func) || isEraseAfterCall(Func)) {
87afb13afcSAdam Balogh verifyMatch(C, Call.getArgSVal(0),
88afb13afcSAdam Balogh InstCall->getCXXThisVal().getAsRegion());
89afb13afcSAdam Balogh if (Call.getNumArgs() == 2) {
90afb13afcSAdam Balogh verifyMatch(C, Call.getArgSVal(1),
91afb13afcSAdam Balogh InstCall->getCXXThisVal().getAsRegion());
92afb13afcSAdam Balogh }
93afb13afcSAdam Balogh } else if (isInsertCall(Func)) {
94afb13afcSAdam Balogh verifyMatch(C, Call.getArgSVal(0),
95afb13afcSAdam Balogh InstCall->getCXXThisVal().getAsRegion());
96afb13afcSAdam Balogh if (Call.getNumArgs() == 3 &&
97afb13afcSAdam Balogh isIteratorType(Call.getArgExpr(1)->getType()) &&
98afb13afcSAdam Balogh isIteratorType(Call.getArgExpr(2)->getType())) {
99afb13afcSAdam Balogh verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
100afb13afcSAdam Balogh }
101afb13afcSAdam Balogh } else if (isEmplaceCall(Func)) {
102afb13afcSAdam Balogh verifyMatch(C, Call.getArgSVal(0),
103afb13afcSAdam Balogh InstCall->getCXXThisVal().getAsRegion());
104afb13afcSAdam Balogh }
105afb13afcSAdam Balogh } else if (isa<CXXConstructorCall>(&Call)) {
106afb13afcSAdam Balogh // Check match of first-last iterator pair in a constructor of a container
107afb13afcSAdam Balogh if (Call.getNumArgs() < 2)
108afb13afcSAdam Balogh return;
109afb13afcSAdam Balogh
110afb13afcSAdam Balogh const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
111afb13afcSAdam Balogh if (Ctr->getNumParams() < 2)
112afb13afcSAdam Balogh return;
113afb13afcSAdam Balogh
114afb13afcSAdam Balogh if (Ctr->getParamDecl(0)->getName() != "first" ||
115afb13afcSAdam Balogh Ctr->getParamDecl(1)->getName() != "last")
116afb13afcSAdam Balogh return;
117afb13afcSAdam Balogh
118afb13afcSAdam Balogh if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
119afb13afcSAdam Balogh !isIteratorType(Call.getArgExpr(1)->getType()))
120afb13afcSAdam Balogh return;
121afb13afcSAdam Balogh
122afb13afcSAdam Balogh verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
123afb13afcSAdam Balogh } else {
124afb13afcSAdam Balogh // The main purpose of iterators is to abstract away from different
125afb13afcSAdam Balogh // containers and provide a (maybe limited) uniform access to them.
126afb13afcSAdam Balogh // This implies that any correctly written template function that
127afb13afcSAdam Balogh // works on multiple containers using iterators takes different
128afb13afcSAdam Balogh // template parameters for different containers. So we can safely
129afb13afcSAdam Balogh // assume that passing iterators of different containers as arguments
130afb13afcSAdam Balogh // whose type replaces the same template parameter is a bug.
131afb13afcSAdam Balogh //
132afb13afcSAdam Balogh // Example:
133afb13afcSAdam Balogh // template<typename I1, typename I2>
134afb13afcSAdam Balogh // void f(I1 first1, I1 last1, I2 first2, I2 last2);
135afb13afcSAdam Balogh //
136afb13afcSAdam Balogh // In this case the first two arguments to f() must be iterators must belong
137afb13afcSAdam Balogh // to the same container and the last to also to the same container but
138afb13afcSAdam Balogh // not necessarily to the same as the first two.
139afb13afcSAdam Balogh
140afb13afcSAdam Balogh const auto *Templ = Func->getPrimaryTemplate();
141afb13afcSAdam Balogh if (!Templ)
142afb13afcSAdam Balogh return;
143afb13afcSAdam Balogh
144afb13afcSAdam Balogh const auto *TParams = Templ->getTemplateParameters();
145afb13afcSAdam Balogh const auto *TArgs = Func->getTemplateSpecializationArgs();
146afb13afcSAdam Balogh
147afb13afcSAdam Balogh // Iterate over all the template parameters
148afb13afcSAdam Balogh for (size_t I = 0; I < TParams->size(); ++I) {
149afb13afcSAdam Balogh const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
150afb13afcSAdam Balogh if (!TPDecl)
151afb13afcSAdam Balogh continue;
152afb13afcSAdam Balogh
153afb13afcSAdam Balogh if (TPDecl->isParameterPack())
154afb13afcSAdam Balogh continue;
155afb13afcSAdam Balogh
156afb13afcSAdam Balogh const auto TAType = TArgs->get(I).getAsType();
157afb13afcSAdam Balogh if (!isIteratorType(TAType))
158afb13afcSAdam Balogh continue;
159afb13afcSAdam Balogh
160afb13afcSAdam Balogh SVal LHS = UndefinedVal();
161afb13afcSAdam Balogh
162afb13afcSAdam Balogh // For every template parameter which is an iterator type in the
163afb13afcSAdam Balogh // instantiation look for all functions' parameters' type by it and
164afb13afcSAdam Balogh // check whether they belong to the same container
165afb13afcSAdam Balogh for (auto J = 0U; J < Func->getNumParams(); ++J) {
166afb13afcSAdam Balogh const auto *Param = Func->getParamDecl(J);
167afb13afcSAdam Balogh const auto *ParamType =
168afb13afcSAdam Balogh Param->getType()->getAs<SubstTemplateTypeParmType>();
169bcd9ba2bSMatheus Izvekov if (!ParamType)
170bcd9ba2bSMatheus Izvekov continue;
171bcd9ba2bSMatheus Izvekov const TemplateTypeParmDecl *D = ParamType->getReplacedParameter();
172bcd9ba2bSMatheus Izvekov if (D != TPDecl)
173afb13afcSAdam Balogh continue;
174afb13afcSAdam Balogh if (LHS.isUndef()) {
175afb13afcSAdam Balogh LHS = Call.getArgSVal(J);
176afb13afcSAdam Balogh } else {
177afb13afcSAdam Balogh verifyMatch(C, LHS, Call.getArgSVal(J));
178afb13afcSAdam Balogh }
179afb13afcSAdam Balogh }
180afb13afcSAdam Balogh }
181afb13afcSAdam Balogh }
182afb13afcSAdam Balogh }
183afb13afcSAdam Balogh
checkPreStmt(const BinaryOperator * BO,CheckerContext & C) const1849e63b190SAdam Balogh void MismatchedIteratorChecker::checkPreStmt(const BinaryOperator *BO,
1859e63b190SAdam Balogh CheckerContext &C) const {
1869e63b190SAdam Balogh if (!BO->isComparisonOp())
1879e63b190SAdam Balogh return;
1889e63b190SAdam Balogh
1899e63b190SAdam Balogh ProgramStateRef State = C.getState();
1909e63b190SAdam Balogh SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
1919e63b190SAdam Balogh SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
1929e63b190SAdam Balogh verifyMatch(C, LVal, RVal);
1939e63b190SAdam Balogh }
1949e63b190SAdam Balogh
verifyMatch(CheckerContext & C,SVal Iter,const MemRegion * Cont) const195*8ee3dfd7SBalazs Benics void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, SVal Iter,
196afb13afcSAdam Balogh const MemRegion *Cont) const {
197afb13afcSAdam Balogh // Verify match between a container and the container of an iterator
198afb13afcSAdam Balogh Cont = Cont->getMostDerivedObjectRegion();
199afb13afcSAdam Balogh
200afb13afcSAdam Balogh if (const auto *ContSym = Cont->getSymbolicBase()) {
201afb13afcSAdam Balogh if (isa<SymbolConjured>(ContSym->getSymbol()))
202afb13afcSAdam Balogh return;
203afb13afcSAdam Balogh }
204afb13afcSAdam Balogh
205afb13afcSAdam Balogh auto State = C.getState();
206afb13afcSAdam Balogh const auto *Pos = getIteratorPosition(State, Iter);
207afb13afcSAdam Balogh if (!Pos)
208afb13afcSAdam Balogh return;
209afb13afcSAdam Balogh
210afb13afcSAdam Balogh const auto *IterCont = Pos->getContainer();
211afb13afcSAdam Balogh
212afb13afcSAdam Balogh // Skip symbolic regions based on conjured symbols. Two conjured symbols
213afb13afcSAdam Balogh // may or may not be the same. For example, the same function can return
214afb13afcSAdam Balogh // the same or a different container but we get different conjured symbols
215afb13afcSAdam Balogh // for each call. This may cause false positives so omit them from the check.
216afb13afcSAdam Balogh if (const auto *ContSym = IterCont->getSymbolicBase()) {
217afb13afcSAdam Balogh if (isa<SymbolConjured>(ContSym->getSymbol()))
218afb13afcSAdam Balogh return;
219afb13afcSAdam Balogh }
220afb13afcSAdam Balogh
221afb13afcSAdam Balogh if (IterCont != Cont) {
222afb13afcSAdam Balogh auto *N = C.generateNonFatalErrorNode(State);
223afb13afcSAdam Balogh if (!N) {
224afb13afcSAdam Balogh return;
225afb13afcSAdam Balogh }
226afb13afcSAdam Balogh reportBug("Container accessed using foreign iterator argument.",
227afb13afcSAdam Balogh Iter, Cont, C, N);
228afb13afcSAdam Balogh }
229afb13afcSAdam Balogh }
230afb13afcSAdam Balogh
verifyMatch(CheckerContext & C,SVal Iter1,SVal Iter2) const231*8ee3dfd7SBalazs Benics void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, SVal Iter1,
232*8ee3dfd7SBalazs Benics SVal Iter2) const {
233afb13afcSAdam Balogh // Verify match between the containers of two iterators
234afb13afcSAdam Balogh auto State = C.getState();
235afb13afcSAdam Balogh const auto *Pos1 = getIteratorPosition(State, Iter1);
236afb13afcSAdam Balogh if (!Pos1)
237afb13afcSAdam Balogh return;
238afb13afcSAdam Balogh
239afb13afcSAdam Balogh const auto *IterCont1 = Pos1->getContainer();
240afb13afcSAdam Balogh
241afb13afcSAdam Balogh // Skip symbolic regions based on conjured symbols. Two conjured symbols
242afb13afcSAdam Balogh // may or may not be the same. For example, the same function can return
243afb13afcSAdam Balogh // the same or a different container but we get different conjured symbols
244afb13afcSAdam Balogh // for each call. This may cause false positives so omit them from the check.
245afb13afcSAdam Balogh if (const auto *ContSym = IterCont1->getSymbolicBase()) {
246afb13afcSAdam Balogh if (isa<SymbolConjured>(ContSym->getSymbol()))
247afb13afcSAdam Balogh return;
248afb13afcSAdam Balogh }
249afb13afcSAdam Balogh
250afb13afcSAdam Balogh const auto *Pos2 = getIteratorPosition(State, Iter2);
251afb13afcSAdam Balogh if (!Pos2)
252afb13afcSAdam Balogh return;
253afb13afcSAdam Balogh
254afb13afcSAdam Balogh const auto *IterCont2 = Pos2->getContainer();
255afb13afcSAdam Balogh if (const auto *ContSym = IterCont2->getSymbolicBase()) {
256afb13afcSAdam Balogh if (isa<SymbolConjured>(ContSym->getSymbol()))
257afb13afcSAdam Balogh return;
258afb13afcSAdam Balogh }
259afb13afcSAdam Balogh
260afb13afcSAdam Balogh if (IterCont1 != IterCont2) {
261afb13afcSAdam Balogh auto *N = C.generateNonFatalErrorNode(State);
262afb13afcSAdam Balogh if (!N)
263afb13afcSAdam Balogh return;
264afb13afcSAdam Balogh reportBug("Iterators of different containers used where the "
265afb13afcSAdam Balogh "same container is expected.", Iter1, Iter2, C, N);
266afb13afcSAdam Balogh }
267afb13afcSAdam Balogh }
268afb13afcSAdam Balogh
reportBug(StringRef Message,SVal Val1,SVal Val2,CheckerContext & C,ExplodedNode * ErrNode) const269*8ee3dfd7SBalazs Benics void MismatchedIteratorChecker::reportBug(StringRef Message, SVal Val1,
270*8ee3dfd7SBalazs Benics SVal Val2, CheckerContext &C,
271afb13afcSAdam Balogh ExplodedNode *ErrNode) const {
27218f219c5SBalazs Benics auto R = std::make_unique<PathSensitiveBugReport>(MismatchedBugType, Message,
273afb13afcSAdam Balogh ErrNode);
274afb13afcSAdam Balogh R->markInteresting(Val1);
275afb13afcSAdam Balogh R->markInteresting(Val2);
276afb13afcSAdam Balogh C.emitReport(std::move(R));
277afb13afcSAdam Balogh }
278afb13afcSAdam Balogh
reportBug(StringRef Message,SVal Val,const MemRegion * Reg,CheckerContext & C,ExplodedNode * ErrNode) const279*8ee3dfd7SBalazs Benics void MismatchedIteratorChecker::reportBug(StringRef Message, SVal Val,
2807619050cSBalazs Benics const MemRegion *Reg,
281afb13afcSAdam Balogh CheckerContext &C,
282afb13afcSAdam Balogh ExplodedNode *ErrNode) const {
28318f219c5SBalazs Benics auto R = std::make_unique<PathSensitiveBugReport>(MismatchedBugType, Message,
284afb13afcSAdam Balogh ErrNode);
285afb13afcSAdam Balogh R->markInteresting(Val);
286afb13afcSAdam Balogh R->markInteresting(Reg);
287afb13afcSAdam Balogh C.emitReport(std::move(R));
288afb13afcSAdam Balogh }
289afb13afcSAdam Balogh
registerMismatchedIteratorChecker(CheckerManager & mgr)290afb13afcSAdam Balogh void ento::registerMismatchedIteratorChecker(CheckerManager &mgr) {
291afb13afcSAdam Balogh mgr.registerChecker<MismatchedIteratorChecker>();
292afb13afcSAdam Balogh }
293afb13afcSAdam Balogh
shouldRegisterMismatchedIteratorChecker(const CheckerManager & mgr)294bda3dd0dSKirstóf Umann bool ento::shouldRegisterMismatchedIteratorChecker(const CheckerManager &mgr) {
295afb13afcSAdam Balogh return true;
296afb13afcSAdam Balogh }
297