xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1480093f4SDimitry Andric //===-- MismatchedIteratorChecker.cpp -----------------------------*- C++ -*--//
2480093f4SDimitry Andric //
3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6480093f4SDimitry Andric //
7480093f4SDimitry Andric //===----------------------------------------------------------------------===//
8480093f4SDimitry Andric //
9480093f4SDimitry Andric // Defines a checker for mistakenly applying a foreign iterator on a container
10480093f4SDimitry Andric // and for using iterators of two different containers in a context where
11480093f4SDimitry Andric // iterators of the same container should be used.
12480093f4SDimitry Andric //
13480093f4SDimitry Andric //===----------------------------------------------------------------------===//
14480093f4SDimitry Andric 
15480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
18480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20480093f4SDimitry Andric 
21480093f4SDimitry Andric 
22480093f4SDimitry Andric #include "Iterator.h"
23480093f4SDimitry Andric 
24480093f4SDimitry Andric using namespace clang;
25480093f4SDimitry Andric using namespace ento;
26480093f4SDimitry Andric using namespace iterator;
27480093f4SDimitry Andric 
28480093f4SDimitry Andric namespace {
29480093f4SDimitry Andric 
30480093f4SDimitry Andric class MismatchedIteratorChecker
315ffd83dbSDimitry Andric   : public Checker<check::PreCall, check::PreStmt<BinaryOperator>> {
32480093f4SDimitry Andric 
33480093f4SDimitry Andric   std::unique_ptr<BugType> MismatchedBugType;
34480093f4SDimitry Andric 
35480093f4SDimitry Andric   void verifyMatch(CheckerContext &C, const SVal &Iter,
36480093f4SDimitry Andric                    const MemRegion *Cont) const;
37480093f4SDimitry Andric   void verifyMatch(CheckerContext &C, const SVal &Iter1,
38480093f4SDimitry Andric                    const SVal &Iter2) const;
39480093f4SDimitry Andric   void reportBug(const StringRef &Message, const SVal &Val1,
40480093f4SDimitry Andric                  const SVal &Val2, CheckerContext &C,
41480093f4SDimitry Andric                  ExplodedNode *ErrNode) const;
42480093f4SDimitry Andric   void reportBug(const StringRef &Message, const SVal &Val,
43480093f4SDimitry Andric                  const MemRegion *Reg, CheckerContext &C,
44480093f4SDimitry Andric                  ExplodedNode *ErrNode) const;
45480093f4SDimitry Andric 
46480093f4SDimitry Andric public:
47480093f4SDimitry Andric   MismatchedIteratorChecker();
48480093f4SDimitry Andric 
49480093f4SDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
505ffd83dbSDimitry Andric   void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
51480093f4SDimitry Andric 
52480093f4SDimitry Andric };
53480093f4SDimitry Andric 
54480093f4SDimitry Andric } // namespace
55480093f4SDimitry Andric 
56480093f4SDimitry Andric MismatchedIteratorChecker::MismatchedIteratorChecker() {
57480093f4SDimitry Andric   MismatchedBugType.reset(
58480093f4SDimitry Andric       new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
59480093f4SDimitry Andric                   /*SuppressOnSink=*/true));
60480093f4SDimitry Andric }
61480093f4SDimitry Andric 
62480093f4SDimitry Andric void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call,
63480093f4SDimitry Andric                                              CheckerContext &C) const {
64480093f4SDimitry Andric   // Check for iterator mismatches
65480093f4SDimitry Andric   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
66480093f4SDimitry Andric   if (!Func)
67480093f4SDimitry Andric     return;
68480093f4SDimitry Andric 
69480093f4SDimitry Andric   if (Func->isOverloadedOperator() &&
70480093f4SDimitry Andric       isComparisonOperator(Func->getOverloadedOperator())) {
71480093f4SDimitry Andric     // Check for comparisons of iterators of different containers
72480093f4SDimitry Andric     if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
73480093f4SDimitry Andric       if (Call.getNumArgs() < 1)
74480093f4SDimitry Andric         return;
75480093f4SDimitry Andric 
76480093f4SDimitry Andric       if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
77480093f4SDimitry Andric           !isIteratorType(Call.getArgExpr(0)->getType()))
78480093f4SDimitry Andric         return;
79480093f4SDimitry Andric 
80480093f4SDimitry Andric       verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
81480093f4SDimitry Andric     } else {
82480093f4SDimitry Andric       if (Call.getNumArgs() < 2)
83480093f4SDimitry Andric         return;
84480093f4SDimitry Andric 
85480093f4SDimitry Andric       if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
86480093f4SDimitry Andric           !isIteratorType(Call.getArgExpr(1)->getType()))
87480093f4SDimitry Andric         return;
88480093f4SDimitry Andric 
89480093f4SDimitry Andric       verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
90480093f4SDimitry Andric     }
91480093f4SDimitry Andric   } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
92480093f4SDimitry Andric     const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
93480093f4SDimitry Andric     if (!ContReg)
94480093f4SDimitry Andric       return;
95480093f4SDimitry Andric     // Check for erase, insert and emplace using iterator of another container
96480093f4SDimitry Andric     if (isEraseCall(Func) || isEraseAfterCall(Func)) {
97480093f4SDimitry Andric       verifyMatch(C, Call.getArgSVal(0),
98480093f4SDimitry Andric                   InstCall->getCXXThisVal().getAsRegion());
99480093f4SDimitry Andric       if (Call.getNumArgs() == 2) {
100480093f4SDimitry Andric         verifyMatch(C, Call.getArgSVal(1),
101480093f4SDimitry Andric                     InstCall->getCXXThisVal().getAsRegion());
102480093f4SDimitry Andric       }
103480093f4SDimitry Andric     } else if (isInsertCall(Func)) {
104480093f4SDimitry Andric       verifyMatch(C, Call.getArgSVal(0),
105480093f4SDimitry Andric                   InstCall->getCXXThisVal().getAsRegion());
106480093f4SDimitry Andric       if (Call.getNumArgs() == 3 &&
107480093f4SDimitry Andric           isIteratorType(Call.getArgExpr(1)->getType()) &&
108480093f4SDimitry Andric           isIteratorType(Call.getArgExpr(2)->getType())) {
109480093f4SDimitry Andric         verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
110480093f4SDimitry Andric       }
111480093f4SDimitry Andric     } else if (isEmplaceCall(Func)) {
112480093f4SDimitry Andric       verifyMatch(C, Call.getArgSVal(0),
113480093f4SDimitry Andric                   InstCall->getCXXThisVal().getAsRegion());
114480093f4SDimitry Andric     }
115480093f4SDimitry Andric   } else if (isa<CXXConstructorCall>(&Call)) {
116480093f4SDimitry Andric     // Check match of first-last iterator pair in a constructor of a container
117480093f4SDimitry Andric     if (Call.getNumArgs() < 2)
118480093f4SDimitry Andric       return;
119480093f4SDimitry Andric 
120480093f4SDimitry Andric     const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
121480093f4SDimitry Andric     if (Ctr->getNumParams() < 2)
122480093f4SDimitry Andric       return;
123480093f4SDimitry Andric 
124480093f4SDimitry Andric     if (Ctr->getParamDecl(0)->getName() != "first" ||
125480093f4SDimitry Andric         Ctr->getParamDecl(1)->getName() != "last")
126480093f4SDimitry Andric       return;
127480093f4SDimitry Andric 
128480093f4SDimitry Andric     if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
129480093f4SDimitry Andric         !isIteratorType(Call.getArgExpr(1)->getType()))
130480093f4SDimitry Andric       return;
131480093f4SDimitry Andric 
132480093f4SDimitry Andric     verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
133480093f4SDimitry Andric   } else {
134480093f4SDimitry Andric     // The main purpose of iterators is to abstract away from different
135480093f4SDimitry Andric     // containers and provide a (maybe limited) uniform access to them.
136480093f4SDimitry Andric     // This implies that any correctly written template function that
137480093f4SDimitry Andric     // works on multiple containers using iterators takes different
138480093f4SDimitry Andric     // template parameters for different containers. So we can safely
139480093f4SDimitry Andric     // assume that passing iterators of different containers as arguments
140480093f4SDimitry Andric     // whose type replaces the same template parameter is a bug.
141480093f4SDimitry Andric     //
142480093f4SDimitry Andric     // Example:
143480093f4SDimitry Andric     // template<typename I1, typename I2>
144480093f4SDimitry Andric     // void f(I1 first1, I1 last1, I2 first2, I2 last2);
145480093f4SDimitry Andric     //
146480093f4SDimitry Andric     // In this case the first two arguments to f() must be iterators must belong
147480093f4SDimitry Andric     // to the same container and the last to also to the same container but
148480093f4SDimitry Andric     // not necessarily to the same as the first two.
149480093f4SDimitry Andric 
150480093f4SDimitry Andric     const auto *Templ = Func->getPrimaryTemplate();
151480093f4SDimitry Andric     if (!Templ)
152480093f4SDimitry Andric       return;
153480093f4SDimitry Andric 
154480093f4SDimitry Andric     const auto *TParams = Templ->getTemplateParameters();
155480093f4SDimitry Andric     const auto *TArgs = Func->getTemplateSpecializationArgs();
156480093f4SDimitry Andric 
157480093f4SDimitry Andric     // Iterate over all the template parameters
158480093f4SDimitry Andric     for (size_t I = 0; I < TParams->size(); ++I) {
159480093f4SDimitry Andric       const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
160480093f4SDimitry Andric       if (!TPDecl)
161480093f4SDimitry Andric         continue;
162480093f4SDimitry Andric 
163480093f4SDimitry Andric       if (TPDecl->isParameterPack())
164480093f4SDimitry Andric         continue;
165480093f4SDimitry Andric 
166480093f4SDimitry Andric       const auto TAType = TArgs->get(I).getAsType();
167480093f4SDimitry Andric       if (!isIteratorType(TAType))
168480093f4SDimitry Andric         continue;
169480093f4SDimitry Andric 
170480093f4SDimitry Andric       SVal LHS = UndefinedVal();
171480093f4SDimitry Andric 
172480093f4SDimitry Andric       // For every template parameter which is an iterator type in the
173480093f4SDimitry Andric       // instantiation look for all functions' parameters' type by it and
174480093f4SDimitry Andric       // check whether they belong to the same container
175480093f4SDimitry Andric       for (auto J = 0U; J < Func->getNumParams(); ++J) {
176480093f4SDimitry Andric         const auto *Param = Func->getParamDecl(J);
177480093f4SDimitry Andric         const auto *ParamType =
178480093f4SDimitry Andric             Param->getType()->getAs<SubstTemplateTypeParmType>();
179*bdd1243dSDimitry Andric         if (!ParamType)
180*bdd1243dSDimitry Andric           continue;
181*bdd1243dSDimitry Andric         const TemplateTypeParmDecl *D = ParamType->getReplacedParameter();
182*bdd1243dSDimitry Andric         if (D != TPDecl)
183480093f4SDimitry Andric           continue;
184480093f4SDimitry Andric         if (LHS.isUndef()) {
185480093f4SDimitry Andric           LHS = Call.getArgSVal(J);
186480093f4SDimitry Andric         } else {
187480093f4SDimitry Andric           verifyMatch(C, LHS, Call.getArgSVal(J));
188480093f4SDimitry Andric         }
189480093f4SDimitry Andric       }
190480093f4SDimitry Andric     }
191480093f4SDimitry Andric   }
192480093f4SDimitry Andric }
193480093f4SDimitry Andric 
1945ffd83dbSDimitry Andric void MismatchedIteratorChecker::checkPreStmt(const BinaryOperator *BO,
1955ffd83dbSDimitry Andric                                              CheckerContext &C) const {
1965ffd83dbSDimitry Andric   if (!BO->isComparisonOp())
1975ffd83dbSDimitry Andric     return;
1985ffd83dbSDimitry Andric 
1995ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
2005ffd83dbSDimitry Andric   SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
2015ffd83dbSDimitry Andric   SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
2025ffd83dbSDimitry Andric   verifyMatch(C, LVal, RVal);
2035ffd83dbSDimitry Andric }
2045ffd83dbSDimitry Andric 
205480093f4SDimitry Andric void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
206480093f4SDimitry Andric                                             const MemRegion *Cont) const {
207480093f4SDimitry Andric   // Verify match between a container and the container of an iterator
208480093f4SDimitry Andric   Cont = Cont->getMostDerivedObjectRegion();
209480093f4SDimitry Andric 
210480093f4SDimitry Andric   if (const auto *ContSym = Cont->getSymbolicBase()) {
211480093f4SDimitry Andric     if (isa<SymbolConjured>(ContSym->getSymbol()))
212480093f4SDimitry Andric       return;
213480093f4SDimitry Andric   }
214480093f4SDimitry Andric 
215480093f4SDimitry Andric   auto State = C.getState();
216480093f4SDimitry Andric   const auto *Pos = getIteratorPosition(State, Iter);
217480093f4SDimitry Andric   if (!Pos)
218480093f4SDimitry Andric     return;
219480093f4SDimitry Andric 
220480093f4SDimitry Andric   const auto *IterCont = Pos->getContainer();
221480093f4SDimitry Andric 
222480093f4SDimitry Andric   // Skip symbolic regions based on conjured symbols. Two conjured symbols
223480093f4SDimitry Andric   // may or may not be the same. For example, the same function can return
224480093f4SDimitry Andric   // the same or a different container but we get different conjured symbols
225480093f4SDimitry Andric   // for each call. This may cause false positives so omit them from the check.
226480093f4SDimitry Andric   if (const auto *ContSym = IterCont->getSymbolicBase()) {
227480093f4SDimitry Andric     if (isa<SymbolConjured>(ContSym->getSymbol()))
228480093f4SDimitry Andric       return;
229480093f4SDimitry Andric   }
230480093f4SDimitry Andric 
231480093f4SDimitry Andric   if (IterCont != Cont) {
232480093f4SDimitry Andric     auto *N = C.generateNonFatalErrorNode(State);
233480093f4SDimitry Andric     if (!N) {
234480093f4SDimitry Andric       return;
235480093f4SDimitry Andric     }
236480093f4SDimitry Andric     reportBug("Container accessed using foreign iterator argument.",
237480093f4SDimitry Andric                         Iter, Cont, C, N);
238480093f4SDimitry Andric   }
239480093f4SDimitry Andric }
240480093f4SDimitry Andric 
241480093f4SDimitry Andric void MismatchedIteratorChecker::verifyMatch(CheckerContext &C,
242480093f4SDimitry Andric                                             const SVal &Iter1,
243480093f4SDimitry Andric                                             const SVal &Iter2) const {
244480093f4SDimitry Andric   // Verify match between the containers of two iterators
245480093f4SDimitry Andric   auto State = C.getState();
246480093f4SDimitry Andric   const auto *Pos1 = getIteratorPosition(State, Iter1);
247480093f4SDimitry Andric   if (!Pos1)
248480093f4SDimitry Andric     return;
249480093f4SDimitry Andric 
250480093f4SDimitry Andric   const auto *IterCont1 = Pos1->getContainer();
251480093f4SDimitry Andric 
252480093f4SDimitry Andric   // Skip symbolic regions based on conjured symbols. Two conjured symbols
253480093f4SDimitry Andric   // may or may not be the same. For example, the same function can return
254480093f4SDimitry Andric   // the same or a different container but we get different conjured symbols
255480093f4SDimitry Andric   // for each call. This may cause false positives so omit them from the check.
256480093f4SDimitry Andric   if (const auto *ContSym = IterCont1->getSymbolicBase()) {
257480093f4SDimitry Andric     if (isa<SymbolConjured>(ContSym->getSymbol()))
258480093f4SDimitry Andric       return;
259480093f4SDimitry Andric   }
260480093f4SDimitry Andric 
261480093f4SDimitry Andric   const auto *Pos2 = getIteratorPosition(State, Iter2);
262480093f4SDimitry Andric   if (!Pos2)
263480093f4SDimitry Andric     return;
264480093f4SDimitry Andric 
265480093f4SDimitry Andric   const auto *IterCont2 = Pos2->getContainer();
266480093f4SDimitry Andric   if (const auto *ContSym = IterCont2->getSymbolicBase()) {
267480093f4SDimitry Andric     if (isa<SymbolConjured>(ContSym->getSymbol()))
268480093f4SDimitry Andric       return;
269480093f4SDimitry Andric   }
270480093f4SDimitry Andric 
271480093f4SDimitry Andric   if (IterCont1 != IterCont2) {
272480093f4SDimitry Andric     auto *N = C.generateNonFatalErrorNode(State);
273480093f4SDimitry Andric     if (!N)
274480093f4SDimitry Andric       return;
275480093f4SDimitry Andric     reportBug("Iterators of different containers used where the "
276480093f4SDimitry Andric                         "same container is expected.", Iter1, Iter2, C, N);
277480093f4SDimitry Andric   }
278480093f4SDimitry Andric }
279480093f4SDimitry Andric 
280480093f4SDimitry Andric void MismatchedIteratorChecker::reportBug(const StringRef &Message,
281480093f4SDimitry Andric                                           const SVal &Val1,
282480093f4SDimitry Andric                                           const SVal &Val2,
283480093f4SDimitry Andric                                           CheckerContext &C,
284480093f4SDimitry Andric                                           ExplodedNode *ErrNode) const {
285480093f4SDimitry Andric   auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
286480093f4SDimitry Andric                                                     ErrNode);
287480093f4SDimitry Andric   R->markInteresting(Val1);
288480093f4SDimitry Andric   R->markInteresting(Val2);
289480093f4SDimitry Andric   C.emitReport(std::move(R));
290480093f4SDimitry Andric }
291480093f4SDimitry Andric 
292480093f4SDimitry Andric void MismatchedIteratorChecker::reportBug(const StringRef &Message,
293480093f4SDimitry Andric                                           const SVal &Val, const MemRegion *Reg,
294480093f4SDimitry Andric                                           CheckerContext &C,
295480093f4SDimitry Andric                                           ExplodedNode *ErrNode) const {
296480093f4SDimitry Andric   auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
297480093f4SDimitry Andric                                                     ErrNode);
298480093f4SDimitry Andric   R->markInteresting(Val);
299480093f4SDimitry Andric   R->markInteresting(Reg);
300480093f4SDimitry Andric   C.emitReport(std::move(R));
301480093f4SDimitry Andric }
302480093f4SDimitry Andric 
303480093f4SDimitry Andric void ento::registerMismatchedIteratorChecker(CheckerManager &mgr) {
304480093f4SDimitry Andric   mgr.registerChecker<MismatchedIteratorChecker>();
305480093f4SDimitry Andric }
306480093f4SDimitry Andric 
3075ffd83dbSDimitry Andric bool ento::shouldRegisterMismatchedIteratorChecker(const CheckerManager &mgr) {
308480093f4SDimitry Andric   return true;
309480093f4SDimitry Andric }
310