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