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: 50 ErrorNotPreventedFuncVisitor() 51 : NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough) {} 52 53 virtual PathDiagnosticPieceRef 54 maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R, 55 const ObjCMethodCall &Call, 56 const ExplodedNode *N) override { 57 return nullptr; 58 } 59 60 virtual PathDiagnosticPieceRef 61 maybeEmitNoteForCXXThis(PathSensitiveBugReport &R, 62 const CXXConstructorCall &Call, 63 const ExplodedNode *N) override { 64 return nullptr; 65 } 66 67 virtual PathDiagnosticPieceRef 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 77 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 mutable std::unique_ptr<BugType> BT; 86 87 public: 88 void checkPreCall(const CallEvent &Call, CheckerContext &C) const { 89 if (CallDescription{"preventError", 0}.matches(Call)) { 90 C.addTransition(C.getState()->set<ErrorPrevented>(true)); 91 return; 92 } 93 94 if (CallDescription{"allowError", 0}.matches(Call)) { 95 C.addTransition(C.getState()->set<ErrorPrevented>(false)); 96 return; 97 } 98 99 if (CallDescription{"error", 0}.matches(Call)) { 100 if (C.getState()->get<ErrorPrevented>()) 101 return; 102 const ExplodedNode *N = C.generateErrorNode(); 103 if (!N) 104 return; 105 if (!BT) 106 BT.reset(new BugType(this->getCheckerName(), "error()", 107 categories::SecurityError)); 108 auto R = 109 std::make_unique<PathSensitiveBugReport>(*BT, "error() called", N); 110 R->template addVisitor<Visitor>(); 111 C.emitReport(std::move(R)); 112 } 113 } 114 }; 115 116 } // namespace 117 } // namespace ento 118 } // namespace clang 119 120 //===----------------------------------------------------------------------===// 121 // Non-thorough analysis: only the state right before and right after the 122 // function call is checked for the difference in trait value. 123 //===----------------------------------------------------------------------===// 124 125 namespace clang { 126 namespace ento { 127 namespace { 128 129 class NonThoroughErrorNotPreventedFuncVisitor 130 : public ErrorNotPreventedFuncVisitor { 131 public: 132 virtual bool 133 wasModifiedInFunction(const ExplodedNode *CallEnterN, 134 const ExplodedNode *CallExitEndN) override { 135 return CallEnterN->getState()->get<ErrorPrevented>() != 136 CallExitEndN->getState()->get<ErrorPrevented>(); 137 } 138 }; 139 140 void addNonThoroughStatefulChecker(AnalysisASTConsumer &AnalysisConsumer, 141 AnalyzerOptions &AnOpts) { 142 AnOpts.CheckersAndPackages = {{"test.StatefulChecker", true}}; 143 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { 144 Registry 145 .addChecker<StatefulChecker<NonThoroughErrorNotPreventedFuncVisitor>>( 146 "test.StatefulChecker", "Description", ""); 147 }); 148 } 149 150 TEST(NoStateChangeFuncVisitor, NonThoroughFunctionAnalysis) { 151 std::string Diags; 152 EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"( 153 void error(); 154 void preventError(); 155 void allowError(); 156 157 void g() { 158 //preventError(); 159 } 160 161 void f() { 162 g(); 163 error(); 164 } 165 )", Diags)); 166 EXPECT_EQ(Diags, 167 "test.StatefulChecker: Calling 'g' | Returning without prevening " 168 "the error | Returning from 'g' | error() called\n"); 169 170 Diags.clear(); 171 172 EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"( 173 void error(); 174 void preventError(); 175 void allowError(); 176 177 void g() { 178 preventError(); 179 allowError(); 180 } 181 182 void f() { 183 g(); 184 error(); 185 } 186 )", Diags)); 187 EXPECT_EQ(Diags, 188 "test.StatefulChecker: Calling 'g' | Returning without prevening " 189 "the error | Returning from 'g' | error() called\n"); 190 191 Diags.clear(); 192 193 EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"( 194 void error(); 195 void preventError(); 196 void allowError(); 197 198 void g() { 199 preventError(); 200 } 201 202 void f() { 203 g(); 204 error(); 205 } 206 )", Diags)); 207 EXPECT_EQ(Diags, ""); 208 } 209 210 } // namespace 211 } // namespace ento 212 } // namespace clang 213 214 //===----------------------------------------------------------------------===// 215 // Thorough analysis: only the state right before and right after the 216 // function call is checked for the difference in trait value. 217 //===----------------------------------------------------------------------===// 218 219 namespace clang { 220 namespace ento { 221 namespace { 222 223 class ThoroughErrorNotPreventedFuncVisitor 224 : public ErrorNotPreventedFuncVisitor { 225 public: 226 virtual bool 227 wasModifiedBeforeCallExit(const ExplodedNode *CurrN, 228 const ExplodedNode *CallExitBeginN) override { 229 return CurrN->getState()->get<ErrorPrevented>() != 230 CallExitBeginN->getState()->get<ErrorPrevented>(); 231 } 232 }; 233 234 void addThoroughStatefulChecker(AnalysisASTConsumer &AnalysisConsumer, 235 AnalyzerOptions &AnOpts) { 236 AnOpts.CheckersAndPackages = {{"test.StatefulChecker", true}}; 237 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { 238 Registry.addChecker<StatefulChecker<ThoroughErrorNotPreventedFuncVisitor>>( 239 "test.StatefulChecker", "Description", ""); 240 }); 241 } 242 243 TEST(NoStateChangeFuncVisitor, ThoroughFunctionAnalysis) { 244 std::string Diags; 245 EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"( 246 void error(); 247 void preventError(); 248 void allowError(); 249 250 void g() { 251 //preventError(); 252 } 253 254 void f() { 255 g(); 256 error(); 257 } 258 )", Diags)); 259 EXPECT_EQ(Diags, 260 "test.StatefulChecker: Calling 'g' | Returning without prevening " 261 "the error | Returning from 'g' | error() called\n"); 262 263 Diags.clear(); 264 265 EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"( 266 void error(); 267 void preventError(); 268 void allowError(); 269 270 void g() { 271 preventError(); 272 allowError(); 273 } 274 275 void f() { 276 g(); 277 error(); 278 } 279 )", Diags)); 280 EXPECT_EQ(Diags, "test.StatefulChecker: error() called\n"); 281 282 Diags.clear(); 283 284 EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"( 285 void error(); 286 void preventError(); 287 void allowError(); 288 289 void g() { 290 preventError(); 291 } 292 293 void f() { 294 g(); 295 error(); 296 } 297 )", Diags)); 298 EXPECT_EQ(Diags, ""); 299 } 300 301 } // namespace 302 } // namespace ento 303 } // namespace clang 304