1 //===- unittests/StaticAnalyzer/NoStateChangeFuncVisitorTest.cpp ----------===//
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 #include "CheckerRegistration.h"
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
12 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
13 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
14 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
15 #include "clang/StaticAnalyzer/Core/Checker.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
21 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
22 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
23 #include "llvm/Support/ErrorHandling.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include "gtest/gtest.h"
26 #include <memory>
27 
28 //===----------------------------------------------------------------------===//
29 // Base classes for testing NoStateChangeFuncVisitor.
30 //
31 // Testing is done by observing a very simple trait change from one node to
32 // another -- the checker sets the ErrorPrevented trait to true if
33 // 'preventError()' is called in the source code, and sets it to false if
34 // 'allowError()' is called. If this trait is false when 'error()' is called,
35 // a warning is emitted.
36 //
37 // The checker then registers a simple NoStateChangeFuncVisitor to add notes to
38 // inlined functions that could have, but neglected to prevent the error.
39 //===----------------------------------------------------------------------===//
40 
41 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrorPrevented, bool)
42 
43 namespace clang {
44 namespace ento {
45 namespace {
46 
47 class ErrorNotPreventedFuncVisitor : public NoStateChangeFuncVisitor {
48 public:
49   ErrorNotPreventedFuncVisitor()
50       : NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough) {}
51 
52   virtual PathDiagnosticPieceRef
53   maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
54                            const ObjCMethodCall &Call,
55                            const ExplodedNode *N) override {
56     return nullptr;
57   }
58 
59   virtual PathDiagnosticPieceRef
60   maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
61                           const CXXConstructorCall &Call,
62                           const ExplodedNode *N) override {
63     return nullptr;
64   }
65 
66   virtual PathDiagnosticPieceRef
67   maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
68                              const ExplodedNode *N) override {
69     PathDiagnosticLocation L = PathDiagnosticLocation::create(
70         N->getLocation(),
71         N->getState()->getStateManager().getContext().getSourceManager());
72     return std::make_shared<PathDiagnosticEventPiece>(
73         L, "Returning without prevening the error");
74   }
75 
76   void Profile(llvm::FoldingSetNodeID &ID) const override {
77     static int Tag = 0;
78     ID.AddPointer(&Tag);
79   }
80 };
81 
82 template <class Visitor>
83 class StatefulChecker : public Checker<check::PreCall> {
84   mutable std::unique_ptr<BugType> BT;
85 
86 public:
87   void checkPreCall(const CallEvent &Call, CheckerContext &C) const {
88     if (Call.isCalled(CallDescription{"preventError", 0})) {
89       C.addTransition(C.getState()->set<ErrorPrevented>(true));
90       return;
91     }
92 
93     if (Call.isCalled(CallDescription{"allowError", 0})) {
94       C.addTransition(C.getState()->set<ErrorPrevented>(false));
95       return;
96     }
97 
98     if (Call.isCalled(CallDescription{"error", 0})) {
99       if (C.getState()->get<ErrorPrevented>())
100         return;
101       const ExplodedNode *N = C.generateErrorNode();
102       if (!N)
103         return;
104       if (!BT)
105         BT.reset(new BugType(this->getCheckerName(), "error()",
106                              categories::SecurityError));
107       auto R =
108           std::make_unique<PathSensitiveBugReport>(*BT, "error() called", N);
109       R->template addVisitor<Visitor>();
110       C.emitReport(std::move(R));
111     }
112   }
113 };
114 
115 } // namespace
116 } // namespace ento
117 } // namespace clang
118 
119 //===----------------------------------------------------------------------===//
120 // Non-thorough analysis: only the state right before and right after the
121 // function call is checked for the difference in trait value.
122 //===----------------------------------------------------------------------===//
123 
124 namespace clang {
125 namespace ento {
126 namespace {
127 
128 class NonThoroughErrorNotPreventedFuncVisitor
129     : public ErrorNotPreventedFuncVisitor {
130 public:
131   virtual bool
132   wasModifiedInFunction(const ExplodedNode *CallEnterN,
133                         const ExplodedNode *CallExitEndN) override {
134     return CallEnterN->getState()->get<ErrorPrevented>() !=
135            CallExitEndN->getState()->get<ErrorPrevented>();
136   }
137 };
138 
139 void addNonThoroughStatefulChecker(AnalysisASTConsumer &AnalysisConsumer,
140                                    AnalyzerOptions &AnOpts) {
141   AnOpts.CheckersAndPackages = {{"test.StatefulChecker", true}};
142   AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
143     Registry
144         .addChecker<StatefulChecker<NonThoroughErrorNotPreventedFuncVisitor>>(
145             "test.StatefulChecker", "Description", "");
146   });
147 }
148 
149 TEST(NoStateChangeFuncVisitor, NonThoroughFunctionAnalysis) {
150   std::string Diags;
151   EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"(
152     void error();
153     void preventError();
154     void allowError();
155 
156     void g() {
157       //preventError();
158     }
159 
160     void f() {
161       g();
162       error();
163     }
164   )", Diags));
165   EXPECT_EQ(Diags,
166             "test.StatefulChecker: Calling 'g' | Returning without prevening "
167             "the error | Returning from 'g' | error() called\n");
168 
169   Diags.clear();
170 
171   EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"(
172     void error();
173     void preventError();
174     void allowError();
175 
176     void g() {
177       preventError();
178       allowError();
179     }
180 
181     void f() {
182       g();
183       error();
184     }
185   )", Diags));
186   EXPECT_EQ(Diags,
187             "test.StatefulChecker: Calling 'g' | Returning without prevening "
188             "the error | Returning from 'g' | error() called\n");
189 
190   Diags.clear();
191 
192   EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"(
193     void error();
194     void preventError();
195     void allowError();
196 
197     void g() {
198       preventError();
199     }
200 
201     void f() {
202       g();
203       error();
204     }
205   )", Diags));
206   EXPECT_EQ(Diags, "");
207 }
208 
209 } // namespace
210 } // namespace ento
211 } // namespace clang
212 
213 //===----------------------------------------------------------------------===//
214 // Thorough analysis: only the state right before and right after the
215 // function call is checked for the difference in trait value.
216 //===----------------------------------------------------------------------===//
217 
218 namespace clang {
219 namespace ento {
220 namespace {
221 
222 class ThoroughErrorNotPreventedFuncVisitor
223     : public ErrorNotPreventedFuncVisitor {
224 public:
225   virtual bool
226   wasModifiedBeforeCallExit(const ExplodedNode *CurrN,
227                             const ExplodedNode *CallExitBeginN) override {
228     return CurrN->getState()->get<ErrorPrevented>() !=
229            CallExitBeginN->getState()->get<ErrorPrevented>();
230   }
231 };
232 
233 void addThoroughStatefulChecker(AnalysisASTConsumer &AnalysisConsumer,
234                                 AnalyzerOptions &AnOpts) {
235   AnOpts.CheckersAndPackages = {{"test.StatefulChecker", true}};
236   AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
237     Registry.addChecker<StatefulChecker<ThoroughErrorNotPreventedFuncVisitor>>(
238         "test.StatefulChecker", "Description", "");
239   });
240 }
241 
242 TEST(NoStateChangeFuncVisitor, ThoroughFunctionAnalysis) {
243   std::string Diags;
244   EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"(
245     void error();
246     void preventError();
247     void allowError();
248 
249     void g() {
250       //preventError();
251     }
252 
253     void f() {
254       g();
255       error();
256     }
257   )", Diags));
258   EXPECT_EQ(Diags,
259             "test.StatefulChecker: Calling 'g' | Returning without prevening "
260             "the error | Returning from 'g' | error() called\n");
261 
262   Diags.clear();
263 
264   EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"(
265     void error();
266     void preventError();
267     void allowError();
268 
269     void g() {
270       preventError();
271       allowError();
272     }
273 
274     void f() {
275       g();
276       error();
277     }
278   )", Diags));
279   EXPECT_EQ(Diags, "test.StatefulChecker: error() called\n");
280 
281   Diags.clear();
282 
283   EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"(
284     void error();
285     void preventError();
286     void allowError();
287 
288     void g() {
289       preventError();
290     }
291 
292     void f() {
293       g();
294       error();
295     }
296   )", Diags));
297   EXPECT_EQ(Diags, "");
298 }
299 
300 } // namespace
301 } // namespace ento
302 } // namespace clang
303