1 //===- unittests/StaticAnalyzer/SvalTest.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 
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/Decl.h"
13 #include "clang/AST/DeclGroup.h"
14 #include "clang/AST/RecursiveASTVisitor.h"
15 #include "clang/AST/Stmt.h"
16 #include "clang/AST/Type.h"
17 #include "clang/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
22 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
23 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
24 #include "clang/Tooling/Tooling.h"
25 #include "llvm/ADT/STLExtras.h"
26 #include "llvm/ADT/StringRef.h"
27 #include "llvm/Support/Casting.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include "gtest/gtest.h"
30 
31 namespace clang {
32 
33 // getType() tests include whole bunch of type comparisons,
34 // so when something is wrong, it's good to have gtest telling us
35 // what are those types.
operator <<(std::ostream & OS,const QualType & T)36 LLVM_ATTRIBUTE_UNUSED std::ostream &operator<<(std::ostream &OS,
37                                                const QualType &T) {
38   return OS << T.getAsString();
39 }
40 
operator <<(std::ostream & OS,const CanQualType & T)41 LLVM_ATTRIBUTE_UNUSED std::ostream &operator<<(std::ostream &OS,
42                                                const CanQualType &T) {
43   return OS << QualType{T};
44 }
45 
46 namespace ento {
47 namespace {
48 
49 //===----------------------------------------------------------------------===//
50 //                       Testing framework implementation
51 //===----------------------------------------------------------------------===//
52 
53 /// A simple map from variable names to symbolic values used to init them.
54 using SVals = llvm::StringMap<SVal>;
55 
56 /// SValCollector is the barebone of all tests.
57 ///
58 /// It is implemented as a checker and reacts to binds, so we find
59 /// symbolic values of interest, and to end analysis, where we actually
60 /// can test whatever we gathered.
61 class SValCollector : public Checker<check::Bind, check::EndAnalysis> {
62 public:
checkBind(SVal Loc,SVal Val,const Stmt * S,CheckerContext & C) const63   void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {
64     // Skip instantly if we finished testing.
65     // Also, we care only for binds happening in variable initializations.
66     if (Tested || !isa<DeclStmt>(S))
67       return;
68 
69     if (const auto *VR = llvm::dyn_cast_or_null<VarRegion>(Loc.getAsRegion())) {
70       CollectedSVals[VR->getDescriptiveName(false)] = Val;
71     }
72   }
73 
checkEndAnalysis(ExplodedGraph & G,BugReporter & B,ExprEngine & Engine) const74   void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,
75                         ExprEngine &Engine) const {
76     if (!Tested) {
77       test(Engine, Engine.getContext());
78       Tested = true;
79       CollectedSVals.clear();
80     }
81   }
82 
83   /// Helper function for tests to access bound symbolic values.
getByName(StringRef Name) const84   SVal getByName(StringRef Name) const { return CollectedSVals[Name]; }
85 
86 private:
87   /// Entry point for tests.
88   virtual void test(ExprEngine &Engine, const ASTContext &Context) const = 0;
89 
90   mutable bool Tested = false;
91   mutable SVals CollectedSVals;
92 };
93 
94 // SVAL_TEST is a combined way of providing a short code snippet and
95 // to test some programmatic predicates on symbolic values produced by the
96 // engine for the actual code.
97 //
98 // Each test has a NAME.  One can think of it as a name for normal gtests.
99 //
100 // Each test should provide a CODE snippet.  Code snippets might contain any
101 // valid C/C++, but have ONLY ONE defined function.  There are no requirements
102 // about function's name or parameters.  It can even be a class method.  The
103 // body of the function must contain a set of variable declarations.  Each
104 // variable declaration gets bound to a symbolic value, so for the following
105 // example:
106 //
107 //     int x = <expr>;
108 //
109 // `x` will be bound to whatever symbolic value the engine produced for <expr>.
110 // LIVENESS and REASSIGNMENTS don't affect this binding.
111 //
112 // During the test the actual values can be accessed via `getByName` function,
113 // and, for the `x`-bound value, one must use "x" as its name.
114 //
115 // Example:
116 // SVAL_TEST(SimpleSValTest, R"(
117 // void foo() {
118 //   int x = 42;
119 // })") {
120 //   SVal X = getByName("x");
121 //   EXPECT_TRUE(X.isConstant(42));
122 // }
123 #define SVAL_TEST(NAME, CODE)                                                  \
124   class NAME##SValCollector final : public SValCollector {                     \
125   public:                                                                      \
126     void test(ExprEngine &Engine, const ASTContext &Context) const override;   \
127   };                                                                           \
128                                                                                \
129   void add##NAME##SValCollector(AnalysisASTConsumer &AnalysisConsumer,         \
130                                 AnalyzerOptions &AnOpts) {                     \
131     AnOpts.CheckersAndPackages = {{"test.##NAME##SValCollector", true}};       \
132     AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {  \
133       Registry.addChecker<NAME##SValCollector>("test.##NAME##SValCollector",   \
134                                                "Description", "");             \
135     });                                                                        \
136   }                                                                            \
137                                                                                \
138   TEST(SValTest, NAME) { runCheckerOnCode<add##NAME##SValCollector>(CODE); }   \
139   void NAME##SValCollector::test(ExprEngine &Engine,                           \
140                                  const ASTContext &Context) const
141 
142 //===----------------------------------------------------------------------===//
143 //                                 Actual tests
144 //===----------------------------------------------------------------------===//
145 
146 SVAL_TEST(GetConstType, R"(
147 void foo() {
148   int x = 42;
149   int *y = nullptr;
150 })") {
151   SVal X = getByName("x");
152   ASSERT_FALSE(X.getType(Context).isNull());
153   EXPECT_EQ(Context.IntTy, X.getType(Context));
154 
155   SVal Y = getByName("y");
156   ASSERT_FALSE(Y.getType(Context).isNull());
157   EXPECT_EQ(Context.getUIntPtrType(), Y.getType(Context));
158 }
159 
160 SVAL_TEST(GetLocAsIntType, R"(
161 void foo(int *x) {
162   long int a = (long int)x;
163   unsigned b = (long unsigned)&a;
164   int c = (long int)nullptr;
165 })") {
166   SVal A = getByName("a");
167   ASSERT_FALSE(A.getType(Context).isNull());
168   // TODO: Turn it into signed long
169   EXPECT_EQ(Context.getUIntPtrType(), A.getType(Context));
170 
171   SVal B = getByName("b");
172   ASSERT_FALSE(B.getType(Context).isNull());
173   EXPECT_EQ(Context.UnsignedIntTy, B.getType(Context));
174 
175   SVal C = getByName("c");
176   ASSERT_FALSE(C.getType(Context).isNull());
177   EXPECT_EQ(Context.IntTy, C.getType(Context));
178 }
179 
180 SVAL_TEST(GetSymExprType, R"(
181 void foo(int a, int b) {
182   int x = a;
183   int y = a + b;
184   long z = a;
185 })") {
186   QualType Int = Context.IntTy;
187 
188   SVal X = getByName("x");
189   ASSERT_FALSE(X.getType(Context).isNull());
190   EXPECT_EQ(Int, X.getType(Context));
191 
192   SVal Y = getByName("y");
193   ASSERT_FALSE(Y.getType(Context).isNull());
194   EXPECT_EQ(Int, Y.getType(Context));
195 
196   // TODO: Change to Long when we support symbolic casts
197   SVal Z = getByName("z");
198   ASSERT_FALSE(Z.getType(Context).isNull());
199   EXPECT_EQ(Int, Z.getType(Context));
200 }
201 
202 SVAL_TEST(GetPointerType, R"(
203 int *bar();
204 int &foobar();
205 struct Z {
206   int a;
207   int *b;
208 };
209 void foo(int x, int *y, Z z) {
210   int &a = x;
211   int &b = *y;
212   int &c = *bar();
213   int &d = foobar();
214   int &e = z.a;
215   int &f = *z.b;
216 })") {
217   QualType Int = Context.IntTy;
218 
219   SVal A = getByName("a");
220   ASSERT_FALSE(A.getType(Context).isNull());
221   const auto *APtrTy = dyn_cast<PointerType>(A.getType(Context));
222   ASSERT_NE(APtrTy, nullptr);
223   EXPECT_EQ(Int, APtrTy->getPointeeType());
224 
225   SVal B = getByName("b");
226   ASSERT_FALSE(B.getType(Context).isNull());
227   const auto *BPtrTy = dyn_cast<PointerType>(B.getType(Context));
228   ASSERT_NE(BPtrTy, nullptr);
229   EXPECT_EQ(Int, BPtrTy->getPointeeType());
230 
231   SVal C = getByName("c");
232   ASSERT_FALSE(C.getType(Context).isNull());
233   const auto *CPtrTy = dyn_cast<PointerType>(C.getType(Context));
234   ASSERT_NE(CPtrTy, nullptr);
235   EXPECT_EQ(Int, CPtrTy->getPointeeType());
236 
237   SVal D = getByName("d");
238   ASSERT_FALSE(D.getType(Context).isNull());
239   const auto *DRefTy = dyn_cast<LValueReferenceType>(D.getType(Context));
240   ASSERT_NE(DRefTy, nullptr);
241   EXPECT_EQ(Int, DRefTy->getPointeeType());
242 
243   SVal E = getByName("e");
244   ASSERT_FALSE(E.getType(Context).isNull());
245   const auto *EPtrTy = dyn_cast<PointerType>(E.getType(Context));
246   ASSERT_NE(EPtrTy, nullptr);
247   EXPECT_EQ(Int, EPtrTy->getPointeeType());
248 
249   SVal F = getByName("f");
250   ASSERT_FALSE(F.getType(Context).isNull());
251   const auto *FPtrTy = dyn_cast<PointerType>(F.getType(Context));
252   ASSERT_NE(FPtrTy, nullptr);
253   EXPECT_EQ(Int, FPtrTy->getPointeeType());
254 }
255 
256 SVAL_TEST(GetCompoundType, R"(
257 struct TestStruct {
258   int a, b;
259 };
260 union TestUnion {
261   int a;
262   float b;
263   TestStruct c;
264 };
265 void foo(int x) {
266   int a[] = {1, x, 2};
267   TestStruct b = {x, 42};
268   TestUnion c = {42};
269   TestUnion d = {.c=b};
270 }
271 )") {
272   SVal A = getByName("a");
273   ASSERT_FALSE(A.getType(Context).isNull());
274   const auto *AArrayType = dyn_cast<ArrayType>(A.getType(Context));
275   ASSERT_NE(AArrayType, nullptr);
276   EXPECT_EQ(Context.IntTy, AArrayType->getElementType());
277 
278   SVal B = getByName("b");
279   ASSERT_FALSE(B.getType(Context).isNull());
280   const auto *BRecordType = dyn_cast<RecordType>(B.getType(Context));
281   ASSERT_NE(BRecordType, nullptr);
282   EXPECT_EQ("TestStruct", BRecordType->getDecl()->getName());
283 
284   SVal C = getByName("c");
285   ASSERT_FALSE(C.getType(Context).isNull());
286   const auto *CRecordType = dyn_cast<RecordType>(C.getType(Context));
287   ASSERT_NE(CRecordType, nullptr);
288   EXPECT_EQ("TestUnion", CRecordType->getDecl()->getName());
289 
290   auto D = getByName("d").getAs<nonloc::CompoundVal>();
291   ASSERT_TRUE(D.hasValue());
292   auto Begin = D->begin();
293   ASSERT_NE(D->end(), Begin);
294   ++Begin;
295   ASSERT_EQ(D->end(), Begin);
296   auto LD = D->begin()->getAs<nonloc::LazyCompoundVal>();
297   ASSERT_TRUE(LD.hasValue());
298   auto LDT = LD->getType(Context);
299   ASSERT_FALSE(LDT.isNull());
300   const auto *DRecordType = dyn_cast<RecordType>(LDT);
301   ASSERT_NE(DRecordType, nullptr);
302   EXPECT_EQ("TestStruct", DRecordType->getDecl()->getName());
303 }
304 
305 SVAL_TEST(GetStringType, R"(
306 void foo() {
307   const char *a = "Hello, world!";
308 }
309 )") {
310   SVal A = getByName("a");
311   ASSERT_FALSE(A.getType(Context).isNull());
312   const auto *APtrTy = dyn_cast<PointerType>(A.getType(Context));
313   ASSERT_NE(APtrTy, nullptr);
314   EXPECT_EQ(Context.CharTy, APtrTy->getPointeeType());
315 }
316 
317 SVAL_TEST(GetThisType, R"(
318 class TestClass {
319   void foo();
320 };
321 void TestClass::foo() {
322   const auto *a = this;
323 }
324 )") {
325   SVal A = getByName("a");
326   ASSERT_FALSE(A.getType(Context).isNull());
327   const auto *APtrTy = dyn_cast<PointerType>(A.getType(Context));
328   ASSERT_NE(APtrTy, nullptr);
329   const auto *ARecordType = dyn_cast<RecordType>(APtrTy->getPointeeType());
330   ASSERT_NE(ARecordType, nullptr);
331   EXPECT_EQ("TestClass", ARecordType->getDecl()->getName());
332 }
333 
334 SVAL_TEST(GetFunctionPtrType, R"(
335 void bar();
336 void foo() {
337   auto *a = &bar;
338 }
339 )") {
340   SVal A = getByName("a");
341   ASSERT_FALSE(A.getType(Context).isNull());
342   const auto *APtrTy = dyn_cast<PointerType>(A.getType(Context));
343   ASSERT_NE(APtrTy, nullptr);
344   ASSERT_TRUE(isa<FunctionProtoType>(APtrTy->getPointeeType()));
345 }
346 
347 SVAL_TEST(GetLabelType, R"(
348 void foo() {
349   entry:
350   void *a = &&entry;
351   char *b = (char *)&&entry;
352 }
353 )") {
354   SVal A = getByName("a");
355   ASSERT_FALSE(A.getType(Context).isNull());
356   EXPECT_EQ(Context.VoidPtrTy, A.getType(Context));
357 
358   SVal B = getByName("a");
359   ASSERT_FALSE(B.getType(Context).isNull());
360   // TODO: Change to CharTy when we support symbolic casts
361   EXPECT_EQ(Context.VoidPtrTy, B.getType(Context));
362 }
363 
364 } // namespace
365 } // namespace ento
366 } // namespace clang
367