xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp (revision 62cfcf62f627e5093fb37026a6d8c98e4d2ef04c)
1 //===-- DebugIteratorModeling.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 debugging iterator modeling.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15 #include "clang/StaticAnalyzer/Core/Checker.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
18 
19 #include "Iterator.h"
20 
21 using namespace clang;
22 using namespace ento;
23 using namespace iterator;
24 
25 namespace {
26 
27 class DebugIteratorModeling
28   : public Checker<eval::Call> {
29 
30   std::unique_ptr<BugType> DebugMsgBugType;
31 
32   template <typename Getter>
33   void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
34                                   Getter get) const;
35   void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
36   void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
37   template <typename Getter>
38   void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
39                                  Getter get, SVal Default) const;
40   void analyzerIteratorPosition(const CallExpr *CE, CheckerContext &C) const;
41   void analyzerIteratorContainer(const CallExpr *CE, CheckerContext &C) const;
42   void analyzerIteratorValidity(const CallExpr *CE, CheckerContext &C) const;
43   ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
44 
45   typedef void (DebugIteratorModeling::*FnCheck)(const CallExpr *,
46                                                  CheckerContext &) const;
47 
48   CallDescriptionMap<FnCheck> Callbacks = {
49     {{0, "clang_analyzer_container_begin", 1},
50      &DebugIteratorModeling::analyzerContainerBegin},
51     {{0, "clang_analyzer_container_end", 1},
52      &DebugIteratorModeling::analyzerContainerEnd},
53     {{0, "clang_analyzer_iterator_position", 1},
54      &DebugIteratorModeling::analyzerIteratorPosition},
55     {{0, "clang_analyzer_iterator_container", 1},
56      &DebugIteratorModeling::analyzerIteratorContainer},
57     {{0, "clang_analyzer_iterator_validity", 1},
58      &DebugIteratorModeling::analyzerIteratorValidity},
59   };
60 
61 public:
62   DebugIteratorModeling();
63 
64   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
65 };
66 
67 } //namespace
68 
69 DebugIteratorModeling::DebugIteratorModeling() {
70   DebugMsgBugType.reset(
71       new BugType(this, "Checking analyzer assumptions", "debug",
72                   /*SuppressOnSink=*/true));
73 }
74 
75 bool DebugIteratorModeling::evalCall(const CallEvent &Call,
76                                      CheckerContext &C) const {
77   const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
78   if (!CE)
79     return false;
80 
81   const FnCheck *Handler = Callbacks.lookup(Call);
82   if (!Handler)
83     return false;
84 
85   (this->**Handler)(CE, C);
86   return true;
87 }
88 
89 template <typename Getter>
90 void DebugIteratorModeling::analyzerContainerDataField(const CallExpr *CE,
91                                                        CheckerContext &C,
92                                                        Getter get) const {
93   if (CE->getNumArgs() == 0) {
94     reportDebugMsg("Missing container argument", C);
95     return;
96   }
97 
98   auto State = C.getState();
99   const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
100   if (Cont) {
101     const auto *Data = getContainerData(State, Cont);
102     if (Data) {
103       SymbolRef Field = get(Data);
104       if (Field) {
105         State = State->BindExpr(CE, C.getLocationContext(),
106                                 nonloc::SymbolVal(Field));
107         C.addTransition(State);
108         return;
109       }
110     }
111   }
112 
113   auto &BVF = C.getSValBuilder().getBasicValueFactory();
114   State = State->BindExpr(CE, C.getLocationContext(),
115                    nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
116 }
117 
118 void DebugIteratorModeling::analyzerContainerBegin(const CallExpr *CE,
119                                                    CheckerContext &C) const {
120   analyzerContainerDataField(CE, C, [](const ContainerData *D) {
121       return D->getBegin();
122     });
123 }
124 
125 void DebugIteratorModeling::analyzerContainerEnd(const CallExpr *CE,
126                                                  CheckerContext &C) const {
127   analyzerContainerDataField(CE, C, [](const ContainerData *D) {
128       return D->getEnd();
129     });
130 }
131 
132 template <typename Getter>
133 void DebugIteratorModeling::analyzerIteratorDataField(const CallExpr *CE,
134                                                       CheckerContext &C,
135                                                       Getter get,
136                                                       SVal Default) const {
137   if (CE->getNumArgs() == 0) {
138     reportDebugMsg("Missing iterator argument", C);
139     return;
140   }
141 
142   auto State = C.getState();
143   SVal V = C.getSVal(CE->getArg(0));
144   const auto *Pos = getIteratorPosition(State, V);
145   if (Pos) {
146     State = State->BindExpr(CE, C.getLocationContext(), get(Pos));
147   } else {
148     State = State->BindExpr(CE, C.getLocationContext(), Default);
149   }
150   C.addTransition(State);
151 }
152 
153 void DebugIteratorModeling::analyzerIteratorPosition(const CallExpr *CE,
154                                                      CheckerContext &C) const {
155   auto &BVF = C.getSValBuilder().getBasicValueFactory();
156   analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
157       return nonloc::SymbolVal(P->getOffset());
158     }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
159 }
160 
161 void DebugIteratorModeling::analyzerIteratorContainer(const CallExpr *CE,
162                                                       CheckerContext &C) const {
163   auto &BVF = C.getSValBuilder().getBasicValueFactory();
164   analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
165       return loc::MemRegionVal(P->getContainer());
166     }, loc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
167 }
168 
169 void DebugIteratorModeling::analyzerIteratorValidity(const CallExpr *CE,
170                                                      CheckerContext &C) const {
171   auto &BVF = C.getSValBuilder().getBasicValueFactory();
172   analyzerIteratorDataField(CE, C, [&BVF](const IteratorPosition *P) {
173       return
174         nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get((P->isValid()))));
175     }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
176 }
177 
178 ExplodedNode *DebugIteratorModeling::reportDebugMsg(llvm::StringRef Msg,
179                                                     CheckerContext &C) const {
180   ExplodedNode *N = C.generateNonFatalErrorNode();
181   if (!N)
182     return nullptr;
183 
184   auto &BR = C.getBugReporter();
185   BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
186                                                          Msg, N));
187   return N;
188 }
189 
190 void ento::registerDebugIteratorModeling(CheckerManager &mgr) {
191   mgr.registerChecker<DebugIteratorModeling>();
192 }
193 
194 bool ento::shouldRegisterDebugIteratorModeling(const LangOptions &LO) {
195   return true;
196 }
197