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