301 lines · cpp
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-exception6//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 to33// another -- the checker sets the ErrorPrevented trait to true if34// 'preventError()' is called in the source code, and sets it to false if35// '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 to39// inlined functions that could have, but neglected to prevent the error.40//===----------------------------------------------------------------------===//41 42REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrorPrevented, bool)43 44namespace clang {45namespace ento {46namespace {47 48class ErrorNotPreventedFuncVisitor : public NoStateChangeFuncVisitor {49public:50 ErrorNotPreventedFuncVisitor()51 : NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough) {}52 53 virtual PathDiagnosticPieceRef54 maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,55 const ObjCMethodCall &Call,56 const ExplodedNode *N) override {57 return nullptr;58 }59 60 virtual PathDiagnosticPieceRef61 maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,62 const CXXConstructorCall &Call,63 const ExplodedNode *N) override {64 return nullptr;65 }66 67 virtual PathDiagnosticPieceRef68 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 83template <class Visitor>84class StatefulChecker : public Checker<check::PreCall> {85 const BugType BT{this, "error()", categories::SecurityError};86 87public:88 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} // namespace114} // namespace ento115} // namespace clang116 117//===----------------------------------------------------------------------===//118// Non-thorough analysis: only the state right before and right after the119// function call is checked for the difference in trait value.120//===----------------------------------------------------------------------===//121 122namespace clang {123namespace ento {124namespace {125 126class NonThoroughErrorNotPreventedFuncVisitor127 : public ErrorNotPreventedFuncVisitor {128public:129 virtual bool130 wasModifiedInFunction(const ExplodedNode *CallEnterN,131 const ExplodedNode *CallExitEndN) override {132 return CallEnterN->getState()->get<ErrorPrevented>() !=133 CallExitEndN->getState()->get<ErrorPrevented>();134 }135};136 137void addNonThoroughStatefulChecker(AnalysisASTConsumer &AnalysisConsumer,138 AnalyzerOptions &AnOpts) {139 AnOpts.CheckersAndPackages = {{"test.StatefulChecker", true}};140 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {141 Registry142 .addChecker<StatefulChecker<NonThoroughErrorNotPreventedFuncVisitor>>(143 "test.StatefulChecker", "MockDescription");144 });145}146 147TEST(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} // namespace208} // namespace ento209} // namespace clang210 211//===----------------------------------------------------------------------===//212// Thorough analysis: only the state right before and right after the213// function call is checked for the difference in trait value.214//===----------------------------------------------------------------------===//215 216namespace clang {217namespace ento {218namespace {219 220class ThoroughErrorNotPreventedFuncVisitor221 : public ErrorNotPreventedFuncVisitor {222public:223 virtual bool224 wasModifiedBeforeCallExit(const ExplodedNode *CurrN,225 const ExplodedNode *CallExitBeginN) override {226 return CurrN->getState()->get<ErrorPrevented>() !=227 CallExitBeginN->getState()->get<ErrorPrevented>();228 }229};230 231void 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", "MockDescription");237 });238}239 240TEST(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} // namespace299} // namespace ento300} // namespace clang301