//===- CheckerManager.h - Static Analyzer Checker Manager -------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Defines the Static Analyzer Checker Manager. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_STATICANALYZER_CORE_CHECKERMANAGER_H #define LLVM_CLANG_STATICANALYZER_CORE_CHECKERMANAGER_H #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LangOptions.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include namespace clang { class AnalyzerOptions; class CallExpr; class Decl; class LocationContext; class Stmt; class TranslationUnitDecl; namespace ento { class AnalysisManager; class CXXAllocatorCall; class BugReporter; class CallEvent; class CheckerBase; class CheckerContext; class CheckerRegistry; struct CheckerRegistryData; class ExplodedGraph; class ExplodedNode; class ExplodedNodeSet; class ExprEngine; struct EvalCallOptions; class MemRegion; struct NodeBuilderContext; class ObjCMethodCall; class RegionAndSymbolInvalidationTraits; class SVal; class SymbolReaper; template class CheckerFn; template class CheckerFn { using Func = RET (*)(void *, Ps...); Func Fn; public: CheckerBase *Checker; CheckerFn(CheckerBase *checker, Func fn) : Fn(fn), Checker(checker) {} RET operator()(Ps... ps) const { return Fn(Checker, ps...); } }; /// Describes the different reasons a pointer escapes /// during analysis. enum PointerEscapeKind { /// A pointer escapes due to binding its value to a location /// that the analyzer cannot track. PSK_EscapeOnBind, /// The pointer has been passed to a function call directly. PSK_DirectEscapeOnCall, /// The pointer has been passed to a function indirectly. /// For example, the pointer is accessible through an /// argument to a function. PSK_IndirectEscapeOnCall, /// Escape for a new symbol that was generated into a region /// that the analyzer cannot follow during a conservative call. PSK_EscapeOutParameters, /// The reason for pointer escape is unknown. For example, /// a region containing this pointer is invalidated. PSK_EscapeOther }; /// This wrapper is used to ensure that only StringRefs originating from the /// CheckerRegistry are used as check names. We want to make sure all checker /// name strings have a lifetime that keeps them alive at least until the path /// diagnostics have been processed, since they are expected to be constexpr /// string literals (most likely generated by TblGen). class CheckerNameRef { friend class ::clang::ento::CheckerRegistry; StringRef Name; explicit CheckerNameRef(StringRef Name) : Name(Name) {} public: CheckerNameRef() = default; StringRef getName() const { return Name; } operator StringRef() const { return Name; } }; enum class ObjCMessageVisitKind { Pre, Post, MessageNil }; class CheckerManager { ASTContext *Context = nullptr; const LangOptions LangOpts; const AnalyzerOptions &AOptions; const Preprocessor *PP = nullptr; CheckerNameRef CurrentCheckerName; DiagnosticsEngine &Diags; std::unique_ptr RegistryData; public: // These constructors are defined in the Frontend library, because // CheckerRegistry, a crucial component of the initialization is in there. // CheckerRegistry cannot be moved to the Core library, because the checker // registration functions are defined in the Checkers library, and the library // dependencies look like this: Core -> Checkers -> Frontend. CheckerManager( ASTContext &Context, AnalyzerOptions &AOptions, const Preprocessor &PP, ArrayRef plugins, ArrayRef> checkerRegistrationFns); /// Constructs a CheckerManager that ignores all non TblGen-generated /// checkers. Useful for unit testing, unless the checker infrastructure /// itself is tested. CheckerManager(ASTContext &Context, AnalyzerOptions &AOptions, const Preprocessor &PP) : CheckerManager(Context, AOptions, PP, {}, {}) {} /// Constructs a CheckerManager without requiring an AST. No checker /// registration will take place. Only useful when one needs to print the /// help flags through CheckerRegistryData, and the AST is unavailable. CheckerManager(AnalyzerOptions &AOptions, const LangOptions &LangOpts, DiagnosticsEngine &Diags, ArrayRef plugins); ~CheckerManager(); void setCurrentCheckerName(CheckerNameRef name) { CurrentCheckerName = name; } CheckerNameRef getCurrentCheckerName() const { return CurrentCheckerName; } bool hasPathSensitiveCheckers() const; void finishedCheckerRegistration(); const LangOptions &getLangOpts() const { return LangOpts; } const AnalyzerOptions &getAnalyzerOptions() const { return AOptions; } const Preprocessor &getPreprocessor() const { assert(PP); return *PP; } const CheckerRegistryData &getCheckerRegistryData() const { return *RegistryData; } DiagnosticsEngine &getDiagnostics() const { return Diags; } ASTContext &getASTContext() const { assert(Context); return *Context; } /// Emits an error through a DiagnosticsEngine about an invalid user supplied /// checker option value. void reportInvalidCheckerOptionValue(const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) const; using CheckerRef = CheckerBase *; using CheckerTag = const void *; using CheckerDtor = CheckerFn; //===----------------------------------------------------------------------===// // Checker registration. //===----------------------------------------------------------------------===// /// Used to register checkers. /// All arguments are automatically passed through to the checker /// constructor. /// /// \returns a pointer to the checker object. template CHECKER *registerChecker(AT &&... Args) { CheckerTag tag = getTag(); CheckerRef &ref = CheckerTags[tag]; assert(!ref && "Checker already registered, use getChecker!"); CHECKER *checker = new CHECKER(std::forward(Args)...); checker->Name = CurrentCheckerName; CheckerDtors.push_back(CheckerDtor(checker, destruct)); CHECKER::_register(checker, *this); ref = checker; return checker; } template CHECKER *getChecker() { CheckerTag tag = getTag(); assert(CheckerTags.count(tag) != 0 && "Requested checker is not registered! Maybe you should add it as a " "dependency in Checkers.td?"); return static_cast(CheckerTags[tag]); } //===----------------------------------------------------------------------===// // Functions for running checkers for AST traversing. //===----------------------------------------------------------------------===// /// Run checkers handling Decls. void runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr, BugReporter &BR); /// Run checkers handling Decls containing a Stmt body. void runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR); //===----------------------------------------------------------------------===// // Functions for running checkers for path-sensitive checking. //===----------------------------------------------------------------------===// /// Run checkers for pre-visiting Stmts. /// /// The notification is performed for every explored CFGElement, which does /// not include the control flow statements such as IfStmt. /// /// \sa runCheckersForBranchCondition, runCheckersForPostStmt void runCheckersForPreStmt(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const Stmt *S, ExprEngine &Eng) { runCheckersForStmt(/*isPreVisit=*/true, Dst, Src, S, Eng); } /// Run checkers for post-visiting Stmts. /// /// The notification is performed for every explored CFGElement, which does /// not include the control flow statements such as IfStmt. /// /// \sa runCheckersForBranchCondition, runCheckersForPreStmt void runCheckersForPostStmt(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const Stmt *S, ExprEngine &Eng, bool wasInlined = false) { runCheckersForStmt(/*isPreVisit=*/false, Dst, Src, S, Eng, wasInlined); } /// Run checkers for visiting Stmts. void runCheckersForStmt(bool isPreVisit, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const Stmt *S, ExprEngine &Eng, bool wasInlined = false); /// Run checkers for pre-visiting obj-c messages. void runCheckersForPreObjCMessage(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const ObjCMethodCall &msg, ExprEngine &Eng) { runCheckersForObjCMessage(ObjCMessageVisitKind::Pre, Dst, Src, msg, Eng); } /// Run checkers for post-visiting obj-c messages. void runCheckersForPostObjCMessage(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const ObjCMethodCall &msg, ExprEngine &Eng, bool wasInlined = false) { runCheckersForObjCMessage(ObjCMessageVisitKind::Post, Dst, Src, msg, Eng, wasInlined); } /// Run checkers for visiting an obj-c message to nil. void runCheckersForObjCMessageNil(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const ObjCMethodCall &msg, ExprEngine &Eng) { runCheckersForObjCMessage(ObjCMessageVisitKind::MessageNil, Dst, Src, msg, Eng); } /// Run checkers for visiting obj-c messages. void runCheckersForObjCMessage(ObjCMessageVisitKind visitKind, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const ObjCMethodCall &msg, ExprEngine &Eng, bool wasInlined = false); /// Run checkers for pre-visiting obj-c messages. void runCheckersForPreCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &Call, ExprEngine &Eng) { runCheckersForCallEvent(/*isPreVisit=*/true, Dst, Src, Call, Eng); } /// Run checkers for post-visiting obj-c messages. void runCheckersForPostCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &Call, ExprEngine &Eng, bool wasInlined = false) { runCheckersForCallEvent(/*isPreVisit=*/false, Dst, Src, Call, Eng, wasInlined); } /// Run checkers for visiting obj-c messages. void runCheckersForCallEvent(bool isPreVisit, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &Call, ExprEngine &Eng, bool wasInlined = false); /// Run checkers for load/store of a location. void runCheckersForLocation(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, bool isLoad, const Stmt *NodeEx, const Stmt *BoundEx, ExprEngine &Eng); /// Run checkers for binding of a value to a location. void runCheckersForBind(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, SVal val, const Stmt *S, ExprEngine &Eng, const ProgramPoint &PP); /// Run checkers for end of analysis. void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng); /// Run checkers on beginning of function. void runCheckersForBeginFunction(ExplodedNodeSet &Dst, const BlockEdge &L, ExplodedNode *Pred, ExprEngine &Eng); /// Run checkers on end of function. void runCheckersForEndFunction(NodeBuilderContext &BC, ExplodedNodeSet &Dst, ExplodedNode *Pred, ExprEngine &Eng, const ReturnStmt *RS); /// Run checkers for branch condition. void runCheckersForBranchCondition(const Stmt *condition, ExplodedNodeSet &Dst, ExplodedNode *Pred, ExprEngine &Eng); /// Run checkers between C++ operator new and constructor calls. void runCheckersForNewAllocator(const CXXAllocatorCall &Call, ExplodedNodeSet &Dst, ExplodedNode *Pred, ExprEngine &Eng, bool wasInlined = false); /// Run checkers for live symbols. /// /// Allows modifying SymbolReaper object. For example, checkers can explicitly /// register symbols of interest as live. These symbols will not be marked /// dead and removed. void runCheckersForLiveSymbols(ProgramStateRef state, SymbolReaper &SymReaper); /// Run checkers for dead symbols. /// /// Notifies checkers when symbols become dead. For example, this allows /// checkers to aggressively clean up/reduce the checker state and produce /// precise diagnostics. void runCheckersForDeadSymbols(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SymbolReaper &SymReaper, const Stmt *S, ExprEngine &Eng, ProgramPoint::Kind K); /// Run checkers for region changes. /// /// This corresponds to the check::RegionChanges callback. /// \param state The current program state. /// \param invalidated A set of all symbols potentially touched by the change. /// \param ExplicitRegions The regions explicitly requested for invalidation. /// For example, in the case of a function call, these would be arguments. /// \param Regions The transitive closure of accessible regions, /// i.e. all regions that may have been touched by this change. /// \param Call The call expression wrapper if the regions are invalidated /// by a call. ProgramStateRef runCheckersForRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, ArrayRef ExplicitRegions, ArrayRef Regions, const LocationContext *LCtx, const CallEvent *Call); /// Run checkers when pointers escape. /// /// This notifies the checkers about pointer escape, which occurs whenever /// the analyzer cannot track the symbol any more. For example, as a /// result of assigning a pointer into a global or when it's passed to a /// function call the analyzer cannot model. /// /// \param State The state at the point of escape. /// \param Escaped The list of escaped symbols. /// \param Call The corresponding CallEvent, if the symbols escape as /// parameters to the given call. /// \param Kind The reason of pointer escape. /// \param ITraits Information about invalidation for a particular /// region/symbol. /// \returns Checkers can modify the state by returning a new one. ProgramStateRef runCheckersForPointerEscape(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, PointerEscapeKind Kind, RegionAndSymbolInvalidationTraits *ITraits); /// Run checkers for handling assumptions on symbolic values. ProgramStateRef runCheckersForEvalAssume(ProgramStateRef state, SVal Cond, bool Assumption); /// Run checkers for evaluating a call. /// /// Warning: Currently, the CallEvent MUST come from a CallExpr! void runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &CE, ExprEngine &Eng, const EvalCallOptions &CallOpts); /// Run checkers for the entire Translation Unit. void runCheckersOnEndOfTranslationUnit(const TranslationUnitDecl *TU, AnalysisManager &mgr, BugReporter &BR); /// Run checkers for debug-printing a ProgramState. /// /// Unlike most other callbacks, any checker can simply implement the virtual /// method CheckerBase::printState if it has custom data to print. /// /// \param Out The output stream /// \param State The state being printed /// \param NL The preferred representation of a newline. /// \param Space The preferred space between the left side and the message. /// \param IsDot Whether the message will be printed in 'dot' format. void runCheckersForPrintStateJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n", unsigned int Space = 0, bool IsDot = false) const; //===----------------------------------------------------------------------===// // Internal registration functions for AST traversing. //===----------------------------------------------------------------------===// // Functions used by the registration mechanism, checkers should not touch // these directly. using CheckDeclFunc = CheckerFn; using HandlesDeclFunc = bool (*)(const Decl *D); void _registerForDecl(CheckDeclFunc checkfn, HandlesDeclFunc isForDeclFn); void _registerForBody(CheckDeclFunc checkfn); //===----------------------------------------------------------------------===// // Internal registration functions for path-sensitive checking. //===----------------------------------------------------------------------===// using CheckStmtFunc = CheckerFn; using CheckObjCMessageFunc = CheckerFn; using CheckCallFunc = CheckerFn; using CheckLocationFunc = CheckerFn; using CheckBindFunc = CheckerFn; using CheckEndAnalysisFunc = CheckerFn; using CheckBeginFunctionFunc = CheckerFn; using CheckEndFunctionFunc = CheckerFn; using CheckBranchConditionFunc = CheckerFn; using CheckNewAllocatorFunc = CheckerFn; using CheckDeadSymbolsFunc = CheckerFn; using CheckLiveSymbolsFunc = CheckerFn; using CheckRegionChangesFunc = CheckerFn ExplicitRegions, ArrayRef Regions, const LocationContext *LCtx, const CallEvent *Call)>; using CheckPointerEscapeFunc = CheckerFn; using EvalAssumeFunc = CheckerFn; using EvalCallFunc = CheckerFn; using CheckEndOfTranslationUnit = CheckerFn; using HandlesStmtFunc = bool (*)(const Stmt *D); void _registerForPreStmt(CheckStmtFunc checkfn, HandlesStmtFunc isForStmtFn); void _registerForPostStmt(CheckStmtFunc checkfn, HandlesStmtFunc isForStmtFn); void _registerForPreObjCMessage(CheckObjCMessageFunc checkfn); void _registerForPostObjCMessage(CheckObjCMessageFunc checkfn); void _registerForObjCMessageNil(CheckObjCMessageFunc checkfn); void _registerForPreCall(CheckCallFunc checkfn); void _registerForPostCall(CheckCallFunc checkfn); void _registerForLocation(CheckLocationFunc checkfn); void _registerForBind(CheckBindFunc checkfn); void _registerForEndAnalysis(CheckEndAnalysisFunc checkfn); void _registerForBeginFunction(CheckBeginFunctionFunc checkfn); void _registerForEndFunction(CheckEndFunctionFunc checkfn); void _registerForBranchCondition(CheckBranchConditionFunc checkfn); void _registerForNewAllocator(CheckNewAllocatorFunc checkfn); void _registerForLiveSymbols(CheckLiveSymbolsFunc checkfn); void _registerForDeadSymbols(CheckDeadSymbolsFunc checkfn); void _registerForRegionChanges(CheckRegionChangesFunc checkfn); void _registerForPointerEscape(CheckPointerEscapeFunc checkfn); void _registerForConstPointerEscape(CheckPointerEscapeFunc checkfn); void _registerForEvalAssume(EvalAssumeFunc checkfn); void _registerForEvalCall(EvalCallFunc checkfn); void _registerForEndOfTranslationUnit(CheckEndOfTranslationUnit checkfn); //===----------------------------------------------------------------------===// // Internal registration functions for events. //===----------------------------------------------------------------------===// using EventTag = void *; using CheckEventFunc = CheckerFn; template void _registerListenerForEvent(CheckEventFunc checkfn) { EventInfo &info = Events[&EVENT::Tag]; info.Checkers.push_back(checkfn); } template void _registerDispatcherForEvent() { EventInfo &info = Events[&EVENT::Tag]; info.HasDispatcher = true; } template void _dispatchEvent(const EVENT &event) const { EventsTy::const_iterator I = Events.find(&EVENT::Tag); if (I == Events.end()) return; const EventInfo &info = I->second; for (const auto &Checker : info.Checkers) Checker(&event); } //===----------------------------------------------------------------------===// // Implementation details. //===----------------------------------------------------------------------===// private: template static void destruct(void *obj) { delete static_cast(obj); } template static void *getTag() { static int tag; return &tag; } llvm::DenseMap CheckerTags; std::vector CheckerDtors; struct DeclCheckerInfo { CheckDeclFunc CheckFn; HandlesDeclFunc IsForDeclFn; }; std::vector DeclCheckers; std::vector BodyCheckers; using CachedDeclCheckers = SmallVector; using CachedDeclCheckersMapTy = llvm::DenseMap; CachedDeclCheckersMapTy CachedDeclCheckersMap; struct StmtCheckerInfo { CheckStmtFunc CheckFn; HandlesStmtFunc IsForStmtFn; bool IsPreVisit; }; std::vector StmtCheckers; using CachedStmtCheckers = SmallVector; using CachedStmtCheckersMapTy = llvm::DenseMap; CachedStmtCheckersMapTy CachedStmtCheckersMap; const CachedStmtCheckers &getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit); /// Returns the checkers that have registered for callbacks of the /// given \p Kind. const std::vector & getObjCMessageCheckers(ObjCMessageVisitKind Kind) const; std::vector PreObjCMessageCheckers; std::vector PostObjCMessageCheckers; std::vector ObjCMessageNilCheckers; std::vector PreCallCheckers; std::vector PostCallCheckers; std::vector LocationCheckers; std::vector BindCheckers; std::vector EndAnalysisCheckers; std::vector BeginFunctionCheckers; std::vector EndFunctionCheckers; std::vector BranchConditionCheckers; std::vector NewAllocatorCheckers; std::vector LiveSymbolsCheckers; std::vector DeadSymbolsCheckers; std::vector RegionChangesCheckers; std::vector PointerEscapeCheckers; std::vector EvalAssumeCheckers; std::vector EvalCallCheckers; std::vector EndOfTranslationUnitCheckers; struct EventInfo { SmallVector Checkers; bool HasDispatcher = false; EventInfo() = default; }; using EventsTy = llvm::DenseMap; EventsTy Events; }; } // namespace ento } // namespace clang #endif // LLVM_CLANG_STATICANALYZER_CORE_CHECKERMANAGER_H