brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.9 KiB · 68d2678 Raw
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