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