xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp (revision 480093f4440d54b30b3025afeac24b48f2ba7a2e)
1*480093f4SDimitry Andric //===-- IteratorRangeChecker.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 dereference of the past-the-end iterator and
10*480093f4SDimitry Andric // out-of-range increments and decrements.
11*480093f4SDimitry Andric //
12*480093f4SDimitry Andric //===----------------------------------------------------------------------===//
13*480093f4SDimitry Andric 
14*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
17*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19*480093f4SDimitry Andric 
20*480093f4SDimitry Andric 
21*480093f4SDimitry Andric #include "Iterator.h"
22*480093f4SDimitry Andric 
23*480093f4SDimitry Andric using namespace clang;
24*480093f4SDimitry Andric using namespace ento;
25*480093f4SDimitry Andric using namespace iterator;
26*480093f4SDimitry Andric 
27*480093f4SDimitry Andric namespace {
28*480093f4SDimitry Andric 
29*480093f4SDimitry Andric class IteratorRangeChecker
30*480093f4SDimitry Andric   : public Checker<check::PreCall> {
31*480093f4SDimitry Andric 
32*480093f4SDimitry Andric   std::unique_ptr<BugType> OutOfRangeBugType;
33*480093f4SDimitry Andric 
34*480093f4SDimitry Andric   void verifyDereference(CheckerContext &C, const SVal &Val) const;
35*480093f4SDimitry Andric   void verifyIncrement(CheckerContext &C, const SVal &Iter) const;
36*480093f4SDimitry Andric   void verifyDecrement(CheckerContext &C, const SVal &Iter) const;
37*480093f4SDimitry Andric   void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
38*480093f4SDimitry Andric                               const SVal &LHS, const SVal &RHS) const;
39*480093f4SDimitry Andric   void reportBug(const StringRef &Message, const SVal &Val,
40*480093f4SDimitry Andric                  CheckerContext &C, ExplodedNode *ErrNode) const;
41*480093f4SDimitry Andric public:
42*480093f4SDimitry Andric   IteratorRangeChecker();
43*480093f4SDimitry Andric 
44*480093f4SDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
45*480093f4SDimitry Andric 
46*480093f4SDimitry Andric };
47*480093f4SDimitry Andric 
48*480093f4SDimitry Andric bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
49*480093f4SDimitry Andric bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos);
50*480093f4SDimitry Andric bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
51*480093f4SDimitry Andric bool isZero(ProgramStateRef State, const NonLoc &Val);
52*480093f4SDimitry Andric 
53*480093f4SDimitry Andric } //namespace
54*480093f4SDimitry Andric 
55*480093f4SDimitry Andric IteratorRangeChecker::IteratorRangeChecker() {
56*480093f4SDimitry Andric   OutOfRangeBugType.reset(
57*480093f4SDimitry Andric       new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
58*480093f4SDimitry Andric }
59*480093f4SDimitry Andric 
60*480093f4SDimitry Andric void IteratorRangeChecker::checkPreCall(const CallEvent &Call,
61*480093f4SDimitry Andric                                         CheckerContext &C) const {
62*480093f4SDimitry Andric   // Check for out of range access
63*480093f4SDimitry Andric   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
64*480093f4SDimitry Andric   if (!Func)
65*480093f4SDimitry Andric     return;
66*480093f4SDimitry Andric 
67*480093f4SDimitry Andric   if (Func->isOverloadedOperator()) {
68*480093f4SDimitry Andric     if (isIncrementOperator(Func->getOverloadedOperator())) {
69*480093f4SDimitry Andric       // Check for out-of-range incrementions
70*480093f4SDimitry Andric       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
71*480093f4SDimitry Andric         verifyIncrement(C, InstCall->getCXXThisVal());
72*480093f4SDimitry Andric       } else {
73*480093f4SDimitry Andric         if (Call.getNumArgs() >= 1) {
74*480093f4SDimitry Andric           verifyIncrement(C, Call.getArgSVal(0));
75*480093f4SDimitry Andric         }
76*480093f4SDimitry Andric       }
77*480093f4SDimitry Andric     } else if (isDecrementOperator(Func->getOverloadedOperator())) {
78*480093f4SDimitry Andric       // Check for out-of-range decrementions
79*480093f4SDimitry Andric       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
80*480093f4SDimitry Andric         verifyDecrement(C, InstCall->getCXXThisVal());
81*480093f4SDimitry Andric       } else {
82*480093f4SDimitry Andric         if (Call.getNumArgs() >= 1) {
83*480093f4SDimitry Andric           verifyDecrement(C, Call.getArgSVal(0));
84*480093f4SDimitry Andric         }
85*480093f4SDimitry Andric       }
86*480093f4SDimitry Andric     } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
87*480093f4SDimitry Andric       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
88*480093f4SDimitry Andric         // Check for out-of-range incrementions and decrementions
89*480093f4SDimitry Andric         if (Call.getNumArgs() >= 1 &&
90*480093f4SDimitry Andric             Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
91*480093f4SDimitry Andric           verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
92*480093f4SDimitry Andric                                  InstCall->getCXXThisVal(),
93*480093f4SDimitry Andric                                  Call.getArgSVal(0));
94*480093f4SDimitry Andric         }
95*480093f4SDimitry Andric       } else {
96*480093f4SDimitry Andric         if (Call.getNumArgs() >= 2 &&
97*480093f4SDimitry Andric             Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
98*480093f4SDimitry Andric           verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
99*480093f4SDimitry Andric                                  Call.getArgSVal(0), Call.getArgSVal(1));
100*480093f4SDimitry Andric         }
101*480093f4SDimitry Andric       }
102*480093f4SDimitry Andric     } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
103*480093f4SDimitry Andric       // Check for dereference of out-of-range iterators
104*480093f4SDimitry Andric       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
105*480093f4SDimitry Andric         verifyDereference(C, InstCall->getCXXThisVal());
106*480093f4SDimitry Andric       } else {
107*480093f4SDimitry Andric         verifyDereference(C, Call.getArgSVal(0));
108*480093f4SDimitry Andric       }
109*480093f4SDimitry Andric     }
110*480093f4SDimitry Andric   }
111*480093f4SDimitry Andric }
112*480093f4SDimitry Andric 
113*480093f4SDimitry Andric void IteratorRangeChecker::verifyDereference(CheckerContext &C,
114*480093f4SDimitry Andric                                              const SVal &Val) const {
115*480093f4SDimitry Andric   auto State = C.getState();
116*480093f4SDimitry Andric   const auto *Pos = getIteratorPosition(State, Val);
117*480093f4SDimitry Andric   if (Pos && isPastTheEnd(State, *Pos)) {
118*480093f4SDimitry Andric     auto *N = C.generateErrorNode(State);
119*480093f4SDimitry Andric     if (!N)
120*480093f4SDimitry Andric       return;
121*480093f4SDimitry Andric     reportBug("Past-the-end iterator dereferenced.", Val, C, N);
122*480093f4SDimitry Andric     return;
123*480093f4SDimitry Andric   }
124*480093f4SDimitry Andric }
125*480093f4SDimitry Andric 
126*480093f4SDimitry Andric void IteratorRangeChecker::verifyIncrement(CheckerContext &C,
127*480093f4SDimitry Andric                                           const SVal &Iter) const {
128*480093f4SDimitry Andric   auto &BVF = C.getSValBuilder().getBasicValueFactory();
129*480093f4SDimitry Andric   verifyRandomIncrOrDecr(C, OO_Plus, Iter,
130*480093f4SDimitry Andric                      nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
131*480093f4SDimitry Andric }
132*480093f4SDimitry Andric 
133*480093f4SDimitry Andric void IteratorRangeChecker::verifyDecrement(CheckerContext &C,
134*480093f4SDimitry Andric                                           const SVal &Iter) const {
135*480093f4SDimitry Andric   auto &BVF = C.getSValBuilder().getBasicValueFactory();
136*480093f4SDimitry Andric   verifyRandomIncrOrDecr(C, OO_Minus, Iter,
137*480093f4SDimitry Andric                      nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
138*480093f4SDimitry Andric }
139*480093f4SDimitry Andric 
140*480093f4SDimitry Andric void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &C,
141*480093f4SDimitry Andric                                                  OverloadedOperatorKind Op,
142*480093f4SDimitry Andric                                                  const SVal &LHS,
143*480093f4SDimitry Andric                                                  const SVal &RHS) const {
144*480093f4SDimitry Andric   auto State = C.getState();
145*480093f4SDimitry Andric 
146*480093f4SDimitry Andric   auto Value = RHS;
147*480093f4SDimitry Andric   if (auto ValAsLoc = RHS.getAs<Loc>()) {
148*480093f4SDimitry Andric     Value = State->getRawSVal(*ValAsLoc);
149*480093f4SDimitry Andric   }
150*480093f4SDimitry Andric 
151*480093f4SDimitry Andric   if (Value.isUnknown())
152*480093f4SDimitry Andric     return;
153*480093f4SDimitry Andric 
154*480093f4SDimitry Andric   // Incremention or decremention by 0 is never a bug.
155*480093f4SDimitry Andric   if (isZero(State, Value.castAs<NonLoc>()))
156*480093f4SDimitry Andric     return;
157*480093f4SDimitry Andric 
158*480093f4SDimitry Andric   // The result may be the past-end iterator of the container, but any other
159*480093f4SDimitry Andric   // out of range position is undefined behaviour
160*480093f4SDimitry Andric   auto StateAfter = advancePosition(State, LHS, Op, Value);
161*480093f4SDimitry Andric   if (!StateAfter)
162*480093f4SDimitry Andric     return;
163*480093f4SDimitry Andric 
164*480093f4SDimitry Andric   const auto *PosAfter = getIteratorPosition(StateAfter, LHS);
165*480093f4SDimitry Andric   assert(PosAfter &&
166*480093f4SDimitry Andric          "Iterator should have position after successful advancement");
167*480093f4SDimitry Andric   if (isAheadOfRange(State, *PosAfter)) {
168*480093f4SDimitry Andric     auto *N = C.generateErrorNode(State);
169*480093f4SDimitry Andric     if (!N)
170*480093f4SDimitry Andric       return;
171*480093f4SDimitry Andric     reportBug("Iterator decremented ahead of its valid range.", LHS,
172*480093f4SDimitry Andric                         C, N);
173*480093f4SDimitry Andric   }
174*480093f4SDimitry Andric   if (isBehindPastTheEnd(State, *PosAfter)) {
175*480093f4SDimitry Andric     auto *N = C.generateErrorNode(State);
176*480093f4SDimitry Andric     if (!N)
177*480093f4SDimitry Andric       return;
178*480093f4SDimitry Andric     reportBug("Iterator incremented behind the past-the-end "
179*480093f4SDimitry Andric                         "iterator.", LHS, C, N);
180*480093f4SDimitry Andric   }
181*480093f4SDimitry Andric }
182*480093f4SDimitry Andric 
183*480093f4SDimitry Andric void IteratorRangeChecker::reportBug(const StringRef &Message,
184*480093f4SDimitry Andric                                     const SVal &Val, CheckerContext &C,
185*480093f4SDimitry Andric                                     ExplodedNode *ErrNode) const {
186*480093f4SDimitry Andric   auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
187*480093f4SDimitry Andric                                                     ErrNode);
188*480093f4SDimitry Andric   R->markInteresting(Val);
189*480093f4SDimitry Andric   C.emitReport(std::move(R));
190*480093f4SDimitry Andric }
191*480093f4SDimitry Andric 
192*480093f4SDimitry Andric namespace {
193*480093f4SDimitry Andric 
194*480093f4SDimitry Andric bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
195*480093f4SDimitry Andric bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
196*480093f4SDimitry Andric bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
197*480093f4SDimitry Andric 
198*480093f4SDimitry Andric bool isZero(ProgramStateRef State, const NonLoc &Val) {
199*480093f4SDimitry Andric   auto &BVF = State->getBasicVals();
200*480093f4SDimitry Andric   return compare(State, Val,
201*480093f4SDimitry Andric                  nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
202*480093f4SDimitry Andric                  BO_EQ);
203*480093f4SDimitry Andric }
204*480093f4SDimitry Andric 
205*480093f4SDimitry Andric bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
206*480093f4SDimitry Andric   const auto *Cont = Pos.getContainer();
207*480093f4SDimitry Andric   const auto *CData = getContainerData(State, Cont);
208*480093f4SDimitry Andric   if (!CData)
209*480093f4SDimitry Andric     return false;
210*480093f4SDimitry Andric 
211*480093f4SDimitry Andric   const auto End = CData->getEnd();
212*480093f4SDimitry Andric   if (End) {
213*480093f4SDimitry Andric     if (isEqual(State, Pos.getOffset(), End)) {
214*480093f4SDimitry Andric       return true;
215*480093f4SDimitry Andric     }
216*480093f4SDimitry Andric   }
217*480093f4SDimitry Andric 
218*480093f4SDimitry Andric   return false;
219*480093f4SDimitry Andric }
220*480093f4SDimitry Andric 
221*480093f4SDimitry Andric bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
222*480093f4SDimitry Andric   const auto *Cont = Pos.getContainer();
223*480093f4SDimitry Andric   const auto *CData = getContainerData(State, Cont);
224*480093f4SDimitry Andric   if (!CData)
225*480093f4SDimitry Andric     return false;
226*480093f4SDimitry Andric 
227*480093f4SDimitry Andric   const auto Beg = CData->getBegin();
228*480093f4SDimitry Andric   if (Beg) {
229*480093f4SDimitry Andric     if (isLess(State, Pos.getOffset(), Beg)) {
230*480093f4SDimitry Andric       return true;
231*480093f4SDimitry Andric     }
232*480093f4SDimitry Andric   }
233*480093f4SDimitry Andric 
234*480093f4SDimitry Andric   return false;
235*480093f4SDimitry Andric }
236*480093f4SDimitry Andric 
237*480093f4SDimitry Andric bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
238*480093f4SDimitry Andric   const auto *Cont = Pos.getContainer();
239*480093f4SDimitry Andric   const auto *CData = getContainerData(State, Cont);
240*480093f4SDimitry Andric   if (!CData)
241*480093f4SDimitry Andric     return false;
242*480093f4SDimitry Andric 
243*480093f4SDimitry Andric   const auto End = CData->getEnd();
244*480093f4SDimitry Andric   if (End) {
245*480093f4SDimitry Andric     if (isGreater(State, Pos.getOffset(), End)) {
246*480093f4SDimitry Andric       return true;
247*480093f4SDimitry Andric     }
248*480093f4SDimitry Andric   }
249*480093f4SDimitry Andric 
250*480093f4SDimitry Andric   return false;
251*480093f4SDimitry Andric }
252*480093f4SDimitry Andric 
253*480093f4SDimitry Andric bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
254*480093f4SDimitry Andric   return compare(State, Sym1, Sym2, BO_LT);
255*480093f4SDimitry Andric }
256*480093f4SDimitry Andric 
257*480093f4SDimitry Andric bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
258*480093f4SDimitry Andric   return compare(State, Sym1, Sym2, BO_GT);
259*480093f4SDimitry Andric }
260*480093f4SDimitry Andric 
261*480093f4SDimitry Andric bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
262*480093f4SDimitry Andric   return compare(State, Sym1, Sym2, BO_EQ);
263*480093f4SDimitry Andric }
264*480093f4SDimitry Andric 
265*480093f4SDimitry Andric } // namespace
266*480093f4SDimitry Andric 
267*480093f4SDimitry Andric void ento::registerIteratorRangeChecker(CheckerManager &mgr) {
268*480093f4SDimitry Andric   mgr.registerChecker<IteratorRangeChecker>();
269*480093f4SDimitry Andric }
270*480093f4SDimitry Andric 
271*480093f4SDimitry Andric bool ento::shouldRegisterIteratorRangeChecker(const LangOptions &LO) {
272*480093f4SDimitry Andric   return true;
273*480093f4SDimitry Andric }
274