Viewing file: CheckerManager.h (26.31 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
//===- 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 <vector>
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; class NodeBuilderContext; class ObjCMethodCall; class RegionAndSymbolInvalidationTraits; class SVal; class SymbolReaper;
template <typename T> class CheckerFn;
template <typename RET, typename... Ps> class CheckerFn<RET(Ps...)> { 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<CheckerRegistryData> 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<std::string> plugins, ArrayRef<std::function<void(CheckerRegistry &)>> 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<std::string> 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<void ()>;
//===----------------------------------------------------------------------===// // Checker registration. //===----------------------------------------------------------------------===//
/// Used to register checkers. /// All arguments are automatically passed through to the checker /// constructor. /// /// \returns a pointer to the checker object. template <typename CHECKER, typename... AT> CHECKER *registerChecker(AT &&... Args) { CheckerTag tag = getTag<CHECKER>(); CheckerRef &ref = CheckerTags[tag]; assert(!ref && "Checker already registered, use getChecker!");
CHECKER *checker = new CHECKER(std::forward<AT>(Args)...); checker->Name = CurrentCheckerName; CheckerDtors.push_back(CheckerDtor(checker, destruct<CHECKER>)); CHECKER::_register(checker, *this); ref = checker; return checker; }
template <typename CHECKER> CHECKER *getChecker() { CheckerTag tag = getTag<CHECKER>(); assert(CheckerTags.count(tag) != 0 && "Requested checker is not registered! Maybe you should add it as a " "dependency in Checkers.td?"); return static_cast<CHECKER *>(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<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> 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<void (const Decl *, AnalysisManager&, BugReporter &)>;
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<void (const Stmt *, CheckerContext &)>;
using CheckObjCMessageFunc = CheckerFn<void (const ObjCMethodCall &, CheckerContext &)>;
using CheckCallFunc = CheckerFn<void (const CallEvent &, CheckerContext &)>;
using CheckLocationFunc = CheckerFn<void(SVal location, bool isLoad, const Stmt *S, CheckerContext &)>;
using CheckBindFunc = CheckerFn<void(SVal location, SVal val, const Stmt *S, CheckerContext &)>;
using CheckEndAnalysisFunc = CheckerFn<void (ExplodedGraph &, BugReporter &, ExprEngine &)>;
using CheckBeginFunctionFunc = CheckerFn<void (CheckerContext &)>;
using CheckEndFunctionFunc = CheckerFn<void (const ReturnStmt *, CheckerContext &)>;
using CheckBranchConditionFunc = CheckerFn<void (const Stmt *, CheckerContext &)>;
using CheckNewAllocatorFunc = CheckerFn<void(const CXXAllocatorCall &Call, CheckerContext &)>;
using CheckDeadSymbolsFunc = CheckerFn<void (SymbolReaper &, CheckerContext &)>;
using CheckLiveSymbolsFunc = CheckerFn<void (ProgramStateRef,SymbolReaper &)>;
using CheckRegionChangesFunc = CheckerFn<ProgramStateRef (ProgramStateRef, const InvalidatedSymbols *symbols, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, const CallEvent *Call)>;
using CheckPointerEscapeFunc = CheckerFn<ProgramStateRef (ProgramStateRef, const InvalidatedSymbols &Escaped, const CallEvent *Call, PointerEscapeKind Kind, RegionAndSymbolInvalidationTraits *ITraits)>;
using EvalAssumeFunc = CheckerFn<ProgramStateRef(ProgramStateRef, SVal cond, bool assumption)>;
using EvalCallFunc = CheckerFn<bool (const CallEvent &, CheckerContext &)>;
using CheckEndOfTranslationUnit = CheckerFn<void (const TranslationUnitDecl *, AnalysisManager &, BugReporter &)>;
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<void (const void *event)>;
template <typename EVENT> void _registerListenerForEvent(CheckEventFunc checkfn) { EventInfo &info = Events[&EVENT::Tag]; info.Checkers.push_back(checkfn); }
template <typename EVENT> void _registerDispatcherForEvent() { EventInfo &info = Events[&EVENT::Tag]; info.HasDispatcher = true; }
template <typename EVENT> 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 <typename CHECKER> static void destruct(void *obj) { delete static_cast<CHECKER *>(obj); }
template <typename T> static void *getTag() { static int tag; return &tag; }
llvm::DenseMap<CheckerTag, CheckerRef> CheckerTags;
std::vector<CheckerDtor> CheckerDtors;
struct DeclCheckerInfo { CheckDeclFunc CheckFn; HandlesDeclFunc IsForDeclFn; }; std::vector<DeclCheckerInfo> DeclCheckers;
std::vector<CheckDeclFunc> BodyCheckers;
using CachedDeclCheckers = SmallVector<CheckDeclFunc, 4>; using CachedDeclCheckersMapTy = llvm::DenseMap<unsigned, CachedDeclCheckers>; CachedDeclCheckersMapTy CachedDeclCheckersMap;
struct StmtCheckerInfo { CheckStmtFunc CheckFn; HandlesStmtFunc IsForStmtFn; bool IsPreVisit; }; std::vector<StmtCheckerInfo> StmtCheckers;
using CachedStmtCheckers = SmallVector<CheckStmtFunc, 4>; using CachedStmtCheckersMapTy = llvm::DenseMap<unsigned, CachedStmtCheckers>; 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<CheckObjCMessageFunc> & getObjCMessageCheckers(ObjCMessageVisitKind Kind) const;
std::vector<CheckObjCMessageFunc> PreObjCMessageCheckers; std::vector<CheckObjCMessageFunc> PostObjCMessageCheckers; std::vector<CheckObjCMessageFunc> ObjCMessageNilCheckers;
std::vector<CheckCallFunc> PreCallCheckers; std::vector<CheckCallFunc> PostCallCheckers;
std::vector<CheckLocationFunc> LocationCheckers;
std::vector<CheckBindFunc> BindCheckers;
std::vector<CheckEndAnalysisFunc> EndAnalysisCheckers;
std::vector<CheckBeginFunctionFunc> BeginFunctionCheckers; std::vector<CheckEndFunctionFunc> EndFunctionCheckers;
std::vector<CheckBranchConditionFunc> BranchConditionCheckers;
std::vector<CheckNewAllocatorFunc> NewAllocatorCheckers;
std::vector<CheckLiveSymbolsFunc> LiveSymbolsCheckers;
std::vector<CheckDeadSymbolsFunc> DeadSymbolsCheckers;
std::vector<CheckRegionChangesFunc> RegionChangesCheckers;
std::vector<CheckPointerEscapeFunc> PointerEscapeCheckers;
std::vector<EvalAssumeFunc> EvalAssumeCheckers;
std::vector<EvalCallFunc> EvalCallCheckers;
std::vector<CheckEndOfTranslationUnit> EndOfTranslationUnitCheckers;
struct EventInfo { SmallVector<CheckEventFunc, 4> Checkers; bool HasDispatcher = false;
EventInfo() = default; };
using EventsTy = llvm::DenseMap<EventTag, EventInfo>; EventsTy Events; };
} // namespace ento
} // namespace clang
#endif // LLVM_CLANG_STATICANALYZER_CORE_CHECKERMANAGER_H
|