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