xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp (revision 1db9f3b21e39176dd5b67cf8ac378633b172463e)
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/CallDescription.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
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, check::PreStmt<UnaryOperator>,
31                    check::PreStmt<BinaryOperator>,
32                    check::PreStmt<ArraySubscriptExpr>,
33                    check::PreStmt<MemberExpr>> {
34 
35   const BugType OutOfRangeBugType{this, "Iterator out of range",
36                                   "Misuse of STL APIs"};
37 
38   void verifyDereference(CheckerContext &C, SVal Val) const;
39   void verifyIncrement(CheckerContext &C, SVal Iter) const;
40   void verifyDecrement(CheckerContext &C, SVal Iter) const;
41   void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
42                               SVal LHS, SVal RHS) const;
43   void verifyAdvance(CheckerContext &C, SVal LHS, SVal RHS) const;
44   void verifyPrev(CheckerContext &C, SVal LHS, SVal RHS) const;
45   void verifyNext(CheckerContext &C, SVal LHS, SVal RHS) const;
46   void reportBug(StringRef Message, SVal Val, CheckerContext &C,
47                  ExplodedNode *ErrNode) const;
48 
49 public:
50   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
51   void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const;
52   void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
53   void checkPreStmt(const ArraySubscriptExpr *ASE, CheckerContext &C) const;
54   void checkPreStmt(const MemberExpr *ME, CheckerContext &C) const;
55 
56   using AdvanceFn = void (IteratorRangeChecker::*)(CheckerContext &, SVal,
57                                                    SVal) const;
58 
59   CallDescriptionMap<AdvanceFn> AdvanceFunctions = {
60       {{{"std", "advance"}, 2}, &IteratorRangeChecker::verifyAdvance},
61       {{{"std", "prev"}, 2}, &IteratorRangeChecker::verifyPrev},
62       {{{"std", "next"}, 2}, &IteratorRangeChecker::verifyNext},
63   };
64 };
65 
66 bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
67 bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos);
68 bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
69 bool isZero(ProgramStateRef State, NonLoc Val);
70 
71 } //namespace
72 
73 void IteratorRangeChecker::checkPreCall(const CallEvent &Call,
74                                         CheckerContext &C) const {
75   // Check for out of range access
76   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
77   if (!Func)
78     return;
79 
80   if (Func->isOverloadedOperator()) {
81     if (isIncrementOperator(Func->getOverloadedOperator())) {
82       // Check for out-of-range incrementions
83       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
84         verifyIncrement(C, InstCall->getCXXThisVal());
85       } else {
86         if (Call.getNumArgs() >= 1) {
87           verifyIncrement(C, Call.getArgSVal(0));
88         }
89       }
90     } else if (isDecrementOperator(Func->getOverloadedOperator())) {
91       // Check for out-of-range decrementions
92       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
93         verifyDecrement(C, InstCall->getCXXThisVal());
94       } else {
95         if (Call.getNumArgs() >= 1) {
96           verifyDecrement(C, Call.getArgSVal(0));
97         }
98       }
99     } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
100       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
101         // Check for out-of-range incrementions and decrementions
102         if (Call.getNumArgs() >= 1 &&
103             Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
104           verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
105                                  InstCall->getCXXThisVal(),
106                                  Call.getArgSVal(0));
107         }
108       } else {
109         if (Call.getNumArgs() >= 2 &&
110             Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
111           verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
112                                  Call.getArgSVal(0), Call.getArgSVal(1));
113         }
114       }
115     } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
116       // Check for dereference of out-of-range iterators
117       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
118         verifyDereference(C, InstCall->getCXXThisVal());
119       } else {
120         verifyDereference(C, Call.getArgSVal(0));
121       }
122     }
123   } else {
124     const AdvanceFn *Verifier = AdvanceFunctions.lookup(Call);
125     if (Verifier) {
126       if (Call.getNumArgs() > 1) {
127         (this->**Verifier)(C, Call.getArgSVal(0), Call.getArgSVal(1));
128       } else {
129         auto &BVF = C.getSValBuilder().getBasicValueFactory();
130         (this->**Verifier)(
131             C, Call.getArgSVal(0),
132             nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
133       }
134     }
135   }
136 }
137 
138 void IteratorRangeChecker::checkPreStmt(const UnaryOperator *UO,
139                                         CheckerContext &C) const {
140   if (isa<CXXThisExpr>(UO->getSubExpr()))
141     return;
142 
143   ProgramStateRef State = C.getState();
144   UnaryOperatorKind OK = UO->getOpcode();
145   SVal SubVal = State->getSVal(UO->getSubExpr(), C.getLocationContext());
146 
147   if (isDereferenceOperator(OK)) {
148     verifyDereference(C, SubVal);
149   } else if (isIncrementOperator(OK)) {
150     verifyIncrement(C, SubVal);
151   } else if (isDecrementOperator(OK)) {
152     verifyDecrement(C, SubVal);
153   }
154 }
155 
156 void IteratorRangeChecker::checkPreStmt(const BinaryOperator *BO,
157                                         CheckerContext &C) const {
158   ProgramStateRef State = C.getState();
159   BinaryOperatorKind OK = BO->getOpcode();
160   SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
161 
162   if (isDereferenceOperator(OK)) {
163     verifyDereference(C, LVal);
164   } else if (isRandomIncrOrDecrOperator(OK)) {
165     SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
166     if (!BO->getRHS()->getType()->isIntegralOrEnumerationType())
167       return;
168     verifyRandomIncrOrDecr(C, BinaryOperator::getOverloadedOperator(OK), LVal,
169                            RVal);
170   }
171 }
172 
173 void IteratorRangeChecker::checkPreStmt(const ArraySubscriptExpr *ASE,
174                                         CheckerContext &C) const {
175   ProgramStateRef State = C.getState();
176   SVal LVal = State->getSVal(ASE->getLHS(), C.getLocationContext());
177   verifyDereference(C, LVal);
178 }
179 
180 void IteratorRangeChecker::checkPreStmt(const MemberExpr *ME,
181                                         CheckerContext &C) const {
182   if (!ME->isArrow() || ME->isImplicitAccess())
183     return;
184 
185   ProgramStateRef State = C.getState();
186   SVal BaseVal = State->getSVal(ME->getBase(), C.getLocationContext());
187   verifyDereference(C, BaseVal);
188 }
189 
190 void IteratorRangeChecker::verifyDereference(CheckerContext &C,
191                                              SVal Val) const {
192   auto State = C.getState();
193   const auto *Pos = getIteratorPosition(State, Val);
194   if (Pos && isPastTheEnd(State, *Pos)) {
195     auto *N = C.generateErrorNode(State);
196     if (!N)
197       return;
198     reportBug("Past-the-end iterator dereferenced.", Val, C, N);
199     return;
200   }
201 }
202 
203 void IteratorRangeChecker::verifyIncrement(CheckerContext &C, SVal Iter) const {
204   auto &BVF = C.getSValBuilder().getBasicValueFactory();
205   verifyRandomIncrOrDecr(C, OO_Plus, Iter,
206                      nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
207 }
208 
209 void IteratorRangeChecker::verifyDecrement(CheckerContext &C, SVal Iter) const {
210   auto &BVF = C.getSValBuilder().getBasicValueFactory();
211   verifyRandomIncrOrDecr(C, OO_Minus, Iter,
212                      nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
213 }
214 
215 void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &C,
216                                                   OverloadedOperatorKind Op,
217                                                   SVal LHS, SVal RHS) const {
218   auto State = C.getState();
219 
220   auto Value = RHS;
221   if (auto ValAsLoc = RHS.getAs<Loc>()) {
222     Value = State->getRawSVal(*ValAsLoc);
223   }
224 
225   if (Value.isUnknownOrUndef() || !isa<NonLoc>(Value))
226     return;
227 
228   // Incremention or decremention by 0 is never a bug.
229   if (isZero(State, Value.castAs<NonLoc>()))
230     return;
231 
232   // The result may be the past-end iterator of the container, but any other
233   // out of range position is undefined behaviour
234   auto StateAfter = advancePosition(State, LHS, Op, Value);
235   if (!StateAfter)
236     return;
237 
238   const auto *PosAfter = getIteratorPosition(StateAfter, LHS);
239   assert(PosAfter &&
240          "Iterator should have position after successful advancement");
241   if (isAheadOfRange(State, *PosAfter)) {
242     auto *N = C.generateErrorNode(State);
243     if (!N)
244       return;
245     reportBug("Iterator decremented ahead of its valid range.", LHS,
246                         C, N);
247   }
248   if (isBehindPastTheEnd(State, *PosAfter)) {
249     auto *N = C.generateErrorNode(State);
250     if (!N)
251       return;
252     reportBug("Iterator incremented behind the past-the-end "
253                         "iterator.", LHS, C, N);
254   }
255 }
256 
257 void IteratorRangeChecker::verifyAdvance(CheckerContext &C, SVal LHS,
258                                          SVal RHS) const {
259   verifyRandomIncrOrDecr(C, OO_PlusEqual, LHS, RHS);
260 }
261 
262 void IteratorRangeChecker::verifyPrev(CheckerContext &C, SVal LHS,
263                                       SVal RHS) const {
264   verifyRandomIncrOrDecr(C, OO_Minus, LHS, RHS);
265 }
266 
267 void IteratorRangeChecker::verifyNext(CheckerContext &C, SVal LHS,
268                                       SVal RHS) const {
269   verifyRandomIncrOrDecr(C, OO_Plus, LHS, RHS);
270 }
271 
272 void IteratorRangeChecker::reportBug(StringRef Message, SVal Val,
273                                      CheckerContext &C,
274                                      ExplodedNode *ErrNode) const {
275   auto R = std::make_unique<PathSensitiveBugReport>(OutOfRangeBugType, Message,
276                                                     ErrNode);
277 
278   const auto *Pos = getIteratorPosition(C.getState(), Val);
279   assert(Pos && "Iterator without known position cannot be out-of-range.");
280 
281   R->markInteresting(Val);
282   R->markInteresting(Pos->getContainer());
283   C.emitReport(std::move(R));
284 }
285 
286 namespace {
287 
288 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
289 bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
290 bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
291 
292 bool isZero(ProgramStateRef State, NonLoc Val) {
293   auto &BVF = State->getBasicVals();
294   return compare(State, Val,
295                  nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
296                  BO_EQ);
297 }
298 
299 bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
300   const auto *Cont = Pos.getContainer();
301   const auto *CData = getContainerData(State, Cont);
302   if (!CData)
303     return false;
304 
305   const auto End = CData->getEnd();
306   if (End) {
307     if (isEqual(State, Pos.getOffset(), End)) {
308       return true;
309     }
310   }
311 
312   return false;
313 }
314 
315 bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
316   const auto *Cont = Pos.getContainer();
317   const auto *CData = getContainerData(State, Cont);
318   if (!CData)
319     return false;
320 
321   const auto Beg = CData->getBegin();
322   if (Beg) {
323     if (isLess(State, Pos.getOffset(), Beg)) {
324       return true;
325     }
326   }
327 
328   return false;
329 }
330 
331 bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
332   const auto *Cont = Pos.getContainer();
333   const auto *CData = getContainerData(State, Cont);
334   if (!CData)
335     return false;
336 
337   const auto End = CData->getEnd();
338   if (End) {
339     if (isGreater(State, Pos.getOffset(), End)) {
340       return true;
341     }
342   }
343 
344   return false;
345 }
346 
347 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
348   return compare(State, Sym1, Sym2, BO_LT);
349 }
350 
351 bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
352   return compare(State, Sym1, Sym2, BO_GT);
353 }
354 
355 bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
356   return compare(State, Sym1, Sym2, BO_EQ);
357 }
358 
359 } // namespace
360 
361 void ento::registerIteratorRangeChecker(CheckerManager &mgr) {
362   mgr.registerChecker<IteratorRangeChecker>();
363 }
364 
365 bool ento::shouldRegisterIteratorRangeChecker(const CheckerManager &mgr) {
366   return true;
367 }
368