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