diff options
Diffstat (limited to 'clang-r353983/include/clang/StaticAnalyzer/Core')
53 files changed, 16518 insertions, 0 deletions
diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/Analyses.def b/clang-r353983/include/clang/StaticAnalyzer/Core/Analyses.def new file mode 100644 index 00000000..37745157 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/Analyses.def @@ -0,0 +1,59 @@ +//===-- Analyses.def - Metadata about Static Analyses -----------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the set of static analyses used by AnalysisConsumer. +// +//===----------------------------------------------------------------------===// + +#ifndef ANALYSIS_STORE +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) +#endif + +ANALYSIS_STORE(RegionStore, "region", "Use region-based analyzer store", CreateRegionStoreManager) + +#ifndef ANALYSIS_CONSTRAINTS +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) +#endif + +ANALYSIS_CONSTRAINTS(RangeConstraints, "range", "Use constraint tracking of concrete value ranges", CreateRangeConstraintManager) +ANALYSIS_CONSTRAINTS(Z3Constraints, "z3", "Use Z3 contraint solver", CreateZ3ConstraintManager) + +#ifndef ANALYSIS_DIAGNOSTICS +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) +#endif + +ANALYSIS_DIAGNOSTICS(HTML, "html", "Output analysis results using HTML", createHTMLDiagnosticConsumer) +ANALYSIS_DIAGNOSTICS(HTML_SINGLE_FILE, "html-single-file", "Output analysis results using HTML (not allowing for multi-file bugs)", createHTMLSingleFileDiagnosticConsumer) +ANALYSIS_DIAGNOSTICS(PLIST, "plist", "Output analysis results using Plists", createPlistDiagnosticConsumer) +ANALYSIS_DIAGNOSTICS(PLIST_MULTI_FILE, "plist-multi-file", "Output analysis results using Plists (allowing for multi-file bugs)", createPlistMultiFileDiagnosticConsumer) +ANALYSIS_DIAGNOSTICS(PLIST_HTML, "plist-html", "Output analysis results using HTML wrapped with Plists", createPlistHTMLDiagnosticConsumer) +ANALYSIS_DIAGNOSTICS(SARIF, "sarif", "Output analysis results in a SARIF file", createSarifDiagnosticConsumer) +ANALYSIS_DIAGNOSTICS(TEXT, "text", "Text output of analysis results", createTextPathDiagnosticConsumer) + +#ifndef ANALYSIS_PURGE +#define ANALYSIS_PURGE(NAME, CMDFLAG, DESC) +#endif + +ANALYSIS_PURGE(PurgeStmt, "statement", "Purge symbols, bindings, and constraints before every statement") +ANALYSIS_PURGE(PurgeBlock, "block", "Purge symbols, bindings, and constraints before every basic block") +ANALYSIS_PURGE(PurgeNone, "none", "Do not purge symbols, bindings, or constraints") + +#ifndef ANALYSIS_INLINING_MODE +#define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC) +#endif + +ANALYSIS_INLINING_MODE(All, "all", "Analyze all functions as top level") +ANALYSIS_INLINING_MODE(NoRedundancy, "noredundancy", "Do not analyze a function which has been previously inlined") + +#undef ANALYSIS_STORE +#undef ANALYSIS_CONSTRAINTS +#undef ANALYSIS_DIAGNOSTICS +#undef ANALYSIS_PURGE +#undef ANALYSIS_INLINING_MODE +#undef ANALYSIS_IPA + diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang-r353983/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def new file mode 100644 index 00000000..cc8b70bc --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -0,0 +1,376 @@ +//===-- AnalyzerOptions.def - Metadata about Static Analyses ----*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the analyzer options avaible with -analyzer-config. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_STRINGREF_H +#error This .def file is expected to be included in translation units where \ +"llvm/ADT/StringRef.h" is already included! +#endif + +#ifdef ANALYZER_OPTION +#ifndef ANALYZER_OPTION_DEPENDS_ON_USER_MODE +#error If you didnt include this file with the intent of generating methods, \ +define both 'ANALYZER_OPTION' and 'ANALYZER_OPTION_DEPENDS_ON_USER_MODE' macros! +#endif +#endif + +#ifndef ANALYZER_OPTION_DEPENDS_ON_USER_MODE +#ifdef ANALYZER_OPTION +#error If you didnt include this file with the intent of generating methods, \ +define both 'ANALYZER_OPTION' and 'ANALYZER_OPTION_DEPENDS_ON_USER_MODE' macros! +#endif +#endif + +#ifndef ANALYZER_OPTION +/// Create a new analyzer option, but dont generate a method for it in +/// AnalyzerOptions. +/// +/// TYPE - The type of the option object that will be stored in +/// AnalyzerOptions. This file is expected to be icluded in translation +/// units where AnalyzerOptions.h is included, so types from that +/// header should be used. +/// NAME - The name of the option object. +/// CMDFLAG - The command line flag for the option. +/// (-analyzer-config CMDFLAG=VALUE) +/// DESC - Description of the flag. +/// DEFAULT_VAL - The default value for CMDFLAG. +#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) +#endif + +#ifndef ANALYZER_OPTION_DEPENDS_ON_USER_MODE +/// Create a new analyzer option, but dont generate a method for it in +/// AnalyzerOptions. It's value depends on the option "user-mode". +/// +/// TYPE - The type of the option object that will be stored in +/// AnalyzerOptions. This file is expected to be icluded in translation +/// units where AnalyzerOptions.h is included, so types from that +/// header should be used. +/// NAME - The name of the option object. +/// CMDFLAG - The command line flag for the option. +/// (-analyzer-config CMDFLAG=VALUE) +/// DESC - Description of the flag. +/// SHALLOW_VAL - The default value for CMDFLAG, when "user-mode" was set to +/// "shallow". +/// DEEP_VAL - The default value for CMDFLAG, when "user-mode" was set to +/// "deep". +#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ + SHALLOW_VAL, DEEP_VAL) +#endif + +//===----------------------------------------------------------------------===// +// The "mode" option. Since some options depend on this, we list it on top of +// this file in order to make sure that the generated field for it is +// initialized before the rest. +//===----------------------------------------------------------------------===// + +ANALYZER_OPTION( + StringRef, UserMode, "mode", + "(string) Controls the high-level analyzer mode, which influences the " + "default settings for some of the lower-level config options (such as " + "IPAMode). Value: \"deep\", \"shallow\".", + "deep") + +//===----------------------------------------------------------------------===// +// Boolean analyzer options. +//===----------------------------------------------------------------------===// + +ANALYZER_OPTION(bool, ShouldIncludeImplicitDtorsInCFG, "cfg-implicit-dtors", + "Whether or not implicit destructors for C++ objects " + "should be included in the CFG.", + true) + +ANALYZER_OPTION(bool, ShouldIncludeTemporaryDtorsInCFG, "cfg-temporary-dtors", + "Whether or not the destructors for C++ temporary " + "objects should be included in the CFG.", + true) + +ANALYZER_OPTION( + bool, ShouldIncludeLifetimeInCFG, "cfg-lifetime", + "Whether or not end-of-lifetime information should be included in the CFG.", + false) + +ANALYZER_OPTION(bool, ShouldIncludeLoopExitInCFG, "cfg-loopexit", + "Whether or not the end of the loop information should " + "be included in the CFG.", + false) + +ANALYZER_OPTION(bool, ShouldIncludeRichConstructorsInCFG, + "cfg-rich-constructors", + "Whether or not construction site information should be " + "included in the CFG C++ constructor elements.", + true) + +ANALYZER_OPTION( + bool, ShouldIncludeScopesInCFG, "cfg-scopes", + "Whether or not scope information should be included in the CFG.", false) + +ANALYZER_OPTION( + bool, MayInlineTemplateFunctions, "c++-template-inlining", + "Whether or not templated functions may be considered for inlining.", true) + +ANALYZER_OPTION(bool, MayInlineCXXStandardLibrary, "c++-stdlib-inlining", + "Whether or not C++ standard library functions may be " + "considered for inlining.", + true) + +ANALYZER_OPTION(bool, MayInlineCXXAllocator, "c++-allocator-inlining", + "Whether or not allocator call may be considered for inlining.", + true) + +ANALYZER_OPTION( + bool, MayInlineCXXSharedPtrDtor, "c++-shared_ptr-inlining", + "Whether or not the destructor of C++ 'shared_ptr' may be considered for " + "inlining. This covers std::shared_ptr, std::tr1::shared_ptr, and " + "boost::shared_ptr, and indeed any destructor named '~shared_ptr'.", + false) + +ANALYZER_OPTION(bool, MayInlineCXXTemporaryDtors, "c++-temp-dtor-inlining", + "Whether C++ temporary destructors should be inlined " + "during analysis. If temporary destructors are disabled " + "in the CFG via the 'cfg-temporary-dtors' option, " + "temporary destructors would not be inlined anyway.", + true) + +ANALYZER_OPTION( + bool, ShouldSuppressNullReturnPaths, "suppress-null-return-paths", + "Whether or not paths that go through null returns should be suppressed. " + "This is a heuristic for avoiding bug reports with paths that go through " + "inlined functions that are more defensive than their callers.", + true) + +ANALYZER_OPTION( + bool, ShouldAvoidSuppressingNullArgumentPaths, + "avoid-suppressing-null-argument-paths", + "Whether a bug report should not be suppressed if its path includes a call " + "with a null argument, even if that call has a null return. This option " + "has no effect when ShouldSuppressNullReturnPaths is false. This is a " + "counter-heuristic to avoid false negatives.", + false) + +ANALYZER_OPTION(bool, ShouldSuppressInlinedDefensiveChecks, + "suppress-inlined-defensive-checks", + "Whether or not diagnostics containing inlined " + "defensive NULL checks should be suppressed.", + true) + +ANALYZER_OPTION(bool, MayInlineCXXContainerMethods, "c++-container-inlining", + "Whether or not methods of C++ container objects may be " + "considered for inlining.", + false) + +ANALYZER_OPTION(bool, ShouldSuppressFromCXXStandardLibrary, + "suppress-c++-stdlib", + "Whether or not diagnostics reported within the C++ " + "standard library should be suppressed.", + true) + +ANALYZER_OPTION(bool, ShouldCrosscheckWithZ3, "crosscheck-with-z3", + "Whether bug reports should be crosschecked with the Z3 " + "constraint manager backend.", + false) + +ANALYZER_OPTION(bool, ShouldReportIssuesInMainSourceFile, + "report-in-main-source-file", + "Whether or not the diagnostic report should be always " + "reported in the main source file and not the headers.", + false) + +ANALYZER_OPTION(bool, ShouldWriteStableReportFilename, "stable-report-filename", + "Whether or not the report filename should be random or not.", + false) + +ANALYZER_OPTION( + bool, ShouldSerializeStats, "serialize-stats", + "Whether the analyzer should serialize statistics to plist output. " + "Statistics would be serialized in JSON format inside the main dictionary " + "under the statistics key. Available only if compiled in assert mode or " + "with LLVM statistics explicitly enabled.", + false) + +ANALYZER_OPTION(bool, MayInlineObjCMethod, "objc-inlining", + "Whether ObjectiveC inlining is enabled, false otherwise.", + true) + +ANALYZER_OPTION(bool, ShouldPrunePaths, "prune-paths", + "Whether irrelevant parts of a bug report path should " + "be pruned out of the final output.", + true) + +ANALYZER_OPTION( + bool, ShouldConditionalizeStaticInitializers, + "cfg-conditional-static-initializers", + "Whether 'static' initializers should be in conditional logic in the CFG.", + true) + +ANALYZER_OPTION(bool, ShouldSynthesizeBodies, "faux-bodies", + "Whether the analyzer engine should synthesize fake " + "bodies for well-known functions.", + true) + +ANALYZER_OPTION( + bool, ShouldElideConstructors, "elide-constructors", + "Whether elidable C++ copy-constructors and move-constructors should be " + "actually elided during analysis. Both behaviors are allowed by the C++ " + "standard, and the analyzer, like CodeGen, defaults to eliding. Starting " + "with C++17 some elisions become mandatory, and in these cases the option " + "will be ignored.", + true) + +ANALYZER_OPTION( + bool, ShouldInlineLambdas, "inline-lambdas", + "Whether lambdas should be inlined. Otherwise a sink node will be " + "generated each time a LambdaExpr is visited.", + true) + +ANALYZER_OPTION(bool, ShouldWidenLoops, "widen-loops", + "Whether the analysis should try to widen loops.", false) + +ANALYZER_OPTION( + bool, ShouldUnrollLoops, "unroll-loops", + "Whether the analysis should try to unroll loops with known bounds.", false) + +ANALYZER_OPTION( + bool, ShouldDisplayNotesAsEvents, "notes-as-events", + "Whether the bug reporter should transparently treat extra note diagnostic " + "pieces as event diagnostic pieces. Useful when the diagnostic consumer " + "doesn't support the extra note pieces.", + false) + +ANALYZER_OPTION( + bool, ShouldAggressivelySimplifyBinaryOperation, + "aggressive-binary-operation-simplification", + "Whether SValBuilder should rearrange comparisons and additive operations " + "of symbolic expressions which consist of a sum of a symbol and a concrete " + "integer into the format where symbols are on the left-hand side and the " + "integer is on the right. This is only done if both symbols and both " + "concrete integers are signed, greater than or equal to the quarter of the " + "minimum value of the type and less than or equal to the quarter of the " + "maximum value of that type. A + n <OP> B + m becomes A - B <OP> m - n, " + "where A and B symbolic, n and m are integers. <OP> is any of '==', '!=', " + "'<', '<=', '>', '>=', '+' or '-'. The rearrangement also happens with '-' " + "instead of '+' on either or both side and also if any or both integers " + "are missing.", + false) + +ANALYZER_OPTION( + bool, ShouldEagerlyAssume, "eagerly-assume", + "Whether we should eagerly assume evaluations of conditionals, thus, " + "bifurcating the path. This indicates how the engine should handle " + "expressions such as: 'x = (y != 0)'. When this is true then the " + "subexpression 'y != 0' will be eagerly assumed to be true or false, thus " + "evaluating it to the integers 0 or 1 respectively. The upside is that " + "this can increase analysis precision until we have a better way to lazily " + "evaluate such logic. The downside is that it eagerly bifurcates paths.", + true) + +ANALYZER_OPTION( + bool, IsNaiveCTUEnabled, "experimental-enable-naive-ctu-analysis", + "Whether naive cross translation unit analysis is enabled. This is an " + "experimental feature to inline functions from other translation units.", + false) + +ANALYZER_OPTION(bool, ShouldDisplayMacroExpansions, "expand-macros", + "Whether macros related to the bugpath should be " + "expanded and included in the plist output.", + false) + +ANALYZER_OPTION(bool, DisplayCTUProgress, "display-ctu-progress", + "Whether to emit verbose output about " + "the analyzer's progress related to ctu.", + false) + +//===----------------------------------------------------------------------===// +// Unsinged analyzer options. +//===----------------------------------------------------------------------===// + +ANALYZER_OPTION( + unsigned, AlwaysInlineSize, "ipa-always-inline-size", + "The size of the functions (in basic blocks), which should be considered " + "to be small enough to always inline.", + 3) + +ANALYZER_OPTION( + unsigned, GraphTrimInterval, "graph-trim-interval", + "How often nodes in the ExplodedGraph should be recycled to save memory. " + "To disable node reclamation, set the option to 0.", + 1000) + +ANALYZER_OPTION( + unsigned, MinCFGSizeTreatFunctionsAsLarge, + "min-cfg-size-treat-functions-as-large", + "The number of basic blocks a function needs to have to be considered " + "large for the 'max-times-inline-large' config option.", + 14) + +ANALYZER_OPTION(unsigned, MaxSymbolComplexity, "max-symbol-complexity", + "The maximum complexity of symbolic constraint.", 35) + +ANALYZER_OPTION(unsigned, MaxTimesInlineLarge, "max-times-inline-large", + "The maximum times a large function could be inlined.", 32) + +ANALYZER_OPTION_DEPENDS_ON_USER_MODE( + unsigned, MaxInlinableSize, "max-inlinable-size", + "The bound on the number of basic blocks in an inlined function.", + /* SHALLOW_VAL */ 4, /* DEEP_VAL */ 100) + +ANALYZER_OPTION_DEPENDS_ON_USER_MODE( + unsigned, MaxNodesPerTopLevelFunction, "max-nodes", + "The maximum number of nodes the analyzer can generate while exploring a " + "top level function (for each exploded graph). 0 means no limit.", + /* SHALLOW_VAL */ 75000, /* DEEP_VAL */ 225000) + +ANALYZER_OPTION( + unsigned, RegionStoreSmallStructLimit, "region-store-small-struct-limit", + "The largest number of fields a struct can have and still be considered " + "small This is currently used to decide whether or not it is worth forcing " + "a LazyCompoundVal on bind. To disable all small-struct-dependent " + "behavior, set the option to 0.", + 2) + +//===----------------------------------------------------------------------===// +// String analyzer options. +//===----------------------------------------------------------------------===// + +ANALYZER_OPTION(StringRef, CTUDir, "ctu-dir", + "The directory containing the CTU related files.", "") + +ANALYZER_OPTION(StringRef, CTUIndexName, "ctu-index-name", + "the name of the file containing the CTU index of definitions.", + "externalDefMap.txt") + +ANALYZER_OPTION( + StringRef, ModelPath, "model-path", + "The analyzer can inline an alternative implementation written in C at the " + "call site if the called function's body is not available. This is a path " + "where to look for those alternative implementations (called models).", + "") + +ANALYZER_OPTION( + StringRef, CXXMemberInliningMode, "c++-inlining", + "Controls which C++ member functions will be considered for inlining. " + "Value: \"constructors\", \"destructors\", \"methods\".", + "destructors") + +ANALYZER_OPTION_DEPENDS_ON_USER_MODE( + StringRef, IPAMode, "ipa", + "Controls the mode of inter-procedural analysis. Value: \"none\", " + "\"basic-inlining\", \"inlining\", \"dynamic\", \"dynamic-bifurcate\".", + /* SHALLOW_VAL */ "inlining", /* DEEP_VAL */ "dynamic-bifurcate") + +ANALYZER_OPTION( + StringRef, ExplorationStrategy, "exploration_strategy", + "Value: \"dfs\", \"bfs\", \"unexplored_first\", " + "\"unexplored_first_queue\", \"unexplored_first_location_queue\", " + "\"bfs_block_dfs_contents\".", + "unexplored_first_queue") + +#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE +#undef ANALYZER_OPTION diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/clang-r353983/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h new file mode 100644 index 00000000..775f0dd4 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -0,0 +1,364 @@ +//===- AnalyzerOptions.h - Analysis Engine Options --------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This header defines various options for the static analyzer that are set +// by the frontend and are consulted throughout the analyzer. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_ANALYZEROPTIONS_H +#define LLVM_CLANG_STATICANALYZER_CORE_ANALYZEROPTIONS_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include <string> +#include <utility> +#include <vector> + +namespace clang { + +namespace ento { + +class CheckerBase; + +} // namespace ento + +/// Analysis - Set of available source code analyses. +enum Analyses { +#define ANALYSIS(NAME, CMDFLAG, DESC, SCOPE) NAME, +#include "clang/StaticAnalyzer/Core/Analyses.def" +NumAnalyses +}; + +/// AnalysisStores - Set of available analysis store models. +enum AnalysisStores { +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) NAME##Model, +#include "clang/StaticAnalyzer/Core/Analyses.def" +NumStores +}; + +/// AnalysisConstraints - Set of available constraint models. +enum AnalysisConstraints { +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) NAME##Model, +#include "clang/StaticAnalyzer/Core/Analyses.def" +NumConstraints +}; + +/// AnalysisDiagClients - Set of available diagnostic clients for rendering +/// analysis results. +enum AnalysisDiagClients { +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN) PD_##NAME, +#include "clang/StaticAnalyzer/Core/Analyses.def" +PD_NONE, +NUM_ANALYSIS_DIAG_CLIENTS +}; + +/// AnalysisPurgeModes - Set of available strategies for dead symbol removal. +enum AnalysisPurgeMode { +#define ANALYSIS_PURGE(NAME, CMDFLAG, DESC) NAME, +#include "clang/StaticAnalyzer/Core/Analyses.def" +NumPurgeModes +}; + +/// AnalysisInlineFunctionSelection - Set of inlining function selection heuristics. +enum AnalysisInliningMode { +#define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC) NAME, +#include "clang/StaticAnalyzer/Core/Analyses.def" +NumInliningModes +}; + +/// Describes the different kinds of C++ member functions which can be +/// considered for inlining by the analyzer. +/// +/// These options are cumulative; enabling one kind of member function will +/// enable all kinds with lower enum values. +enum CXXInlineableMemberKind { + // Uninitialized = 0, + + /// A dummy mode in which no C++ inlining is enabled. + CIMK_None, + + /// Refers to regular member function and operator calls. + CIMK_MemberFunctions, + + /// Refers to constructors (implicit or explicit). + /// + /// Note that a constructor will not be inlined if the corresponding + /// destructor is non-trivial. + CIMK_Constructors, + + /// Refers to destructors (implicit or explicit). + CIMK_Destructors +}; + +/// Describes the different modes of inter-procedural analysis. +enum IPAKind { + /// Perform only intra-procedural analysis. + IPAK_None = 1, + + /// Inline C functions and blocks when their definitions are available. + IPAK_BasicInlining = 2, + + /// Inline callees(C, C++, ObjC) when their definitions are available. + IPAK_Inlining = 3, + + /// Enable inlining of dynamically dispatched methods. + IPAK_DynamicDispatch = 4, + + /// Enable inlining of dynamically dispatched methods, bifurcate paths when + /// exact type info is unavailable. + IPAK_DynamicDispatchBifurcate = 5 +}; + +enum class ExplorationStrategyKind { + DFS, + BFS, + UnexploredFirst, + UnexploredFirstQueue, + UnexploredFirstLocationQueue, + BFSBlockDFSContents, +}; + +/// Describes the kinds for high-level analyzer mode. +enum UserModeKind { + /// Perform shallow but fast analyzes. + UMK_Shallow = 1, + + /// Perform deep analyzes. + UMK_Deep = 2 +}; + +/// Stores options for the analyzer from the command line. +/// +/// Some options are frontend flags (e.g.: -analyzer-output), but some are +/// analyzer configuration options, which are preceded by -analyzer-config +/// (e.g.: -analyzer-config notes-as-events=true). +/// +/// If you'd like to add a new frontend flag, add it to +/// include/clang/Driver/CC1Options.td, add a new field to store the value of +/// that flag in this class, and initialize it in +/// lib/Frontend/CompilerInvocation.cpp. +/// +/// If you'd like to add a new non-checker configuration, register it in +/// include/clang/StaticAnalyzer/Core/AnalyzerOptions.def, and refer to the +/// top of the file for documentation. +/// +/// If you'd like to add a new checker option, call getChecker*Option() +/// whenever. +/// +/// Some of the options are controlled by raw frontend flags for no good reason, +/// and should be eventually converted into -analyzer-config flags. New analyzer +/// options should not be implemented as frontend flags. Frontend flags still +/// make sense for things that do not affect the actual analysis. +class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> { +public: + using ConfigTable = llvm::StringMap<std::string>; + + static std::vector<StringRef> + getRegisteredCheckers(bool IncludeExperimental = false); + + /// Pair of checker name and enable/disable. + std::vector<std::pair<std::string, bool>> CheckersControlList; + + /// A key-value table of use-specified configuration values. + // TODO: This shouldn't be public. + ConfigTable Config; + AnalysisStores AnalysisStoreOpt = RegionStoreModel; + AnalysisConstraints AnalysisConstraintsOpt = RangeConstraintsModel; + AnalysisDiagClients AnalysisDiagOpt = PD_HTML; + AnalysisPurgeMode AnalysisPurgeOpt = PurgeStmt; + + std::string AnalyzeSpecificFunction; + + /// File path to which the exploded graph should be dumped. + std::string DumpExplodedGraphTo; + + /// Store full compiler invocation for reproducible instructions in the + /// generated report. + std::string FullCompilerInvocation; + + /// The maximum number of times the analyzer visits a block. + unsigned maxBlockVisitOnPath; + + /// Disable all analyzer checks. + /// + /// This flag allows one to disable analyzer checks on the code processed by + /// the given analysis consumer. Note, the code will get parsed and the + /// command-line options will get checked. + unsigned DisableAllChecks : 1; + + unsigned ShowCheckerHelp : 1; + unsigned ShowEnabledCheckerList : 1; + unsigned ShowConfigOptionsList : 1; + unsigned ShouldEmitErrorsOnInvalidConfigValue : 1; + unsigned AnalyzeAll : 1; + unsigned AnalyzerDisplayProgress : 1; + unsigned AnalyzeNestedBlocks : 1; + + unsigned eagerlyAssumeBinOpBifurcation : 1; + + unsigned TrimGraph : 1; + unsigned visualizeExplodedGraphWithGraphViz : 1; + unsigned UnoptimizedCFG : 1; + unsigned PrintStats : 1; + + /// Do not re-analyze paths leading to exhausted nodes with a different + /// strategy. We get better code coverage when retry is enabled. + unsigned NoRetryExhausted : 1; + + /// The inlining stack depth limit. + // Cap the stack depth at 4 calls (5 stack frames, base + 4 calls). + unsigned InlineMaxStackDepth = 5; + + /// The mode of function selection used during inlining. + AnalysisInliningMode InliningMode = NoRedundancy; + + // Create a field for each -analyzer-config option. +#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ + SHALLOW_VAL, DEEP_VAL) \ + ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL) + +#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ + TYPE NAME; + +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" +#undef ANALYZER_OPTION +#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE + + // Create an array of all -analyzer-config command line options. Sort it in + // the constructor. + std::vector<StringRef> AnalyzerConfigCmdFlags = { +#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ + SHALLOW_VAL, DEEP_VAL) \ + ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL) + +#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ + CMDFLAG, + +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" +#undef ANALYZER_OPTION +#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE + }; + + bool isUnknownAnalyzerConfig(StringRef Name) const { + + assert(std::is_sorted(AnalyzerConfigCmdFlags.begin(), + AnalyzerConfigCmdFlags.end())); + + return !std::binary_search(AnalyzerConfigCmdFlags.begin(), + AnalyzerConfigCmdFlags.end(), Name); + } + + AnalyzerOptions() + : DisableAllChecks(false), ShowCheckerHelp(false), + ShowEnabledCheckerList(false), ShowConfigOptionsList(false), + AnalyzeAll(false), AnalyzerDisplayProgress(false), + AnalyzeNestedBlocks(false), eagerlyAssumeBinOpBifurcation(false), + TrimGraph(false), visualizeExplodedGraphWithGraphViz(false), + UnoptimizedCFG(false), PrintStats(false), NoRetryExhausted(false) { + llvm::sort(AnalyzerConfigCmdFlags); + } + + /// Interprets an option's string value as a boolean. The "true" string is + /// interpreted as true and the "false" string is interpreted as false. + /// + /// If an option value is not provided, returns the given \p DefaultVal. + /// @param [in] Name Name for option to retrieve. + /// @param [in] DefaultVal Default value returned if no such option was + /// specified. + /// @param [in] C The checker object the option belongs to. Checker options + /// are retrieved in the following format: + /// `-analyzer-config <package and checker name>:OptionName=Value. + /// @param [in] SearchInParents If set to true and the searched option was not + /// specified for the given checker the options for the parent packages will + /// be searched as well. The inner packages take precedence over the outer + /// ones. + bool getCheckerBooleanOption(StringRef Name, bool DefaultVal, + const ento::CheckerBase *C, + bool SearchInParents = false) const; + + + /// Interprets an option's string value as an integer value. + /// + /// If an option value is not provided, returns the given \p DefaultVal. + /// @param [in] Name Name for option to retrieve. + /// @param [in] DefaultVal Default value returned if no such option was + /// specified. + /// @param [in] C The checker object the option belongs to. Checker options + /// are retrieved in the following format: + /// `-analyzer-config <package and checker name>:OptionName=Value. + /// @param [in] SearchInParents If set to true and the searched option was not + /// specified for the given checker the options for the parent packages will + /// be searched as well. The inner packages take precedence over the outer + /// ones. + int getCheckerIntegerOption(StringRef Name, int DefaultVal, + const ento::CheckerBase *C, + bool SearchInParents = false) const; + + /// Query an option's string value. + /// + /// If an option value is not provided, returns the given \p DefaultVal. + /// @param [in] Name Name for option to retrieve. + /// @param [in] DefaultVal Default value returned if no such option was + /// specified. + /// @param [in] C The checker object the option belongs to. Checker options + /// are retrieved in the following format: + /// `-analyzer-config <package and checker name>:OptionName=Value. + /// @param [in] SearchInParents If set to true and the searched option was not + /// specified for the given checker the options for the parent packages will + /// be searched as well. The inner packages take precedence over the outer + /// ones. + StringRef getCheckerStringOption(StringRef Name, StringRef DefaultVal, + const ento::CheckerBase *C, + bool SearchInParents = false) const; + + /// Retrieves and sets the UserMode. This is a high-level option, + /// which is used to set other low-level options. It is not accessible + /// outside of AnalyzerOptions. + UserModeKind getUserMode() const; + + ExplorationStrategyKind getExplorationStrategy() const; + + /// Returns the inter-procedural analysis mode. + IPAKind getIPAMode() const; + + /// Returns the option controlling which C++ member functions will be + /// considered for inlining. + /// + /// This is controlled by the 'c++-inlining' config option. + /// + /// \sa CXXMemberInliningMode + bool mayInlineCXXMemberFunction(CXXInlineableMemberKind K) const; +}; + +using AnalyzerOptionsRef = IntrusiveRefCntPtr<AnalyzerOptions>; + +//===----------------------------------------------------------------------===// +// We'll use AnalyzerOptions in the frontend, but we can't link the frontend +// with clangStaticAnalyzerCore, because clangStaticAnalyzerCore depends on +// clangFrontend. +// +// For this reason, implement some methods in this header file. +//===----------------------------------------------------------------------===// + +inline UserModeKind AnalyzerOptions::getUserMode() const { + auto K = llvm::StringSwitch<llvm::Optional<UserModeKind>>(UserMode) + .Case("shallow", UMK_Shallow) + .Case("deep", UMK_Deep) + .Default(None); + assert(K.hasValue() && "User mode is invalid."); + return K.getValue(); +} + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_ANALYZEROPTIONS_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h new file mode 100644 index 00000000..a53efbbb --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -0,0 +1,599 @@ +//===- BugReporter.h - Generate PathDiagnostics -----------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines BugReporter, a utility class for generating +// PathDiagnostics for analyses based on ProgramState. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H +#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/ilist.h" +#include "llvm/ADT/ilist_node.h" +#include "llvm/ADT/iterator_range.h" +#include <cassert> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +namespace clang { + +class AnalyzerOptions; +class ASTContext; +class Decl; +class DiagnosticsEngine; +class LocationContext; +class SourceManager; +class Stmt; + +namespace ento { + +class BugType; +class CheckerBase; +class ExplodedGraph; +class ExplodedNode; +class ExprEngine; +class MemRegion; +class SValBuilder; + +//===----------------------------------------------------------------------===// +// Interface for individual bug reports. +//===----------------------------------------------------------------------===// + +/// A mapping from diagnostic consumers to the diagnostics they should +/// consume. +using DiagnosticForConsumerMapTy = + llvm::DenseMap<PathDiagnosticConsumer *, std::unique_ptr<PathDiagnostic>>; + +/// This class provides an interface through which checkers can create +/// individual bug reports. +class BugReport : public llvm::ilist_node<BugReport> { +public: + class NodeResolver { + virtual void anchor(); + + public: + virtual ~NodeResolver() = default; + + virtual const ExplodedNode* + getOriginalNode(const ExplodedNode *N) = 0; + }; + + using ranges_iterator = const SourceRange *; + using VisitorList = SmallVector<std::unique_ptr<BugReporterVisitor>, 8>; + using visitor_iterator = VisitorList::iterator; + using ExtraTextList = SmallVector<StringRef, 2>; + using NoteList = SmallVector<std::shared_ptr<PathDiagnosticNotePiece>, 4>; + +protected: + friend class BugReportEquivClass; + friend class BugReporter; + + const BugType& BT; + const Decl *DeclWithIssue = nullptr; + std::string ShortDescription; + std::string Description; + PathDiagnosticLocation Location; + PathDiagnosticLocation UniqueingLocation; + const Decl *UniqueingDecl; + + const ExplodedNode *ErrorNode = nullptr; + SmallVector<SourceRange, 4> Ranges; + ExtraTextList ExtraText; + NoteList Notes; + + using Symbols = llvm::DenseSet<SymbolRef>; + using Regions = llvm::DenseSet<const MemRegion *>; + + /// A (stack of) a set of symbols that are registered with this + /// report as being "interesting", and thus used to help decide which + /// diagnostics to include when constructing the final path diagnostic. + /// The stack is largely used by BugReporter when generating PathDiagnostics + /// for multiple PathDiagnosticConsumers. + SmallVector<Symbols *, 2> interestingSymbols; + + /// A (stack of) set of regions that are registered with this report as being + /// "interesting", and thus used to help decide which diagnostics + /// to include when constructing the final path diagnostic. + /// The stack is largely used by BugReporter when generating PathDiagnostics + /// for multiple PathDiagnosticConsumers. + SmallVector<Regions *, 2> interestingRegions; + + /// A set of location contexts that correspoind to call sites which should be + /// considered "interesting". + llvm::SmallSet<const LocationContext *, 2> InterestingLocationContexts; + + /// A set of custom visitors which generate "event" diagnostics at + /// interesting points in the path. + VisitorList Callbacks; + + /// Used for ensuring the visitors are only added once. + llvm::FoldingSet<BugReporterVisitor> CallbacksSet; + + /// When set, this flag disables all callstack pruning from a diagnostic + /// path. This is useful for some reports that want maximum fidelty + /// when reporting an issue. + bool DoNotPrunePath = false; + + /// Used to track unique reasons why a bug report might be invalid. + /// + /// \sa markInvalid + /// \sa removeInvalidation + using InvalidationRecord = std::pair<const void *, const void *>; + + /// If non-empty, this bug report is likely a false positive and should not be + /// shown to the user. + /// + /// \sa markInvalid + /// \sa removeInvalidation + llvm::SmallSet<InvalidationRecord, 4> Invalidations; + +private: + // Used internally by BugReporter. + Symbols &getInterestingSymbols(); + Regions &getInterestingRegions(); + + void lazyInitializeInterestingSets(); + void pushInterestingSymbolsAndRegions(); + void popInterestingSymbolsAndRegions(); + +public: + BugReport(const BugType& bt, StringRef desc, const ExplodedNode *errornode) + : BT(bt), Description(desc), ErrorNode(errornode) {} + + BugReport(const BugType& bt, StringRef shortDesc, StringRef desc, + const ExplodedNode *errornode) + : BT(bt), ShortDescription(shortDesc), Description(desc), + ErrorNode(errornode) {} + + BugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l) + : BT(bt), Description(desc), Location(l) {} + + /// Create a BugReport with a custom uniqueing location. + /// + /// The reports that have the same report location, description, bug type, and + /// ranges are uniqued - only one of the equivalent reports will be presented + /// to the user. This method allows to rest the location which should be used + /// for uniquing reports. For example, memory leaks checker, could set this to + /// the allocation site, rather then the location where the bug is reported. + BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode, + PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique) + : BT(bt), Description(desc), UniqueingLocation(LocationToUnique), + UniqueingDecl(DeclToUnique), ErrorNode(errornode) {} + + virtual ~BugReport(); + + const BugType& getBugType() const { return BT; } + //BugType& getBugType() { return BT; } + + /// True when the report has an execution path associated with it. + /// + /// A report is said to be path-sensitive if it was thrown against a + /// particular exploded node in the path-sensitive analysis graph. + /// Path-sensitive reports have their intermediate path diagnostics + /// auto-generated, perhaps with the help of checker-defined visitors, + /// and may contain extra notes. + /// Path-insensitive reports consist only of a single warning message + /// in a specific location, and perhaps extra notes. + /// Path-sensitive checkers are allowed to throw path-insensitive reports. + bool isPathSensitive() const { return ErrorNode != nullptr; } + + const ExplodedNode *getErrorNode() const { return ErrorNode; } + + StringRef getDescription() const { return Description; } + + StringRef getShortDescription(bool UseFallback = true) const { + if (ShortDescription.empty() && UseFallback) + return Description; + return ShortDescription; + } + + /// Indicates whether or not any path pruning should take place + /// when generating a PathDiagnostic from this BugReport. + bool shouldPrunePath() const { return !DoNotPrunePath; } + + /// Disable all path pruning when generating a PathDiagnostic. + void disablePathPruning() { DoNotPrunePath = true; } + + void markInteresting(SymbolRef sym); + void markInteresting(const MemRegion *R); + void markInteresting(SVal V); + void markInteresting(const LocationContext *LC); + + bool isInteresting(SymbolRef sym); + bool isInteresting(const MemRegion *R); + bool isInteresting(SVal V); + bool isInteresting(const LocationContext *LC); + + /// Returns whether or not this report should be considered valid. + /// + /// Invalid reports are those that have been classified as likely false + /// positives after the fact. + bool isValid() const { + return Invalidations.empty(); + } + + /// Marks the current report as invalid, meaning that it is probably a false + /// positive and should not be reported to the user. + /// + /// The \p Tag and \p Data arguments are intended to be opaque identifiers for + /// this particular invalidation, where \p Tag represents the visitor + /// responsible for invalidation, and \p Data represents the reason this + /// visitor decided to invalidate the bug report. + /// + /// \sa removeInvalidation + void markInvalid(const void *Tag, const void *Data) { + Invalidations.insert(std::make_pair(Tag, Data)); + } + + /// Return the canonical declaration, be it a method or class, where + /// this issue semantically occurred. + const Decl *getDeclWithIssue() const; + + /// Specifically set the Decl where an issue occurred. This isn't necessary + /// for BugReports that cover a path as it will be automatically inferred. + void setDeclWithIssue(const Decl *declWithIssue) { + DeclWithIssue = declWithIssue; + } + + /// Add new item to the list of additional notes that need to be attached to + /// this path-insensitive report. If you want to add extra notes to a + /// path-sensitive report, you need to use a BugReporterVisitor because it + /// allows you to specify where exactly in the auto-generated path diagnostic + /// the extra note should appear. + void addNote(StringRef Msg, const PathDiagnosticLocation &Pos, + ArrayRef<SourceRange> Ranges) { + auto P = std::make_shared<PathDiagnosticNotePiece>(Pos, Msg); + + for (const auto &R : Ranges) + P->addRange(R); + + Notes.push_back(std::move(P)); + } + + // FIXME: Instead of making an override, we could have default-initialized + // Ranges with {}, however it crashes the MSVC 2013 compiler. + void addNote(StringRef Msg, const PathDiagnosticLocation &Pos) { + std::vector<SourceRange> Ranges; + addNote(Msg, Pos, Ranges); + } + + virtual const NoteList &getNotes() { + return Notes; + } + + /// This allows for addition of meta data to the diagnostic. + /// + /// Currently, only the HTMLDiagnosticClient knows how to display it. + void addExtraText(StringRef S) { + ExtraText.push_back(S); + } + + virtual const ExtraTextList &getExtraText() { + return ExtraText; + } + + /// Return the "definitive" location of the reported bug. + /// + /// While a bug can span an entire path, usually there is a specific + /// location that can be used to identify where the key issue occurred. + /// This location is used by clients rendering diagnostics. + virtual PathDiagnosticLocation getLocation(const SourceManager &SM) const; + + /// Get the location on which the report should be uniqued. + PathDiagnosticLocation getUniqueingLocation() const { + return UniqueingLocation; + } + + /// Get the declaration containing the uniqueing location. + const Decl *getUniqueingDecl() const { + return UniqueingDecl; + } + + const Stmt *getStmt() const; + + /// Add a range to a bug report. + /// + /// Ranges are used to highlight regions of interest in the source code. + /// They should be at the same source code line as the BugReport location. + /// By default, the source range of the statement corresponding to the error + /// node will be used; add a single invalid range to specify absence of + /// ranges. + void addRange(SourceRange R) { + assert((R.isValid() || Ranges.empty()) && "Invalid range can only be used " + "to specify that the report does not have a range."); + Ranges.push_back(R); + } + + /// Get the SourceRanges associated with the report. + virtual llvm::iterator_range<ranges_iterator> getRanges(); + + /// Add custom or predefined bug report visitors to this report. + /// + /// The visitors should be used when the default trace is not sufficient. + /// For example, they allow constructing a more elaborate trace. + /// \sa registerConditionVisitor(), registerTrackNullOrUndefValue(), + /// registerFindLastStore(), registerNilReceiverVisitor(), and + /// registerVarDeclsLastStore(). + void addVisitor(std::unique_ptr<BugReporterVisitor> visitor); + + /// Remove all visitors attached to this bug report. + void clearVisitors(); + + /// Iterators through the custom diagnostic visitors. + visitor_iterator visitor_begin() { return Callbacks.begin(); } + visitor_iterator visitor_end() { return Callbacks.end(); } + + /// Profile to identify equivalent bug reports for error report coalescing. + /// Reports are uniqued to ensure that we do not emit multiple diagnostics + /// for each bug. + virtual void Profile(llvm::FoldingSetNodeID& hash) const; +}; + +//===----------------------------------------------------------------------===// +// BugTypes (collections of related reports). +//===----------------------------------------------------------------------===// + +class BugReportEquivClass : public llvm::FoldingSetNode { + friend class BugReporter; + + /// List of *owned* BugReport objects. + llvm::ilist<BugReport> Reports; + + void AddReport(std::unique_ptr<BugReport> R) { + Reports.push_back(R.release()); + } + +public: + BugReportEquivClass(std::unique_ptr<BugReport> R) { AddReport(std::move(R)); } + ~BugReportEquivClass(); + + void Profile(llvm::FoldingSetNodeID& ID) const { + assert(!Reports.empty()); + Reports.front().Profile(ID); + } + + using iterator = llvm::ilist<BugReport>::iterator; + using const_iterator = llvm::ilist<BugReport>::const_iterator; + + iterator begin() { return Reports.begin(); } + iterator end() { return Reports.end(); } + + const_iterator begin() const { return Reports.begin(); } + const_iterator end() const { return Reports.end(); } +}; + +//===----------------------------------------------------------------------===// +// BugReporter and friends. +//===----------------------------------------------------------------------===// + +class BugReporterData { +public: + virtual ~BugReporterData(); + + virtual DiagnosticsEngine& getDiagnostic() = 0; + virtual ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() = 0; + virtual ASTContext &getASTContext() = 0; + virtual SourceManager &getSourceManager() = 0; + virtual AnalyzerOptions &getAnalyzerOptions() = 0; +}; + +/// BugReporter is a utility class for generating PathDiagnostics for analysis. +/// It collects the BugReports and BugTypes and knows how to generate +/// and flush the corresponding diagnostics. +/// +/// The base class is used for generating path-insensitive +class BugReporter { +public: + enum Kind { BaseBRKind, GRBugReporterKind }; + +private: + using BugTypesTy = llvm::ImmutableSet<BugType *>; + + BugTypesTy::Factory F; + BugTypesTy BugTypes; + + const Kind kind; + BugReporterData& D; + + /// Generate and flush the diagnostics for the given bug report. + void FlushReport(BugReportEquivClass& EQ); + + /// Generate the diagnostics for the given bug report. + std::unique_ptr<DiagnosticForConsumerMapTy> + generateDiagnosticForConsumerMap(BugReport *exampleReport, + ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> bugReports); + + /// The set of bug reports tracked by the BugReporter. + llvm::FoldingSet<BugReportEquivClass> EQClasses; + + /// A vector of BugReports for tracking the allocated pointers and cleanup. + std::vector<BugReportEquivClass *> EQClassesVector; + +protected: + BugReporter(BugReporterData& d, Kind k) + : BugTypes(F.getEmptySet()), kind(k), D(d) {} + +public: + BugReporter(BugReporterData& d) + : BugTypes(F.getEmptySet()), kind(BaseBRKind), D(d) {} + virtual ~BugReporter(); + + /// Generate and flush diagnostics for all bug reports. + void FlushReports(); + + Kind getKind() const { return kind; } + + DiagnosticsEngine& getDiagnostic() { + return D.getDiagnostic(); + } + + ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() { + return D.getPathDiagnosticConsumers(); + } + + /// Iterator over the set of BugTypes tracked by the BugReporter. + using iterator = BugTypesTy::iterator; + iterator begin() { return BugTypes.begin(); } + iterator end() { return BugTypes.end(); } + + /// Iterator over the set of BugReports tracked by the BugReporter. + using EQClasses_iterator = llvm::FoldingSet<BugReportEquivClass>::iterator; + EQClasses_iterator EQClasses_begin() { return EQClasses.begin(); } + EQClasses_iterator EQClasses_end() { return EQClasses.end(); } + + ASTContext &getContext() { return D.getASTContext(); } + + SourceManager &getSourceManager() { return D.getSourceManager(); } + + AnalyzerOptions &getAnalyzerOptions() { return D.getAnalyzerOptions(); } + + virtual std::unique_ptr<DiagnosticForConsumerMapTy> + generatePathDiagnostics(ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> &bugReports) { + return {}; + } + + void Register(const BugType *BT); + + /// Add the given report to the set of reports tracked by BugReporter. + /// + /// The reports are usually generated by the checkers. Further, they are + /// folded based on the profile value, which is done to coalesce similar + /// reports. + void emitReport(std::unique_ptr<BugReport> R); + + void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, + StringRef BugName, StringRef BugCategory, + StringRef BugStr, PathDiagnosticLocation Loc, + ArrayRef<SourceRange> Ranges = None); + + void EmitBasicReport(const Decl *DeclWithIssue, CheckName CheckName, + StringRef BugName, StringRef BugCategory, + StringRef BugStr, PathDiagnosticLocation Loc, + ArrayRef<SourceRange> Ranges = None); + +private: + llvm::StringMap<BugType *> StrBugTypes; + + /// Returns a BugType that is associated with the given name and + /// category. + BugType *getBugTypeForName(CheckName CheckName, StringRef name, + StringRef category); +}; + +/// GRBugReporter is used for generating path-sensitive reports. +class GRBugReporter : public BugReporter { + ExprEngine& Eng; + +public: + GRBugReporter(BugReporterData& d, ExprEngine& eng) + : BugReporter(d, GRBugReporterKind), Eng(eng) {} + + ~GRBugReporter() override; + + /// getGraph - Get the exploded graph created by the analysis engine + /// for the analyzed method or function. + ExplodedGraph &getGraph(); + + /// getStateManager - Return the state manager used by the analysis + /// engine. + ProgramStateManager &getStateManager(); + + /// \p bugReports A set of bug reports within a *single* equivalence class + /// + /// \return A mapping from consumers to the corresponding diagnostics. + /// Iterates through the bug reports within a single equivalence class, + /// stops at a first non-invalidated report. + std::unique_ptr<DiagnosticForConsumerMapTy> + generatePathDiagnostics(ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> &bugReports) override; + + /// classof - Used by isa<>, cast<>, and dyn_cast<>. + static bool classof(const BugReporter* R) { + return R->getKind() == GRBugReporterKind; + } +}; + + +class NodeMapClosure : public BugReport::NodeResolver { + InterExplodedGraphMap &M; + +public: + NodeMapClosure(InterExplodedGraphMap &m) : M(m) {} + + const ExplodedNode *getOriginalNode(const ExplodedNode *N) override { + return M.lookup(N); + } +}; + +class BugReporterContext { + GRBugReporter &BR; + NodeMapClosure NMC; + + virtual void anchor(); + +public: + BugReporterContext(GRBugReporter &br, InterExplodedGraphMap &Backmap) + : BR(br), NMC(Backmap) {} + + virtual ~BugReporterContext() = default; + + GRBugReporter& getBugReporter() { return BR; } + + ExplodedGraph &getGraph() { return BR.getGraph(); } + + ProgramStateManager& getStateManager() { + return BR.getStateManager(); + } + + SValBuilder &getSValBuilder() { + return getStateManager().getSValBuilder(); + } + + ASTContext &getASTContext() { + return BR.getContext(); + } + + SourceManager& getSourceManager() { + return BR.getSourceManager(); + } + + AnalyzerOptions &getAnalyzerOptions() { + return BR.getAnalyzerOptions(); + } + + NodeMapClosure& getNodeResolver() { return NMC; } +}; + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h new file mode 100644 index 00000000..91f7cee5 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -0,0 +1,370 @@ +//===- BugReporterVisitors.h - Generate PathDiagnostics ---------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file declares BugReporterVisitors, which are used to generate enhanced +// diagnostic traces. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H +#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H + +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include <memory> + +namespace clang { + +class BinaryOperator; +class CFGBlock; +class DeclRefExpr; +class Expr; +class Stmt; + +namespace ento { + +class BugReport; +class BugReporterContext; +class ExplodedNode; +class MemRegion; +class PathDiagnosticPiece; + +/// BugReporterVisitors are used to add custom diagnostics along a path. +class BugReporterVisitor : public llvm::FoldingSetNode { +public: + BugReporterVisitor() = default; + BugReporterVisitor(const BugReporterVisitor &) = default; + BugReporterVisitor(BugReporterVisitor &&) {} + virtual ~BugReporterVisitor(); + + /// Return a diagnostic piece which should be associated with the + /// given node. + /// Note that this function does *not* get run on the very last node + /// of the report, as the PathDiagnosticPiece associated with the + /// last node should be unique. + /// Use {@code getEndPath} to customize the note associated with the report + /// end instead. + /// + /// The last parameter can be used to register a new visitor with the given + /// BugReport while processing a node. + virtual std::shared_ptr<PathDiagnosticPiece> + VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, BugReport &BR) = 0; + + /// Last function called on the visitor, no further calls to VisitNode + /// would follow. + virtual void finalizeVisitor(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + BugReport &BR); + + /// Provide custom definition for the final diagnostic piece on the + /// path - the piece, which is displayed before the path is expanded. + /// + /// NOTE that this function can be implemented on at most one used visitor, + /// and otherwise it crahes at runtime. + virtual std::shared_ptr<PathDiagnosticPiece> + getEndPath(BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR); + + virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0; + + /// Generates the default final diagnostic piece. + static std::shared_ptr<PathDiagnosticPiece> + getDefaultEndPath(BugReporterContext &BRC, const ExplodedNode *N, + BugReport &BR); +}; + +/// Finds last store into the given region, +/// which is different from a given symbolic value. +class FindLastStoreBRVisitor final : public BugReporterVisitor { + const MemRegion *R; + SVal V; + bool Satisfied = false; + + /// If the visitor is tracking the value directly responsible for the + /// bug, we are going to employ false positive suppression. + bool EnableNullFPSuppression; + +public: + /// Creates a visitor for every VarDecl inside a Stmt and registers it with + /// the BugReport. + static void registerStatementVarDecls(BugReport &BR, const Stmt *S, + bool EnableNullFPSuppression); + + FindLastStoreBRVisitor(KnownSVal V, const MemRegion *R, + bool InEnableNullFPSuppression) + : R(R), V(V), EnableNullFPSuppression(InEnableNullFPSuppression) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; +}; + +class TrackConstraintBRVisitor final : public BugReporterVisitor { + DefinedSVal Constraint; + bool Assumption; + bool IsSatisfied = false; + bool IsZeroCheck; + + /// We should start tracking from the last node along the path in which the + /// value is constrained. + bool IsTrackingTurnedOn = false; + +public: + TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) + : Constraint(constraint), Assumption(assumption), + IsZeroCheck(!Assumption && Constraint.getAs<Loc>()) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + /// Return the tag associated with this visitor. This tag will be used + /// to make all PathDiagnosticPieces created by this visitor. + static const char *getTag(); + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + +private: + /// Checks if the constraint is valid in the current state. + bool isUnderconstrained(const ExplodedNode *N) const; +}; + +/// \class NilReceiverBRVisitor +/// Prints path notes when a message is sent to a nil receiver. +class NilReceiverBRVisitor final : public BugReporterVisitor { +public: + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int x = 0; + ID.AddPointer(&x); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + /// If the statement is a message send expression with nil receiver, returns + /// the receiver expression. Returns NULL otherwise. + static const Expr *getNilReceiver(const Stmt *S, const ExplodedNode *N); +}; + +/// Visitor that tries to report interesting diagnostics from conditions. +class ConditionBRVisitor final : public BugReporterVisitor { + // FIXME: constexpr initialization isn't supported by MSVC2013. + static const char *const GenericTrueMessage; + static const char *const GenericFalseMessage; + +public: + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int x = 0; + ID.AddPointer(&x); + } + + /// Return the tag associated with this visitor. This tag will be used + /// to make all PathDiagnosticPieces created by this visitor. + static const char *getTag(); + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + std::shared_ptr<PathDiagnosticPiece> VisitNodeImpl(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR); + + std::shared_ptr<PathDiagnosticPiece> + VisitTerminator(const Stmt *Term, const ExplodedNode *N, + const CFGBlock *srcBlk, const CFGBlock *dstBlk, BugReport &R, + BugReporterContext &BRC); + + std::shared_ptr<PathDiagnosticPiece> + VisitTrueTest(const Expr *Cond, bool tookTrue, BugReporterContext &BRC, + BugReport &R, const ExplodedNode *N); + + std::shared_ptr<PathDiagnosticPiece> + VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, const bool tookTrue, + BugReporterContext &BRC, BugReport &R, const ExplodedNode *N); + + std::shared_ptr<PathDiagnosticPiece> + VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, + const bool tookTrue, BugReporterContext &BRC, BugReport &R, + const ExplodedNode *N); + + std::shared_ptr<PathDiagnosticPiece> + VisitConditionVariable(StringRef LhsString, const Expr *CondVarExpr, + const bool tookTrue, BugReporterContext &BRC, + BugReport &R, const ExplodedNode *N); + + bool patternMatch(const Expr *Ex, + const Expr *ParentEx, + raw_ostream &Out, + BugReporterContext &BRC, + BugReport &R, + const ExplodedNode *N, + Optional<bool> &prunable); + + static bool isPieceMessageGeneric(const PathDiagnosticPiece *Piece); +}; + +/// Suppress reports that might lead to known false positives. +/// +/// Currently this suppresses reports based on locations of bugs. +class LikelyFalsePositiveSuppressionBRVisitor final + : public BugReporterVisitor { +public: + static void *getTag() { + static int Tag = 0; + return static_cast<void *>(&Tag); + } + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.AddPointer(getTag()); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *, + BugReporterContext &, + BugReport &) override { + return nullptr; + } + + void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N, + BugReport &BR) override; +}; + +/// When a region containing undefined value or '0' value is passed +/// as an argument in a call, marks the call as interesting. +/// +/// As a result, BugReporter will not prune the path through the function even +/// if the region's contents are not modified/accessed by the call. +class UndefOrNullArgVisitor final : public BugReporterVisitor { + /// The interesting memory region this visitor is tracking. + const MemRegion *R; + +public: + UndefOrNullArgVisitor(const MemRegion *InR) : R(InR) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int Tag = 0; + ID.AddPointer(&Tag); + ID.AddPointer(R); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; +}; + +class SuppressInlineDefensiveChecksVisitor final : public BugReporterVisitor { + /// The symbolic value for which we are tracking constraints. + /// This value is constrained to null in the end of path. + DefinedSVal V; + + /// Track if we found the node where the constraint was first added. + bool IsSatisfied = false; + + /// Since the visitors can be registered on nodes previous to the last + /// node in the BugReport, but the path traversal always starts with the last + /// node, the visitor invariant (that we start with a node in which V is null) + /// might not hold when node visitation starts. We are going to start tracking + /// from the last node in which the value is null. + bool IsTrackingTurnedOn = false; + +public: + SuppressInlineDefensiveChecksVisitor(DefinedSVal Val, const ExplodedNode *N); + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + /// Return the tag associated with this visitor. This tag will be used + /// to make all PathDiagnosticPieces created by this visitor. + static const char *getTag(); + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + BugReport &BR) override; +}; + +class CXXSelfAssignmentBRVisitor final : public BugReporterVisitor { + bool Satisfied = false; + +public: + CXXSelfAssignmentBRVisitor() = default; + + void Profile(llvm::FoldingSetNodeID &ID) const override {} + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + BugReport &BR) override; +}; + +/// The bug visitor prints a diagnostic message at the location where a given +/// variable was tainted. +class TaintBugVisitor final : public BugReporterVisitor { +private: + const SVal V; + +public: + TaintBugVisitor(const SVal V) : V(V) {} + void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(V); } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; +}; + +/// The bug visitor will walk all the nodes in a path and collect all the +/// constraints. When it reaches the root node, will create a refutation +/// manager and check if the constraints are satisfiable +class FalsePositiveRefutationBRVisitor final : public BugReporterVisitor { +private: + /// Holds the constraints in a given path + ConstraintRangeTy Constraints; + +public: + FalsePositiveRefutationBRVisitor(); + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode, + BugReport &BR) override; +}; + +namespace bugreporter { + +/// Attempts to add visitors to track expression value back to its point of +/// origin. +/// +/// \param N A node "downstream" from the evaluation of the statement. +/// \param E The expression value which we are tracking +/// \param R The bug report to which visitors should be attached. +/// \param EnableNullFPSuppression Whether we should employ false positive +/// suppression (inlined defensive checks, returned null). +/// +/// \return Whether or not the function was able to add visitors for this +/// statement. Note that returning \c true does not actually imply +/// that any visitors were added. +bool trackExpressionValue(const ExplodedNode *N, const Expr *E, BugReport &R, + bool EnableNullFPSuppression = true); + +const Expr *getDerefExpr(const Stmt *S); + +} // namespace bugreporter + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h b/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h new file mode 100644 index 00000000..324b5312 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h @@ -0,0 +1,90 @@ +//===--- BugType.h - Bug Information Description ---------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines BugType, a class representing a bug type. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGTYPE_H +#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGTYPE_H + +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include <string> + +namespace clang { + +namespace ento { + +class BugReporter; +class ExplodedNode; +class ExprEngine; + +class BugType { +private: + const CheckName Check; + const std::string Name; + const std::string Category; + const CheckerBase *Checker; + bool SuppressOnSink; + + virtual void anchor(); + +public: + BugType(CheckName Check, StringRef Name, StringRef Cat, + bool SuppressOnSink=false) + : Check(Check), Name(Name), Category(Cat), Checker(nullptr), + SuppressOnSink(SuppressOnSink) {} + BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat, + bool SuppressOnSink=false) + : Check(Checker->getCheckName()), Name(Name), Category(Cat), + Checker(Checker), SuppressOnSink(SuppressOnSink) {} + virtual ~BugType() = default; + + StringRef getName() const { return Name; } + StringRef getCategory() const { return Category; } + StringRef getCheckName() const { + // FIXME: This is a workaround to ensure that the correct check name is used + // The check names are set after the constructors are run. + // In case the BugType object is initialized in the checker's ctor + // the Check field will be empty. To circumvent this problem we use + // CheckerBase whenever it is possible. + StringRef CheckName = + Checker ? Checker->getCheckName().getName() : Check.getName(); + assert(!CheckName.empty() && "Check name is not set properly."); + return CheckName; + } + + /// isSuppressOnSink - Returns true if bug reports associated with this bug + /// type should be suppressed if the end node of the report is post-dominated + /// by a sink node. + bool isSuppressOnSink() const { return SuppressOnSink; } +}; + +class BuiltinBug : public BugType { + const std::string desc; + void anchor() override; +public: + BuiltinBug(class CheckName check, const char *name, const char *description) + : BugType(check, name, categories::LogicError), desc(description) {} + + BuiltinBug(const CheckerBase *checker, const char *name, + const char *description) + : BugType(checker, name, categories::LogicError), desc(description) {} + + BuiltinBug(const CheckerBase *checker, const char *name) + : BugType(checker, name, categories::LogicError), desc(name) {} + + StringRef getDescription() const { return desc; } +}; + +} // end ento namespace + +} // end clang namespace +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h b/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h new file mode 100644 index 00000000..85526eb4 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h @@ -0,0 +1,25 @@ +//=--- CommonBugCategories.h - Provides common issue categories -*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_COMMONBUGCATEGORIES_H +#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_COMMONBUGCATEGORIES_H + +// Common strings used for the "category" of many static analyzer issues. +namespace clang { + namespace ento { + namespace categories { + extern const char * const CoreFoundationObjectiveC; + extern const char * const LogicError; + extern const char * const MemoryRefCount; + extern const char * const MemoryError; + extern const char * const UnixAPI; + } + } +} +#endif + diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h new file mode 100644 index 00000000..3c8f85e2 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h @@ -0,0 +1,905 @@ +//===- PathDiagnostic.h - Path-Specific Diagnostic Handling -----*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the PathDiagnostic-related interfaces. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H +#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H + +#include "clang/AST/Stmt.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" +#include <cassert> +#include <deque> +#include <iterator> +#include <list> +#include <map> +#include <memory> +#include <set> +#include <string> +#include <utility> +#include <vector> + +namespace clang { + +class AnalysisDeclContext; +class BinaryOperator; +class CallEnter; +class CallExitEnd; +class CallExpr; +class ConditionalOperator; +class Decl; +class Expr; +class LocationContext; +class MemberExpr; +class ProgramPoint; +class SourceManager; + +namespace ento { + +class ExplodedNode; +class SymExpr; + +using SymbolRef = const SymExpr *; + +//===----------------------------------------------------------------------===// +// High-level interface for handlers of path-sensitive diagnostics. +//===----------------------------------------------------------------------===// + +class PathDiagnostic; + +class PathDiagnosticConsumer { +public: + class PDFileEntry : public llvm::FoldingSetNode { + public: + PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {} + + using ConsumerFiles = std::vector<std::pair<StringRef, StringRef>>; + + /// A vector of <consumer,file> pairs. + ConsumerFiles files; + + /// A precomputed hash tag used for uniquing PDFileEntry objects. + const llvm::FoldingSetNodeID NodeID; + + /// Used for profiling in the FoldingSet. + void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; } + }; + + class FilesMade { + llvm::BumpPtrAllocator Alloc; + llvm::FoldingSet<PDFileEntry> Set; + + public: + ~FilesMade(); + + bool empty() const { return Set.empty(); } + + void addDiagnostic(const PathDiagnostic &PD, + StringRef ConsumerName, + StringRef fileName); + + PDFileEntry::ConsumerFiles *getFiles(const PathDiagnostic &PD); + }; + +private: + virtual void anchor(); + +public: + PathDiagnosticConsumer() = default; + virtual ~PathDiagnosticConsumer(); + + void FlushDiagnostics(FilesMade *FilesMade); + + virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + FilesMade *filesMade) = 0; + + virtual StringRef getName() const = 0; + + void HandlePathDiagnostic(std::unique_ptr<PathDiagnostic> D); + + enum PathGenerationScheme { + /// Only runs visitors, no output generated. + None, + + /// Used for HTML, SARIF, and text output. + Minimal, + + /// Used for plist output, used for "arrows" generation. + Extensive, + }; + + virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } + virtual bool supportsLogicalOpControlFlow() const { return false; } + + /// Return true if the PathDiagnosticConsumer supports individual + /// PathDiagnostics that span multiple files. + virtual bool supportsCrossFileDiagnostics() const { return false; } + +protected: + bool flushed = false; + llvm::FoldingSet<PathDiagnostic> Diags; +}; + +//===----------------------------------------------------------------------===// +// Path-sensitive diagnostics. +//===----------------------------------------------------------------------===// + +class PathDiagnosticRange : public SourceRange { +public: + bool isPoint = false; + + PathDiagnosticRange(SourceRange R, bool isP = false) + : SourceRange(R), isPoint(isP) {} + PathDiagnosticRange() = default; +}; + +using LocationOrAnalysisDeclContext = + llvm::PointerUnion<const LocationContext *, AnalysisDeclContext *>; + +class PathDiagnosticLocation { +private: + enum Kind { RangeK, SingleLocK, StmtK, DeclK } K = SingleLocK; + + const Stmt *S = nullptr; + const Decl *D = nullptr; + const SourceManager *SM = nullptr; + FullSourceLoc Loc; + PathDiagnosticRange Range; + + PathDiagnosticLocation(SourceLocation L, const SourceManager &sm, Kind kind) + : K(kind), SM(&sm), Loc(genLocation(L)), Range(genRange()) {} + + FullSourceLoc genLocation( + SourceLocation L = SourceLocation(), + LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; + + PathDiagnosticRange genRange( + LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; + +public: + /// Create an invalid location. + PathDiagnosticLocation() = default; + + /// Create a location corresponding to the given statement. + PathDiagnosticLocation(const Stmt *s, const SourceManager &sm, + LocationOrAnalysisDeclContext lac) + : K(s->getBeginLoc().isValid() ? StmtK : SingleLocK), + S(K == StmtK ? s : nullptr), SM(&sm), + Loc(genLocation(SourceLocation(), lac)), Range(genRange(lac)) { + assert(K == SingleLocK || S); + assert(K == SingleLocK || Loc.isValid()); + assert(K == SingleLocK || Range.isValid()); + } + + /// Create a location corresponding to the given declaration. + PathDiagnosticLocation(const Decl *d, const SourceManager &sm) + : K(DeclK), D(d), SM(&sm), Loc(genLocation()), Range(genRange()) { + assert(D); + assert(Loc.isValid()); + assert(Range.isValid()); + } + + /// Create a location at an explicit offset in the source. + /// + /// This should only be used if there are no more appropriate constructors. + PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm) + : SM(&sm), Loc(loc, sm), Range(genRange()) { + assert(Loc.isValid()); + assert(Range.isValid()); + } + + /// Create a location corresponding to the given declaration. + static PathDiagnosticLocation create(const Decl *D, + const SourceManager &SM) { + return PathDiagnosticLocation(D, SM); + } + + /// Create a location for the beginning of the declaration. + static PathDiagnosticLocation createBegin(const Decl *D, + const SourceManager &SM); + + /// Create a location for the beginning of the declaration. + /// The third argument is ignored, useful for generic treatment + /// of statements and declarations. + static PathDiagnosticLocation + createBegin(const Decl *D, const SourceManager &SM, + const LocationOrAnalysisDeclContext LAC) { + return createBegin(D, SM); + } + + /// Create a location for the beginning of the statement. + static PathDiagnosticLocation createBegin(const Stmt *S, + const SourceManager &SM, + const LocationOrAnalysisDeclContext LAC); + + /// Create a location for the end of the statement. + /// + /// If the statement is a CompoundStatement, the location will point to the + /// closing brace instead of following it. + static PathDiagnosticLocation createEnd(const Stmt *S, + const SourceManager &SM, + const LocationOrAnalysisDeclContext LAC); + + /// Create the location for the operator of the binary expression. + /// Assumes the statement has a valid location. + static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO, + const SourceManager &SM); + static PathDiagnosticLocation createConditionalColonLoc( + const ConditionalOperator *CO, + const SourceManager &SM); + + /// For member expressions, return the location of the '.' or '->'. + /// Assumes the statement has a valid location. + static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME, + const SourceManager &SM); + + /// Create a location for the beginning of the compound statement. + /// Assumes the statement has a valid location. + static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS, + const SourceManager &SM); + + /// Create a location for the end of the compound statement. + /// Assumes the statement has a valid location. + static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS, + const SourceManager &SM); + + /// Create a location for the beginning of the enclosing declaration body. + /// Defaults to the beginning of the first statement in the declaration body. + static PathDiagnosticLocation createDeclBegin(const LocationContext *LC, + const SourceManager &SM); + + /// Constructs a location for the end of the enclosing declaration body. + /// Defaults to the end of brace. + static PathDiagnosticLocation createDeclEnd(const LocationContext *LC, + const SourceManager &SM); + + /// Create a location corresponding to the given valid ExplodedNode. + static PathDiagnosticLocation create(const ProgramPoint &P, + const SourceManager &SMng); + + /// Create a location corresponding to the next valid ExplodedNode as end + /// of path location. + static PathDiagnosticLocation createEndOfPath(const ExplodedNode* N, + const SourceManager &SM); + + /// Convert the given location into a single kind location. + static PathDiagnosticLocation createSingleLocation( + const PathDiagnosticLocation &PDL); + + bool operator==(const PathDiagnosticLocation &X) const { + return K == X.K && Loc == X.Loc && Range == X.Range; + } + + bool operator!=(const PathDiagnosticLocation &X) const { + return !(*this == X); + } + + bool isValid() const { + return SM != nullptr; + } + + FullSourceLoc asLocation() const { + return Loc; + } + + PathDiagnosticRange asRange() const { + return Range; + } + + const Stmt *asStmt() const { assert(isValid()); return S; } + const Stmt *getStmtOrNull() const { + if (!isValid()) + return nullptr; + return asStmt(); + } + + const Decl *asDecl() const { assert(isValid()); return D; } + + bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; } + + void invalidate() { + *this = PathDiagnosticLocation(); + } + + void flatten(); + + const SourceManager& getManager() const { assert(isValid()); return *SM; } + + void Profile(llvm::FoldingSetNodeID &ID) const; + + void dump() const; + + /// Given an exploded node, retrieve the statement that should be used + /// for the diagnostic location. + static const Stmt *getStmt(const ExplodedNode *N); + + /// Retrieve the statement corresponding to the successor node. + static const Stmt *getNextStmt(const ExplodedNode *N); +}; + +class PathDiagnosticLocationPair { +private: + PathDiagnosticLocation Start, End; + +public: + PathDiagnosticLocationPair(const PathDiagnosticLocation &start, + const PathDiagnosticLocation &end) + : Start(start), End(end) {} + + const PathDiagnosticLocation &getStart() const { return Start; } + const PathDiagnosticLocation &getEnd() const { return End; } + + void setStart(const PathDiagnosticLocation &L) { Start = L; } + void setEnd(const PathDiagnosticLocation &L) { End = L; } + + void flatten() { + Start.flatten(); + End.flatten(); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + Start.Profile(ID); + End.Profile(ID); + } +}; + +//===----------------------------------------------------------------------===// +// Path "pieces" for path-sensitive diagnostics. +//===----------------------------------------------------------------------===// + +class PathDiagnosticPiece: public llvm::FoldingSetNode { +public: + enum Kind { ControlFlow, Event, Macro, Call, Note }; + enum DisplayHint { Above, Below }; + +private: + const std::string str; + const Kind kind; + const DisplayHint Hint; + + /// In the containing bug report, this piece is the last piece from + /// the main source file. + bool LastInMainSourceFile = false; + + /// A constant string that can be used to tag the PathDiagnosticPiece, + /// typically with the identification of the creator. The actual pointer + /// value is meant to be an identifier; the string itself is useful for + /// debugging. + StringRef Tag; + + std::vector<SourceRange> ranges; + +protected: + PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below); + PathDiagnosticPiece(Kind k, DisplayHint hint = Below); + +public: + PathDiagnosticPiece() = delete; + PathDiagnosticPiece(const PathDiagnosticPiece &) = delete; + PathDiagnosticPiece &operator=(const PathDiagnosticPiece &) = delete; + virtual ~PathDiagnosticPiece(); + + StringRef getString() const { return str; } + + /// Tag this PathDiagnosticPiece with the given C-string. + void setTag(const char *tag) { Tag = tag; } + + /// Return the opaque tag (if any) on the PathDiagnosticPiece. + const void *getTag() const { return Tag.data(); } + + /// Return the string representation of the tag. This is useful + /// for debugging. + StringRef getTagStr() const { return Tag; } + + /// getDisplayHint - Return a hint indicating where the diagnostic should + /// be displayed by the PathDiagnosticConsumer. + DisplayHint getDisplayHint() const { return Hint; } + + virtual PathDiagnosticLocation getLocation() const = 0; + virtual void flattenLocations() = 0; + + Kind getKind() const { return kind; } + + void addRange(SourceRange R) { + if (!R.isValid()) + return; + ranges.push_back(R); + } + + void addRange(SourceLocation B, SourceLocation E) { + if (!B.isValid() || !E.isValid()) + return; + ranges.push_back(SourceRange(B,E)); + } + + /// Return the SourceRanges associated with this PathDiagnosticPiece. + ArrayRef<SourceRange> getRanges() const { return ranges; } + + virtual void Profile(llvm::FoldingSetNodeID &ID) const; + + void setAsLastInMainSourceFile() { + LastInMainSourceFile = true; + } + + bool isLastInMainSourceFile() const { + return LastInMainSourceFile; + } + + virtual void dump() const = 0; +}; + +class PathPieces : public std::list<std::shared_ptr<PathDiagnosticPiece>> { + void flattenTo(PathPieces &Primary, PathPieces &Current, + bool ShouldFlattenMacros) const; + +public: + PathPieces flatten(bool ShouldFlattenMacros) const { + PathPieces Result; + flattenTo(Result, Result, ShouldFlattenMacros); + return Result; + } + + void dump() const; +}; + +class PathDiagnosticSpotPiece : public PathDiagnosticPiece { +private: + PathDiagnosticLocation Pos; + +public: + PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos, + StringRef s, + PathDiagnosticPiece::Kind k, + bool addPosRange = true) + : PathDiagnosticPiece(s, k), Pos(pos) { + assert(Pos.isValid() && Pos.asLocation().isValid() && + "PathDiagnosticSpotPiece's must have a valid location."); + if (addPosRange && Pos.hasRange()) addRange(Pos.asRange()); + } + + PathDiagnosticLocation getLocation() const override { return Pos; } + void flattenLocations() override { Pos.flatten(); } + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == Event || P->getKind() == Macro || + P->getKind() == Note; + } +}; + +/// Interface for classes constructing Stack hints. +/// +/// If a PathDiagnosticEvent occurs in a different frame than the final +/// diagnostic the hints can be used to summarize the effect of the call. +class StackHintGenerator { +public: + virtual ~StackHintGenerator() = 0; + + /// Construct the Diagnostic message for the given ExplodedNode. + virtual std::string getMessage(const ExplodedNode *N) = 0; +}; + +/// Constructs a Stack hint for the given symbol. +/// +/// The class knows how to construct the stack hint message based on +/// traversing the CallExpr associated with the call and checking if the given +/// symbol is returned or is one of the arguments. +/// The hint can be customized by redefining 'getMessageForX()' methods. +class StackHintGeneratorForSymbol : public StackHintGenerator { +private: + SymbolRef Sym; + std::string Msg; + +public: + StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {} + ~StackHintGeneratorForSymbol() override = default; + + /// Search the call expression for the symbol Sym and dispatch the + /// 'getMessageForX()' methods to construct a specific message. + std::string getMessage(const ExplodedNode *N) override; + + /// Produces the message of the following form: + /// 'Msg via Nth parameter' + virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex); + + virtual std::string getMessageForReturn(const CallExpr *CallExpr) { + return Msg; + } + + virtual std::string getMessageForSymbolNotFound() { + return Msg; + } +}; + +class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece { + Optional<bool> IsPrunable; + + /// If the event occurs in a different frame than the final diagnostic, + /// supply a message that will be used to construct an extra hint on the + /// returns from all the calls on the stack from this event to the final + /// diagnostic. + std::unique_ptr<StackHintGenerator> CallStackHint; + +public: + PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, + StringRef s, bool addPosRange = true, + StackHintGenerator *stackHint = nullptr) + : PathDiagnosticSpotPiece(pos, s, Event, addPosRange), + CallStackHint(stackHint) {} + ~PathDiagnosticEventPiece() override; + + /// Mark the diagnostic piece as being potentially prunable. This + /// flag may have been previously set, at which point it will not + /// be reset unless one specifies to do so. + void setPrunable(bool isPrunable, bool override = false) { + if (IsPrunable.hasValue() && !override) + return; + IsPrunable = isPrunable; + } + + /// Return true if the diagnostic piece is prunable. + bool isPrunable() const { + return IsPrunable.hasValue() ? IsPrunable.getValue() : false; + } + + bool hasCallStackHint() { return (bool)CallStackHint; } + + /// Produce the hint for the given node. The node contains + /// information about the call for which the diagnostic can be generated. + std::string getCallStackMessage(const ExplodedNode *N) { + if (CallStackHint) + return CallStackHint->getMessage(N); + return {}; + } + + void dump() const override; + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == Event; + } +}; + +class PathDiagnosticCallPiece : public PathDiagnosticPiece { + const Decl *Caller; + const Decl *Callee = nullptr; + + // Flag signifying that this diagnostic has only call enter and no matching + // call exit. + bool NoExit; + + // Flag signifying that the callee function is an Objective-C autosynthesized + // property getter or setter. + bool IsCalleeAnAutosynthesizedPropertyAccessor = false; + + // The custom string, which should appear after the call Return Diagnostic. + // TODO: Should we allow multiple diagnostics? + std::string CallStackMessage; + + PathDiagnosticCallPiece(const Decl *callerD, + const PathDiagnosticLocation &callReturnPos) + : PathDiagnosticPiece(Call), Caller(callerD), NoExit(false), + callReturn(callReturnPos) {} + PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller) + : PathDiagnosticPiece(Call), Caller(caller), NoExit(true), + path(oldPath) {} + +public: + PathDiagnosticLocation callEnter; + PathDiagnosticLocation callEnterWithin; + PathDiagnosticLocation callReturn; + PathPieces path; + + ~PathDiagnosticCallPiece() override; + + const Decl *getCaller() const { return Caller; } + + const Decl *getCallee() const { return Callee; } + void setCallee(const CallEnter &CE, const SourceManager &SM); + + bool hasCallStackMessage() { return !CallStackMessage.empty(); } + void setCallStackMessage(StringRef st) { CallStackMessage = st; } + + PathDiagnosticLocation getLocation() const override { return callEnter; } + + std::shared_ptr<PathDiagnosticEventPiece> getCallEnterEvent() const; + std::shared_ptr<PathDiagnosticEventPiece> + getCallEnterWithinCallerEvent() const; + std::shared_ptr<PathDiagnosticEventPiece> getCallExitEvent() const; + + void flattenLocations() override { + callEnter.flatten(); + callReturn.flatten(); + for (const auto &I : path) + I->flattenLocations(); + } + + static std::shared_ptr<PathDiagnosticCallPiece> + construct(const CallExitEnd &CE, + const SourceManager &SM); + + static PathDiagnosticCallPiece *construct(PathPieces &pieces, + const Decl *caller); + + void dump() const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == Call; + } +}; + +class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece { + std::vector<PathDiagnosticLocationPair> LPairs; + +public: + PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, + const PathDiagnosticLocation &endPos, + StringRef s) + : PathDiagnosticPiece(s, ControlFlow) { + LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); + } + + PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, + const PathDiagnosticLocation &endPos) + : PathDiagnosticPiece(ControlFlow) { + LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); + } + + ~PathDiagnosticControlFlowPiece() override; + + PathDiagnosticLocation getStartLocation() const { + assert(!LPairs.empty() && + "PathDiagnosticControlFlowPiece needs at least one location."); + return LPairs[0].getStart(); + } + + PathDiagnosticLocation getEndLocation() const { + assert(!LPairs.empty() && + "PathDiagnosticControlFlowPiece needs at least one location."); + return LPairs[0].getEnd(); + } + + void setStartLocation(const PathDiagnosticLocation &L) { + LPairs[0].setStart(L); + } + + void setEndLocation(const PathDiagnosticLocation &L) { + LPairs[0].setEnd(L); + } + + void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); } + + PathDiagnosticLocation getLocation() const override { + return getStartLocation(); + } + + using iterator = std::vector<PathDiagnosticLocationPair>::iterator; + + iterator begin() { return LPairs.begin(); } + iterator end() { return LPairs.end(); } + + void flattenLocations() override { + for (auto &I : *this) + I.flatten(); + } + + using const_iterator = + std::vector<PathDiagnosticLocationPair>::const_iterator; + + const_iterator begin() const { return LPairs.begin(); } + const_iterator end() const { return LPairs.end(); } + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == ControlFlow; + } + + void dump() const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; +}; + +class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece { +public: + PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos) + : PathDiagnosticSpotPiece(pos, "", Macro) {} + ~PathDiagnosticMacroPiece() override; + + PathPieces subPieces; + + bool containsEvent() const; + + void flattenLocations() override { + PathDiagnosticSpotPiece::flattenLocations(); + for (const auto &I : subPieces) + I->flattenLocations(); + } + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == Macro; + } + + void dump() const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; +}; + +class PathDiagnosticNotePiece: public PathDiagnosticSpotPiece { +public: + PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S, + bool AddPosRange = true) + : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {} + ~PathDiagnosticNotePiece() override; + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == Note; + } + + void dump() const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; +}; + +/// File IDs mapped to sets of line numbers. +using FilesToLineNumsMap = std::map<FileID, std::set<unsigned>>; + +/// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive +/// diagnostic. It represents an ordered-collection of PathDiagnosticPieces, +/// each which represent the pieces of the path. +class PathDiagnostic : public llvm::FoldingSetNode { + std::string CheckName; + const Decl *DeclWithIssue; + std::string BugType; + std::string VerboseDesc; + std::string ShortDesc; + std::string Category; + std::deque<std::string> OtherDesc; + + /// Loc The location of the path diagnostic report. + PathDiagnosticLocation Loc; + + PathPieces pathImpl; + SmallVector<PathPieces *, 3> pathStack; + + /// Important bug uniqueing location. + /// The location info is useful to differentiate between bugs. + PathDiagnosticLocation UniqueingLoc; + const Decl *UniqueingDecl; + + /// Lines executed in the path. + std::unique_ptr<FilesToLineNumsMap> ExecutedLines; + +public: + PathDiagnostic() = delete; + PathDiagnostic(StringRef CheckName, const Decl *DeclWithIssue, + StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, + StringRef category, PathDiagnosticLocation LocationToUnique, + const Decl *DeclToUnique, + std::unique_ptr<FilesToLineNumsMap> ExecutedLines); + ~PathDiagnostic(); + + const PathPieces &path; + + /// Return the path currently used by builders for constructing the + /// PathDiagnostic. + PathPieces &getActivePath() { + if (pathStack.empty()) + return pathImpl; + return *pathStack.back(); + } + + /// Return a mutable version of 'path'. + PathPieces &getMutablePieces() { + return pathImpl; + } + + /// Return the unrolled size of the path. + unsigned full_size(); + + void pushActivePath(PathPieces *p) { pathStack.push_back(p); } + void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); } + + bool isWithinCall() const { return !pathStack.empty(); } + + void setEndOfPath(std::shared_ptr<PathDiagnosticPiece> EndPiece) { + assert(!Loc.isValid() && "End location already set!"); + Loc = EndPiece->getLocation(); + assert(Loc.isValid() && "Invalid location for end-of-path piece"); + getActivePath().push_back(std::move(EndPiece)); + } + + void appendToDesc(StringRef S) { + if (!ShortDesc.empty()) + ShortDesc += S; + VerboseDesc += S; + } + + /// If the last piece of the report point to the header file, resets + /// the location of the report to be the last location in the main source + /// file. + void resetDiagnosticLocationToMainFile(); + + StringRef getVerboseDescription() const { return VerboseDesc; } + + StringRef getShortDescription() const { + return ShortDesc.empty() ? VerboseDesc : ShortDesc; + } + + StringRef getCheckName() const { return CheckName; } + StringRef getBugType() const { return BugType; } + StringRef getCategory() const { return Category; } + + /// Return the semantic context where an issue occurred. If the + /// issue occurs along a path, this represents the "central" area + /// where the bug manifests. + const Decl *getDeclWithIssue() const { return DeclWithIssue; } + + using meta_iterator = std::deque<std::string>::const_iterator; + + meta_iterator meta_begin() const { return OtherDesc.begin(); } + meta_iterator meta_end() const { return OtherDesc.end(); } + void addMeta(StringRef s) { OtherDesc.push_back(s); } + + const FilesToLineNumsMap &getExecutedLines() const { + return *ExecutedLines; + } + + FilesToLineNumsMap &getExecutedLines() { + return *ExecutedLines; + } + + PathDiagnosticLocation getLocation() const { + return Loc; + } + + /// Get the location on which the report should be uniqued. + PathDiagnosticLocation getUniqueingLoc() const { + return UniqueingLoc; + } + + /// Get the declaration containing the uniqueing location. + const Decl *getUniqueingDecl() const { + return UniqueingDecl; + } + + void flattenLocations() { + Loc.flatten(); + for (const auto &I : pathImpl) + I->flattenLocations(); + } + + /// Profiles the diagnostic, independent of the path it references. + /// + /// This can be used to merge diagnostics that refer to the same issue + /// along different paths. + void Profile(llvm::FoldingSetNodeID &ID) const; + + /// Profiles the diagnostic, including its path. + /// + /// Two diagnostics with the same issue along different paths will generate + /// different profiles. + void FullProfile(llvm::FoldingSetNodeID &ID) const; +}; + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/Checker.h b/clang-r353983/include/clang/StaticAnalyzer/Core/Checker.h new file mode 100644 index 00000000..db3ae74f --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/Checker.h @@ -0,0 +1,581 @@ +//== Checker.h - Registration mechanism for checkers -------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines Checker, used to create and register checkers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_CHECKER_H +#define LLVM_CLANG_STATICANALYZER_CORE_CHECKER_H + +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LangOptions.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/Support/Casting.h" + +namespace clang { +namespace ento { + class BugReporter; + +namespace check { + +template <typename DECL> +class ASTDecl { + template <typename CHECKER> + static void _checkDecl(void *checker, const Decl *D, AnalysisManager& mgr, + BugReporter &BR) { + ((const CHECKER *)checker)->checkASTDecl(cast<DECL>(D), mgr, BR); + } + + static bool _handlesDecl(const Decl *D) { + return isa<DECL>(D); + } +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForDecl(CheckerManager::CheckDeclFunc(checker, + _checkDecl<CHECKER>), + _handlesDecl); + } +}; + +class ASTCodeBody { + template <typename CHECKER> + static void _checkBody(void *checker, const Decl *D, AnalysisManager& mgr, + BugReporter &BR) { + ((const CHECKER *)checker)->checkASTCodeBody(D, mgr, BR); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForBody(CheckerManager::CheckDeclFunc(checker, + _checkBody<CHECKER>)); + } +}; + +class EndOfTranslationUnit { + template <typename CHECKER> + static void _checkEndOfTranslationUnit(void *checker, + const TranslationUnitDecl *TU, + AnalysisManager& mgr, + BugReporter &BR) { + ((const CHECKER *)checker)->checkEndOfTranslationUnit(TU, mgr, BR); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr){ + mgr._registerForEndOfTranslationUnit( + CheckerManager::CheckEndOfTranslationUnit(checker, + _checkEndOfTranslationUnit<CHECKER>)); + } +}; + +template <typename STMT> +class PreStmt { + template <typename CHECKER> + static void _checkStmt(void *checker, const Stmt *S, CheckerContext &C) { + ((const CHECKER *)checker)->checkPreStmt(cast<STMT>(S), C); + } + + static bool _handlesStmt(const Stmt *S) { + return isa<STMT>(S); + } +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPreStmt(CheckerManager::CheckStmtFunc(checker, + _checkStmt<CHECKER>), + _handlesStmt); + } +}; + +template <typename STMT> +class PostStmt { + template <typename CHECKER> + static void _checkStmt(void *checker, const Stmt *S, CheckerContext &C) { + ((const CHECKER *)checker)->checkPostStmt(cast<STMT>(S), C); + } + + static bool _handlesStmt(const Stmt *S) { + return isa<STMT>(S); + } +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPostStmt(CheckerManager::CheckStmtFunc(checker, + _checkStmt<CHECKER>), + _handlesStmt); + } +}; + +class PreObjCMessage { + template <typename CHECKER> + static void _checkObjCMessage(void *checker, const ObjCMethodCall &msg, + CheckerContext &C) { + ((const CHECKER *)checker)->checkPreObjCMessage(msg, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPreObjCMessage( + CheckerManager::CheckObjCMessageFunc(checker, _checkObjCMessage<CHECKER>)); + } +}; + +class ObjCMessageNil { + template <typename CHECKER> + static void _checkObjCMessage(void *checker, const ObjCMethodCall &msg, + CheckerContext &C) { + ((const CHECKER *)checker)->checkObjCMessageNil(msg, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForObjCMessageNil( + CheckerManager::CheckObjCMessageFunc(checker, _checkObjCMessage<CHECKER>)); + } +}; + +class PostObjCMessage { + template <typename CHECKER> + static void _checkObjCMessage(void *checker, const ObjCMethodCall &msg, + CheckerContext &C) { + ((const CHECKER *)checker)->checkPostObjCMessage(msg, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPostObjCMessage( + CheckerManager::CheckObjCMessageFunc(checker, _checkObjCMessage<CHECKER>)); + } +}; + +class PreCall { + template <typename CHECKER> + static void _checkCall(void *checker, const CallEvent &msg, + CheckerContext &C) { + ((const CHECKER *)checker)->checkPreCall(msg, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPreCall( + CheckerManager::CheckCallFunc(checker, _checkCall<CHECKER>)); + } +}; + +class PostCall { + template <typename CHECKER> + static void _checkCall(void *checker, const CallEvent &msg, + CheckerContext &C) { + ((const CHECKER *)checker)->checkPostCall(msg, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPostCall( + CheckerManager::CheckCallFunc(checker, _checkCall<CHECKER>)); + } +}; + +class Location { + template <typename CHECKER> + static void _checkLocation(void *checker, + const SVal &location, bool isLoad, const Stmt *S, + CheckerContext &C) { + ((const CHECKER *)checker)->checkLocation(location, isLoad, S, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForLocation( + CheckerManager::CheckLocationFunc(checker, _checkLocation<CHECKER>)); + } +}; + +class Bind { + template <typename CHECKER> + static void _checkBind(void *checker, + const SVal &location, const SVal &val, const Stmt *S, + CheckerContext &C) { + ((const CHECKER *)checker)->checkBind(location, val, S, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForBind( + CheckerManager::CheckBindFunc(checker, _checkBind<CHECKER>)); + } +}; + +class EndAnalysis { + template <typename CHECKER> + static void _checkEndAnalysis(void *checker, ExplodedGraph &G, + BugReporter &BR, ExprEngine &Eng) { + ((const CHECKER *)checker)->checkEndAnalysis(G, BR, Eng); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForEndAnalysis( + CheckerManager::CheckEndAnalysisFunc(checker, _checkEndAnalysis<CHECKER>)); + } +}; + +class BeginFunction { + template <typename CHECKER> + static void _checkBeginFunction(void *checker, CheckerContext &C) { + ((const CHECKER *)checker)->checkBeginFunction(C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForBeginFunction(CheckerManager::CheckBeginFunctionFunc( + checker, _checkBeginFunction<CHECKER>)); + } +}; + +class EndFunction { + template <typename CHECKER> + static void _checkEndFunction(void *checker, const ReturnStmt *RS, + CheckerContext &C) { + ((const CHECKER *)checker)->checkEndFunction(RS, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForEndFunction( + CheckerManager::CheckEndFunctionFunc(checker, _checkEndFunction<CHECKER>)); + } +}; + +class BranchCondition { + template <typename CHECKER> + static void _checkBranchCondition(void *checker, const Stmt *Condition, + CheckerContext & C) { + ((const CHECKER *)checker)->checkBranchCondition(Condition, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForBranchCondition( + CheckerManager::CheckBranchConditionFunc(checker, + _checkBranchCondition<CHECKER>)); + } +}; + +class NewAllocator { + template <typename CHECKER> + static void _checkNewAllocator(void *checker, const CXXNewExpr *NE, + SVal Target, CheckerContext &C) { + ((const CHECKER *)checker)->checkNewAllocator(NE, Target, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForNewAllocator( + CheckerManager::CheckNewAllocatorFunc(checker, + _checkNewAllocator<CHECKER>)); + } +}; + +class LiveSymbols { + template <typename CHECKER> + static void _checkLiveSymbols(void *checker, ProgramStateRef state, + SymbolReaper &SR) { + ((const CHECKER *)checker)->checkLiveSymbols(state, SR); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForLiveSymbols( + CheckerManager::CheckLiveSymbolsFunc(checker, _checkLiveSymbols<CHECKER>)); + } +}; + +class DeadSymbols { + template <typename CHECKER> + static void _checkDeadSymbols(void *checker, + SymbolReaper &SR, CheckerContext &C) { + ((const CHECKER *)checker)->checkDeadSymbols(SR, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForDeadSymbols( + CheckerManager::CheckDeadSymbolsFunc(checker, _checkDeadSymbols<CHECKER>)); + } +}; + +class RegionChanges { + template <typename CHECKER> + static ProgramStateRef + _checkRegionChanges(void *checker, + ProgramStateRef state, + const InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> Explicits, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call) { + return ((const CHECKER *) checker)->checkRegionChanges(state, invalidated, + Explicits, Regions, + LCtx, Call); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForRegionChanges( + CheckerManager::CheckRegionChangesFunc(checker, + _checkRegionChanges<CHECKER>)); + } +}; + +class PointerEscape { + template <typename CHECKER> + static ProgramStateRef + _checkPointerEscape(void *Checker, + ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + RegionAndSymbolInvalidationTraits *ETraits) { + + if (!ETraits) + return ((const CHECKER *)Checker)->checkPointerEscape(State, + Escaped, + Call, + Kind); + + InvalidatedSymbols RegularEscape; + for (InvalidatedSymbols::const_iterator I = Escaped.begin(), + E = Escaped.end(); I != E; ++I) + if (!ETraits->hasTrait(*I, + RegionAndSymbolInvalidationTraits::TK_PreserveContents) && + !ETraits->hasTrait(*I, + RegionAndSymbolInvalidationTraits::TK_SuppressEscape)) + RegularEscape.insert(*I); + + if (RegularEscape.empty()) + return State; + + return ((const CHECKER *)Checker)->checkPointerEscape(State, + RegularEscape, + Call, + Kind); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPointerEscape( + CheckerManager::CheckPointerEscapeFunc(checker, + _checkPointerEscape<CHECKER>)); + } +}; + +class ConstPointerEscape { + template <typename CHECKER> + static ProgramStateRef + _checkConstPointerEscape(void *Checker, + ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + RegionAndSymbolInvalidationTraits *ETraits) { + + if (!ETraits) + return State; + + InvalidatedSymbols ConstEscape; + for (InvalidatedSymbols::const_iterator I = Escaped.begin(), + E = Escaped.end(); I != E; ++I) + if (ETraits->hasTrait(*I, + RegionAndSymbolInvalidationTraits::TK_PreserveContents) && + !ETraits->hasTrait(*I, + RegionAndSymbolInvalidationTraits::TK_SuppressEscape)) + ConstEscape.insert(*I); + + if (ConstEscape.empty()) + return State; + + return ((const CHECKER *)Checker)->checkConstPointerEscape(State, + ConstEscape, + Call, + Kind); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPointerEscape( + CheckerManager::CheckPointerEscapeFunc(checker, + _checkConstPointerEscape<CHECKER>)); + } +}; + + +template <typename EVENT> +class Event { + template <typename CHECKER> + static void _checkEvent(void *checker, const void *event) { + ((const CHECKER *)checker)->checkEvent(*(const EVENT *)event); + } +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerListenerForEvent<EVENT>( + CheckerManager::CheckEventFunc(checker, _checkEvent<CHECKER>)); + } +}; + +} // end check namespace + +namespace eval { + +class Assume { + template <typename CHECKER> + static ProgramStateRef _evalAssume(void *checker, + ProgramStateRef state, + const SVal &cond, + bool assumption) { + return ((const CHECKER *)checker)->evalAssume(state, cond, assumption); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForEvalAssume( + CheckerManager::EvalAssumeFunc(checker, _evalAssume<CHECKER>)); + } +}; + +class Call { + template <typename CHECKER> + static bool _evalCall(void *checker, const CallExpr *CE, CheckerContext &C) { + return ((const CHECKER *)checker)->evalCall(CE, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForEvalCall( + CheckerManager::EvalCallFunc(checker, _evalCall<CHECKER>)); + } +}; + +} // end eval namespace + +class CheckerBase : public ProgramPointTag { + CheckName Name; + friend class ::clang::ento::CheckerManager; + +public: + StringRef getTagDescription() const override; + CheckName getCheckName() const; + + /// See CheckerManager::runCheckersForPrintState. + virtual void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { } +}; + +/// Dump checker name to stream. +raw_ostream& operator<<(raw_ostream &Out, const CheckerBase &Checker); + +/// Tag that can use a checker name as a message provider +/// (see SimpleProgramPointTag). +class CheckerProgramPointTag : public SimpleProgramPointTag { +public: + CheckerProgramPointTag(StringRef CheckerName, StringRef Msg); + CheckerProgramPointTag(const CheckerBase *Checker, StringRef Msg); +}; + +template <typename CHECK1, typename... CHECKs> +class Checker : public CHECK1, public CHECKs..., public CheckerBase { +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + CHECK1::_register(checker, mgr); + Checker<CHECKs...>::_register(checker, mgr); + } +}; + +template <typename CHECK1> +class Checker<CHECK1> : public CHECK1, public CheckerBase { +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + CHECK1::_register(checker, mgr); + } +}; + +template <typename EVENT> +class EventDispatcher { + CheckerManager *Mgr; +public: + EventDispatcher() : Mgr(nullptr) { } + + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerDispatcherForEvent<EVENT>(); + static_cast<EventDispatcher<EVENT> *>(checker)->Mgr = &mgr; + } + + void dispatchEvent(const EVENT &event) const { + Mgr->_dispatchEvent(event); + } +}; + +/// We dereferenced a location that may be null. +struct ImplicitNullDerefEvent { + SVal Location; + bool IsLoad; + ExplodedNode *SinkNode; + BugReporter *BR; + // When true, the dereference is in the source code directly. When false, the + // dereference might happen later (for example pointer passed to a parameter + // that is marked with nonnull attribute.) + bool IsDirectDereference; + + static int Tag; +}; + +/// A helper class which wraps a boolean value set to false by default. +/// +/// This class should behave exactly like 'bool' except that it doesn't need to +/// be explicitly initialized. +struct DefaultBool { + bool val; + DefaultBool() : val(false) {} + /*implicit*/ operator bool&() { return val; } + /*implicit*/ operator const bool&() const { return val; } + DefaultBool &operator=(bool b) { val = b; return *this; } +}; + +} // end ento namespace + +} // end clang namespace + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/CheckerManager.h b/clang-r353983/include/clang/StaticAnalyzer/Core/CheckerManager.h new file mode 100644 index 00000000..b4de26d7 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -0,0 +1,657 @@ +//===- 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/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 CXXNewExpr; +class Decl; +class LocationContext; +class Stmt; +class TranslationUnitDecl; + +namespace ento { + +class AnalysisManager; +class BugReporter; +class CallEvent; +class CheckerBase; +class CheckerContext; +class CheckerRegistry; +class ExplodedGraph; +class ExplodedNode; +class ExplodedNodeSet; +class ExprEngine; +class MemRegion; +struct 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, + + /// 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 check +// name strings have a lifetime that keeps them alive at least until the path +// diagnostics have been processed. +class CheckName { + friend class ::clang::ento::CheckerRegistry; + + StringRef Name; + + explicit CheckName(StringRef Name) : Name(Name) {} + +public: + CheckName() = default; + + StringRef getName() const { return Name; } +}; + +enum class ObjCMessageVisitKind { + Pre, + Post, + MessageNil +}; + +class CheckerManager { + ASTContext &Context; + const LangOptions LangOpts; + AnalyzerOptions &AOptions; + CheckName CurrentCheckName; + +public: + CheckerManager(ASTContext &Context, AnalyzerOptions &AOptions) + : Context(Context), LangOpts(Context.getLangOpts()), AOptions(AOptions) {} + + ~CheckerManager(); + + void setCurrentCheckName(CheckName name) { CurrentCheckName = name; } + CheckName getCurrentCheckName() const { return CurrentCheckName; } + + bool hasPathSensitiveCheckers() const; + + void finishedCheckerRegistration(); + + const LangOptions &getLangOpts() const { return LangOpts; } + AnalyzerOptions &getAnalyzerOptions() { return AOptions; } + ASTContext &getASTContext() { return Context; } + + 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 = CurrentCheckName; + 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 CXXNewExpr *NE, SVal Target, + 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); + + /// 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 Sep The preferred separator between different kinds of data. + void runCheckersForPrintState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep); + +//===----------------------------------------------------------------------===// +// 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 (const SVal &location, bool isLoad, const Stmt *S, + CheckerContext &)>; + + using CheckBindFunc = + CheckerFn<void (const SVal &location, const 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 CXXNewExpr *, SVal, 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, const SVal &cond, + bool assumption)>; + + using EvalCallFunc = CheckerFn<bool (const CallExpr *, 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); + + 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 diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/IssueHash.h b/clang-r353983/include/clang/StaticAnalyzer/Core/IssueHash.h new file mode 100644 index 00000000..38d5f847 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/IssueHash.h @@ -0,0 +1,50 @@ +//===---------- IssueHash.h - Generate identification hashes ----*- 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 +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_STATICANALYZER_CORE_ISSUE_HASH_H +#define LLVM_CLANG_STATICANALYZER_CORE_ISSUE_HASH_H + +#include "llvm/ADT/SmallString.h" + +namespace clang { +class Decl; +class SourceManager; +class FullSourceLoc; +class LangOptions; + +/// Get an MD5 hash to help identify bugs. +/// +/// This function returns a hash that helps identify bugs within a source file. +/// This identification can be utilized to diff diagnostic results on different +/// snapshots of a projects, or maintain a database of suppressed diagnotics. +/// +/// The hash contains the normalized text of the location associated with the +/// diagnostic. Normalization means removing the whitespaces. The associated +/// location is the either the last location of a diagnostic path or a uniqueing +/// location. The bugtype and the name of the checker is also part of the hash. +/// The last component is the string representation of the enclosing declaration +/// of the associated location. +/// +/// In case a new hash is introduced, the old one should still be maintained for +/// a while. One should not introduce a new hash for every change, it is +/// possible to introduce experimental hashes that may change in the future. +/// Such hashes should be marked as experimental using a comment in the plist +/// files. +llvm::SmallString<32> GetIssueHash(const SourceManager &SM, + FullSourceLoc &IssueLoc, + llvm::StringRef CheckerName, + llvm::StringRef BugType, const Decl *D, + const LangOptions &LangOpts); + +/// Get the string representation of issue hash. See GetIssueHash() for +/// more information. +std::string GetIssueString(const SourceManager &SM, FullSourceLoc &IssueLoc, + llvm::StringRef CheckerName, llvm::StringRef BugType, + const Decl *D, const LangOptions &LangOpts); +} // namespace clang + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h new file mode 100644 index 00000000..ef6e7e0f --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h @@ -0,0 +1,39 @@ +//===--- PathDiagnosticConsumers.h - Path Diagnostic Clients ------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the interface to create different path diagostic clients. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHDIAGNOSTICCONSUMERS_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHDIAGNOSTICCONSUMERS_H + +#include <string> +#include <vector> + +namespace clang { + +class AnalyzerOptions; +class Preprocessor; + +namespace ento { + +class PathDiagnosticConsumer; +typedef std::vector<PathDiagnosticConsumer*> PathDiagnosticConsumers; + +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN)\ +void CREATEFN(AnalyzerOptions &AnalyzerOpts,\ + PathDiagnosticConsumers &C,\ + const std::string &Prefix,\ + const Preprocessor &PP); +#include "clang/StaticAnalyzer/Core/Analyses.def" + +} // end 'ento' namespace +} // end 'clang' namespace + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h new file mode 100644 index 00000000..4b7d6054 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h @@ -0,0 +1,108 @@ +//== APSIntType.h - Simple record of the type of APSInts --------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_APSINTTYPE_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_APSINTTYPE_H + +#include "llvm/ADT/APSInt.h" +#include <tuple> + +namespace clang { +namespace ento { + +/// A record of the "type" of an APSInt, used for conversions. +class APSIntType { + uint32_t BitWidth; + bool IsUnsigned; + +public: + APSIntType(uint32_t Width, bool Unsigned) + : BitWidth(Width), IsUnsigned(Unsigned) {} + + /* implicit */ APSIntType(const llvm::APSInt &Value) + : BitWidth(Value.getBitWidth()), IsUnsigned(Value.isUnsigned()) {} + + uint32_t getBitWidth() const { return BitWidth; } + bool isUnsigned() const { return IsUnsigned; } + + /// Convert a given APSInt, in place, to match this type. + /// + /// This behaves like a C cast: converting 255u8 (0xFF) to s16 gives + /// 255 (0x00FF), and converting -1s8 (0xFF) to u16 gives 65535 (0xFFFF). + void apply(llvm::APSInt &Value) const { + // Note the order here. We extend first to preserve the sign, if this value + // is signed, /then/ match the signedness of the result type. + Value = Value.extOrTrunc(BitWidth); + Value.setIsUnsigned(IsUnsigned); + } + + /// Convert and return a new APSInt with the given value, but this + /// type's bit width and signedness. + /// + /// \see apply + llvm::APSInt convert(const llvm::APSInt &Value) const LLVM_READONLY { + llvm::APSInt Result(Value, Value.isUnsigned()); + apply(Result); + return Result; + } + + /// Returns an all-zero value for this type. + llvm::APSInt getZeroValue() const LLVM_READONLY { + return llvm::APSInt(BitWidth, IsUnsigned); + } + + /// Returns the minimum value for this type. + llvm::APSInt getMinValue() const LLVM_READONLY { + return llvm::APSInt::getMinValue(BitWidth, IsUnsigned); + } + + /// Returns the maximum value for this type. + llvm::APSInt getMaxValue() const LLVM_READONLY { + return llvm::APSInt::getMaxValue(BitWidth, IsUnsigned); + } + + llvm::APSInt getValue(uint64_t RawValue) const LLVM_READONLY { + return (llvm::APSInt(BitWidth, IsUnsigned) = RawValue); + } + + /// Used to classify whether a value is representable using this type. + /// + /// \see testInRange + enum RangeTestResultKind { + RTR_Below = -1, ///< Value is less than the minimum representable value. + RTR_Within = 0, ///< Value is representable using this type. + RTR_Above = 1 ///< Value is greater than the maximum representable value. + }; + + /// Tests whether a given value is losslessly representable using this type. + /// + /// \param Val The value to test. + /// \param AllowMixedSign Whether or not to allow signedness conversions. + /// This determines whether -1s8 is considered in range + /// for 'unsigned char' (u8). + RangeTestResultKind testInRange(const llvm::APSInt &Val, + bool AllowMixedSign) const LLVM_READONLY; + + bool operator==(const APSIntType &Other) const { + return BitWidth == Other.BitWidth && IsUnsigned == Other.IsUnsigned; + } + + /// Provide an ordering for finding a common conversion type. + /// + /// Unsigned integers are considered to be better conversion types than + /// signed integers of the same width. + bool operator<(const APSIntType &Other) const { + return std::tie(BitWidth, IsUnsigned) < + std::tie(Other.BitWidth, Other.IsUnsigned); + } +}; + +} // end ento namespace +} // end clang namespace + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h new file mode 100644 index 00000000..b0dda78a --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h @@ -0,0 +1,161 @@ +//== AnalysisManager.h - Path sensitive analysis data 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the AnalysisManager class that manages the data and policy +// for path sensitive analysis. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ANALYSISMANAGER_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ANALYSISMANAGER_H + +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" + +namespace clang { + +class CodeInjector; + +namespace ento { + class CheckerManager; + +class AnalysisManager : public BugReporterData { + virtual void anchor(); + AnalysisDeclContextManager AnaCtxMgr; + + ASTContext &Ctx; + DiagnosticsEngine &Diags; + const LangOptions &LangOpts; + PathDiagnosticConsumers PathConsumers; + + // Configurable components creators. + StoreManagerCreator CreateStoreMgr; + ConstraintManagerCreator CreateConstraintMgr; + + CheckerManager *CheckerMgr; + +public: + AnalyzerOptions &options; + + AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, + const PathDiagnosticConsumers &Consumers, + StoreManagerCreator storemgr, + ConstraintManagerCreator constraintmgr, + CheckerManager *checkerMgr, AnalyzerOptions &Options, + CodeInjector *injector = nullptr); + + ~AnalysisManager() override; + + void ClearContexts() { + AnaCtxMgr.clear(); + } + + AnalysisDeclContextManager& getAnalysisDeclContextManager() { + return AnaCtxMgr; + } + + StoreManagerCreator getStoreManagerCreator() { + return CreateStoreMgr; + } + + AnalyzerOptions& getAnalyzerOptions() override { + return options; + } + + ConstraintManagerCreator getConstraintManagerCreator() { + return CreateConstraintMgr; + } + + CheckerManager *getCheckerManager() const { return CheckerMgr; } + + ASTContext &getASTContext() override { + return Ctx; + } + + SourceManager &getSourceManager() override { + return getASTContext().getSourceManager(); + } + + DiagnosticsEngine &getDiagnostic() override { + return Diags; + } + + const LangOptions &getLangOpts() const { + return LangOpts; + } + + ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() override { + return PathConsumers; + } + + void FlushDiagnostics(); + + bool shouldVisualize() const { + return options.visualizeExplodedGraphWithGraphViz; + } + + bool shouldInlineCall() const { + return options.getIPAMode() != IPAK_None; + } + + CFG *getCFG(Decl const *D) { + return AnaCtxMgr.getContext(D)->getCFG(); + } + + template <typename T> + T *getAnalysis(Decl const *D) { + return AnaCtxMgr.getContext(D)->getAnalysis<T>(); + } + + ParentMap &getParentMap(Decl const *D) { + return AnaCtxMgr.getContext(D)->getParentMap(); + } + + AnalysisDeclContext *getAnalysisDeclContext(const Decl *D) { + return AnaCtxMgr.getContext(D); + } + + static bool isInCodeFile(SourceLocation SL, const SourceManager &SM) { + if (SM.isInMainFile(SL)) + return true; + + // Support the "unified sources" compilation method (eg. WebKit) that + // involves producing non-header files that include other non-header files. + // We should be included directly from a UnifiedSource* file + // and we shouldn't be a header - which is a very safe defensive check. + SourceLocation IL = SM.getIncludeLoc(SM.getFileID(SL)); + if (!IL.isValid() || !SM.isInMainFile(IL)) + return false; + // Should rather be "file name starts with", but the current .getFilename + // includes the full path. + if (SM.getFilename(IL).contains("UnifiedSource")) { + // It might be great to reuse FrontendOptions::getInputKindForExtension() + // but for now it doesn't discriminate between code and header files. + return llvm::StringSwitch<bool>(SM.getFilename(SL).rsplit('.').second) + .Cases("c", "m", "mm", "C", "cc", "cp", true) + .Cases("cpp", "CPP", "c++", "cxx", "cppm", true) + .Default(false); + } + + return false; + } + + bool isInCodeFile(SourceLocation SL) { + const SourceManager &SM = getASTContext().getSourceManager(); + return isInCodeFile(SL, SM); + } +}; + +} // enAnaCtxMgrspace + +} // end clang namespace + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h new file mode 100644 index 00000000..ac218bc0 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -0,0 +1,270 @@ +//==- BasicValueFactory.h - Basic values for Path Sens analysis --*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicValueFactory, a class that manages the lifetime +// of APSInt objects and symbolic constraints used by ExprEngine +// and related classes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_BASICVALUEFACTORY_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_BASICVALUEFACTORY_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Allocator.h" +#include <cassert> +#include <cstdint> +#include <utility> + +namespace clang { + +class CXXBaseSpecifier; +class DeclaratorDecl; + +namespace ento { + +class CompoundValData : public llvm::FoldingSetNode { + QualType T; + llvm::ImmutableList<SVal> L; + +public: + CompoundValData(QualType t, llvm::ImmutableList<SVal> l) : T(t), L(l) { + assert(NonLoc::isCompoundType(t)); + } + + using iterator = llvm::ImmutableList<SVal>::iterator; + + iterator begin() const { return L.begin(); } + iterator end() const { return L.end(); } + + static void Profile(llvm::FoldingSetNodeID& ID, QualType T, + llvm::ImmutableList<SVal> L); + + void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, T, L); } +}; + +class LazyCompoundValData : public llvm::FoldingSetNode { + StoreRef store; + const TypedValueRegion *region; + +public: + LazyCompoundValData(const StoreRef &st, const TypedValueRegion *r) + : store(st), region(r) { + assert(NonLoc::isCompoundType(r->getValueType())); + } + + const void *getStore() const { return store.getStore(); } + const TypedValueRegion *getRegion() const { return region; } + + static void Profile(llvm::FoldingSetNodeID& ID, + const StoreRef &store, + const TypedValueRegion *region); + + void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, store, region); } +}; + +class PointerToMemberData : public llvm::FoldingSetNode { + const DeclaratorDecl *D; + llvm::ImmutableList<const CXXBaseSpecifier *> L; + +public: + PointerToMemberData(const DeclaratorDecl *D, + llvm::ImmutableList<const CXXBaseSpecifier *> L) + : D(D), L(L) {} + + using iterator = llvm::ImmutableList<const CXXBaseSpecifier *>::iterator; + + iterator begin() const { return L.begin(); } + iterator end() const { return L.end(); } + + static void Profile(llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D, + llvm::ImmutableList<const CXXBaseSpecifier *> L); + + void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, D, L); } + const DeclaratorDecl *getDeclaratorDecl() const {return D;} + + llvm::ImmutableList<const CXXBaseSpecifier *> getCXXBaseList() const { + return L; + } +}; + +class BasicValueFactory { + using APSIntSetTy = + llvm::FoldingSet<llvm::FoldingSetNodeWrapper<llvm::APSInt>>; + + ASTContext &Ctx; + llvm::BumpPtrAllocator& BPAlloc; + + APSIntSetTy APSIntSet; + void *PersistentSVals = nullptr; + void *PersistentSValPairs = nullptr; + + llvm::ImmutableList<SVal>::Factory SValListFactory; + llvm::ImmutableList<const CXXBaseSpecifier *>::Factory CXXBaseListFactory; + llvm::FoldingSet<CompoundValData> CompoundValDataSet; + llvm::FoldingSet<LazyCompoundValData> LazyCompoundValDataSet; + llvm::FoldingSet<PointerToMemberData> PointerToMemberDataSet; + + // This is private because external clients should use the factory + // method that takes a QualType. + const llvm::APSInt& getValue(uint64_t X, unsigned BitWidth, bool isUnsigned); + +public: + BasicValueFactory(ASTContext &ctx, llvm::BumpPtrAllocator &Alloc) + : Ctx(ctx), BPAlloc(Alloc), SValListFactory(Alloc), + CXXBaseListFactory(Alloc) {} + + ~BasicValueFactory(); + + ASTContext &getContext() const { return Ctx; } + + const llvm::APSInt& getValue(const llvm::APSInt& X); + const llvm::APSInt& getValue(const llvm::APInt& X, bool isUnsigned); + const llvm::APSInt& getValue(uint64_t X, QualType T); + + /// Returns the type of the APSInt used to store values of the given QualType. + APSIntType getAPSIntType(QualType T) const { + assert(T->isIntegralOrEnumerationType() || Loc::isLocType(T)); + return APSIntType(Ctx.getIntWidth(T), + !T->isSignedIntegerOrEnumerationType()); + } + + /// Convert - Create a new persistent APSInt with the same value as 'From' + /// but with the bitwidth and signedness of 'To'. + const llvm::APSInt &Convert(const llvm::APSInt& To, + const llvm::APSInt& From) { + APSIntType TargetType(To); + if (TargetType == APSIntType(From)) + return From; + + return getValue(TargetType.convert(From)); + } + + const llvm::APSInt &Convert(QualType T, const llvm::APSInt &From) { + APSIntType TargetType = getAPSIntType(T); + if (TargetType == APSIntType(From)) + return From; + + return getValue(TargetType.convert(From)); + } + + const llvm::APSInt &getIntValue(uint64_t X, bool isUnsigned) { + QualType T = isUnsigned ? Ctx.UnsignedIntTy : Ctx.IntTy; + return getValue(X, T); + } + + const llvm::APSInt &getMaxValue(const llvm::APSInt &v) { + return getValue(APSIntType(v).getMaxValue()); + } + + const llvm::APSInt &getMinValue(const llvm::APSInt &v) { + return getValue(APSIntType(v).getMinValue()); + } + + const llvm::APSInt &getMaxValue(QualType T) { + return getValue(getAPSIntType(T).getMaxValue()); + } + + const llvm::APSInt &getMinValue(QualType T) { + return getValue(getAPSIntType(T).getMinValue()); + } + + const llvm::APSInt &Add1(const llvm::APSInt &V) { + llvm::APSInt X = V; + ++X; + return getValue(X); + } + + const llvm::APSInt &Sub1(const llvm::APSInt &V) { + llvm::APSInt X = V; + --X; + return getValue(X); + } + + const llvm::APSInt &getZeroWithTypeSize(QualType T) { + assert(T->isScalarType()); + return getValue(0, Ctx.getTypeSize(T), true); + } + + const llvm::APSInt &getZeroWithPtrWidth(bool isUnsigned = true) { + return getValue(0, Ctx.getTypeSize(Ctx.VoidPtrTy), isUnsigned); + } + + const llvm::APSInt &getIntWithPtrWidth(uint64_t X, bool isUnsigned) { + return getValue(X, Ctx.getTypeSize(Ctx.VoidPtrTy), isUnsigned); + } + + const llvm::APSInt &getTruthValue(bool b, QualType T) { + return getValue(b ? 1 : 0, Ctx.getIntWidth(T), + T->isUnsignedIntegerOrEnumerationType()); + } + + const llvm::APSInt &getTruthValue(bool b) { + return getTruthValue(b, Ctx.getLogicalOperationType()); + } + + const CompoundValData *getCompoundValData(QualType T, + llvm::ImmutableList<SVal> Vals); + + const LazyCompoundValData *getLazyCompoundValData(const StoreRef &store, + const TypedValueRegion *region); + + const PointerToMemberData *getPointerToMemberData( + const DeclaratorDecl *DD, + llvm::ImmutableList<const CXXBaseSpecifier *> L); + + llvm::ImmutableList<SVal> getEmptySValList() { + return SValListFactory.getEmptyList(); + } + + llvm::ImmutableList<SVal> prependSVal(SVal X, llvm::ImmutableList<SVal> L) { + return SValListFactory.add(X, L); + } + + llvm::ImmutableList<const CXXBaseSpecifier *> getEmptyCXXBaseList() { + return CXXBaseListFactory.getEmptyList(); + } + + llvm::ImmutableList<const CXXBaseSpecifier *> prependCXXBase( + const CXXBaseSpecifier *CBS, + llvm::ImmutableList<const CXXBaseSpecifier *> L) { + return CXXBaseListFactory.add(CBS, L); + } + + const PointerToMemberData *accumCXXBase( + llvm::iterator_range<CastExpr::path_const_iterator> PathRange, + const nonloc::PointerToMember &PTM); + + const llvm::APSInt* evalAPSInt(BinaryOperator::Opcode Op, + const llvm::APSInt& V1, + const llvm::APSInt& V2); + + const std::pair<SVal, uintptr_t>& + getPersistentSValWithData(const SVal& V, uintptr_t Data); + + const std::pair<SVal, SVal>& + getPersistentSValPair(const SVal& V1, const SVal& V2); + + const SVal* getPersistentSVal(SVal X); +}; + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_BASICVALUEFACTORY_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h new file mode 100644 index 00000000..46ff69e0 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h @@ -0,0 +1,59 @@ +//==- BlockCounter.h - ADT for counting block visits ---------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines BlockCounter, an abstract data type used to count +// the number of times a given block has been visited along a path +// analyzed by CoreEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_BLOCKCOUNTER_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_BLOCKCOUNTER_H + +#include "llvm/Support/Allocator.h" + +namespace clang { + +class StackFrameContext; + +namespace ento { + +/// \class BlockCounter +/// An abstract data type used to count the number of times a given +/// block has been visited along a path analyzed by CoreEngine. +class BlockCounter { + void *Data; + + BlockCounter(void *D) : Data(D) {} + +public: + BlockCounter() : Data(nullptr) {} + + unsigned getNumVisited(const StackFrameContext *CallSite, + unsigned BlockID) const; + + class Factory { + void *F; + public: + Factory(llvm::BumpPtrAllocator& Alloc); + ~Factory(); + + BlockCounter GetEmptyCounter(); + BlockCounter IncrementCount(BlockCounter BC, + const StackFrameContext *CallSite, + unsigned BlockID); + }; + + friend class Factory; +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h new file mode 100644 index 00000000..19996cf9 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -0,0 +1,1229 @@ +//===- CallEvent.h - Wrapper for all function and method calls --*- 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 +// +//===----------------------------------------------------------------------===// +// +/// \file This file defines CallEvent and its subclasses, which represent path- +/// sensitive instances of different kinds of function and method calls +/// (C, C++, and Objective-C). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLEVENT_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLEVENT_H + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include <cassert> +#include <limits> +#include <utility> + +namespace clang { + +class LocationContext; +class ProgramPoint; +class ProgramPointTag; +class StackFrameContext; + +namespace ento { + +enum CallEventKind { + CE_Function, + CE_CXXMember, + CE_CXXMemberOperator, + CE_CXXDestructor, + CE_BEG_CXX_INSTANCE_CALLS = CE_CXXMember, + CE_END_CXX_INSTANCE_CALLS = CE_CXXDestructor, + CE_CXXConstructor, + CE_CXXAllocator, + CE_BEG_FUNCTION_CALLS = CE_Function, + CE_END_FUNCTION_CALLS = CE_CXXAllocator, + CE_Block, + CE_ObjCMessage +}; + +class CallEvent; + +/// This class represents a description of a function call using the number of +/// arguments and the name of the function. +class CallDescription { + friend CallEvent; + + mutable IdentifierInfo *II = nullptr; + mutable bool IsLookupDone = false; + // The list of the qualified names used to identify the specified CallEvent, + // e.g. "{a, b}" represent the qualified names, like "a::b". + std::vector<const char *> QualifiedName; + unsigned RequiredArgs; + +public: + const static unsigned NoArgRequirement = std::numeric_limits<unsigned>::max(); + + /// Constructs a CallDescription object. + /// + /// @param QualifiedName The list of the name qualifiers of the function that + /// will be matched. The user is allowed to skip any of the qualifiers. + /// For example, {"std", "basic_string", "c_str"} would match both + /// std::basic_string<...>::c_str() and std::__1::basic_string<...>::c_str(). + /// + /// @param RequiredArgs The number of arguments that is expected to match a + /// call. Omit this parameter to match every occurrence of call with a given + /// name regardless the number of arguments. + CallDescription(ArrayRef<const char *> QualifiedName, + unsigned RequiredArgs = NoArgRequirement) + : QualifiedName(QualifiedName), RequiredArgs(RequiredArgs) {} + + /// Get the name of the function that this object matches. + StringRef getFunctionName() const { return QualifiedName.back(); } +}; + +template<typename T = CallEvent> +class CallEventRef : public IntrusiveRefCntPtr<const T> { +public: + CallEventRef(const T *Call) : IntrusiveRefCntPtr<const T>(Call) {} + CallEventRef(const CallEventRef &Orig) : IntrusiveRefCntPtr<const T>(Orig) {} + + CallEventRef<T> cloneWithState(ProgramStateRef State) const { + return this->get()->template cloneWithState<T>(State); + } + + // Allow implicit conversions to a superclass type, since CallEventRef + // behaves like a pointer-to-const. + template <typename SuperT> + operator CallEventRef<SuperT> () const { + return this->get(); + } +}; + +/// \class RuntimeDefinition +/// Defines the runtime definition of the called function. +/// +/// Encapsulates the information we have about which Decl will be used +/// when the call is executed on the given path. When dealing with dynamic +/// dispatch, the information is based on DynamicTypeInfo and might not be +/// precise. +class RuntimeDefinition { + /// The Declaration of the function which could be called at runtime. + /// NULL if not available. + const Decl *D = nullptr; + + /// The region representing an object (ObjC/C++) on which the method is + /// called. With dynamic dispatch, the method definition depends on the + /// runtime type of this object. NULL when the DynamicTypeInfo is + /// precise. + const MemRegion *R = nullptr; + +public: + RuntimeDefinition() = default; + RuntimeDefinition(const Decl *InD): D(InD) {} + RuntimeDefinition(const Decl *InD, const MemRegion *InR): D(InD), R(InR) {} + + const Decl *getDecl() { return D; } + + /// Check if the definition we have is precise. + /// If not, it is possible that the call dispatches to another definition at + /// execution time. + bool mayHaveOtherDefinitions() { return R != nullptr; } + + /// When other definitions are possible, returns the region whose runtime type + /// determines the method definition. + const MemRegion *getDispatchRegion() { return R; } +}; + +/// Represents an abstract call to a function or method along a +/// particular path. +/// +/// CallEvents are created through the factory methods of CallEventManager. +/// +/// CallEvents should always be cheap to create and destroy. In order for +/// CallEventManager to be able to re-use CallEvent-sized memory blocks, +/// subclasses of CallEvent may not add any data members to the base class. +/// Use the "Data" and "Location" fields instead. +class CallEvent { +public: + using Kind = CallEventKind; + +private: + ProgramStateRef State; + const LocationContext *LCtx; + llvm::PointerUnion<const Expr *, const Decl *> Origin; + +protected: + // This is user data for subclasses. + const void *Data; + + // This is user data for subclasses. + // This should come right before RefCount, so that the two fields can be + // packed together on LP64 platforms. + SourceLocation Location; + +private: + template <typename T> friend struct llvm::IntrusiveRefCntPtrInfo; + + mutable unsigned RefCount = 0; + + void Retain() const { ++RefCount; } + void Release() const; + +protected: + friend class CallEventManager; + + CallEvent(const Expr *E, ProgramStateRef state, const LocationContext *lctx) + : State(std::move(state)), LCtx(lctx), Origin(E) {} + + CallEvent(const Decl *D, ProgramStateRef state, const LocationContext *lctx) + : State(std::move(state)), LCtx(lctx), Origin(D) {} + + // DO NOT MAKE PUBLIC + CallEvent(const CallEvent &Original) + : State(Original.State), LCtx(Original.LCtx), Origin(Original.Origin), + Data(Original.Data), Location(Original.Location) {} + + /// Copies this CallEvent, with vtable intact, into a new block of memory. + virtual void cloneTo(void *Dest) const = 0; + + /// Get the value of arbitrary expressions at this point in the path. + SVal getSVal(const Stmt *S) const { + return getState()->getSVal(S, getLocationContext()); + } + + using ValueList = SmallVectorImpl<SVal>; + + /// Used to specify non-argument regions that will be invalidated as a + /// result of this call. + virtual void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const {} + +public: + CallEvent &operator=(const CallEvent &) = delete; + virtual ~CallEvent() = default; + + /// Returns the kind of call this is. + virtual Kind getKind() const = 0; + + /// Returns the declaration of the function or method that will be + /// called. May be null. + virtual const Decl *getDecl() const { + return Origin.dyn_cast<const Decl *>(); + } + + /// The state in which the call is being evaluated. + const ProgramStateRef &getState() const { + return State; + } + + /// The context in which the call is being evaluated. + const LocationContext *getLocationContext() const { + return LCtx; + } + + /// Returns the definition of the function or method that will be + /// called. + virtual RuntimeDefinition getRuntimeDefinition() const = 0; + + /// Returns the expression whose value will be the result of this call. + /// May be null. + const Expr *getOriginExpr() const { + return Origin.dyn_cast<const Expr *>(); + } + + /// Returns the number of arguments (explicit and implicit). + /// + /// Note that this may be greater than the number of parameters in the + /// callee's declaration, and that it may include arguments not written in + /// the source. + virtual unsigned getNumArgs() const = 0; + + /// Returns true if the callee is known to be from a system header. + bool isInSystemHeader() const { + const Decl *D = getDecl(); + if (!D) + return false; + + SourceLocation Loc = D->getLocation(); + if (Loc.isValid()) { + const SourceManager &SM = + getState()->getStateManager().getContext().getSourceManager(); + return SM.isInSystemHeader(D->getLocation()); + } + + // Special case for implicitly-declared global operator new/delete. + // These should be considered system functions. + if (const auto *FD = dyn_cast<FunctionDecl>(D)) + return FD->isOverloadedOperator() && FD->isImplicit() && FD->isGlobal(); + + return false; + } + + /// Returns true if the CallEvent is a call to a function that matches + /// the CallDescription. + /// + /// Note that this function is not intended to be used to match Obj-C method + /// calls. + bool isCalled(const CallDescription &CD) const; + + /// Returns a source range for the entire call, suitable for + /// outputting in diagnostics. + virtual SourceRange getSourceRange() const { + return getOriginExpr()->getSourceRange(); + } + + /// Returns the value of a given argument at the time of the call. + virtual SVal getArgSVal(unsigned Index) const; + + /// Returns the expression associated with a given argument. + /// May be null if this expression does not appear in the source. + virtual const Expr *getArgExpr(unsigned Index) const { return nullptr; } + + /// Returns the source range for errors associated with this argument. + /// + /// May be invalid if the argument is not written in the source. + virtual SourceRange getArgSourceRange(unsigned Index) const; + + /// Returns the result type, adjusted for references. + QualType getResultType() const; + + /// Returns the return value of the call. + /// + /// This should only be called if the CallEvent was created using a state in + /// which the return value has already been bound to the origin expression. + SVal getReturnValue() const; + + /// Returns true if the type of any of the non-null arguments satisfies + /// the condition. + bool hasNonNullArgumentsWithType(bool (*Condition)(QualType)) const; + + /// Returns true if any of the arguments appear to represent callbacks. + bool hasNonZeroCallbackArg() const; + + /// Returns true if any of the arguments is void*. + bool hasVoidPointerToNonConstArg() const; + + /// Returns true if any of the arguments are known to escape to long- + /// term storage, even if this method will not modify them. + // NOTE: The exact semantics of this are still being defined! + // We don't really want a list of hardcoded exceptions in the long run, + // but we don't want duplicated lists of known APIs in the short term either. + virtual bool argumentsMayEscape() const { + return hasNonZeroCallbackArg(); + } + + /// Returns true if the callee is an externally-visible function in the + /// top-level namespace, such as \c malloc. + /// + /// You can use this call to determine that a particular function really is + /// a library function and not, say, a C++ member function with the same name. + /// + /// If a name is provided, the function must additionally match the given + /// name. + /// + /// Note that this deliberately excludes C++ library functions in the \c std + /// namespace, but will include C library functions accessed through the + /// \c std namespace. This also does not check if the function is declared + /// as 'extern "C"', or if it uses C++ name mangling. + // FIXME: Add a helper for checking namespaces. + // FIXME: Move this down to AnyFunctionCall once checkers have more + // precise callbacks. + bool isGlobalCFunction(StringRef SpecificName = StringRef()) const; + + /// Returns the name of the callee, if its name is a simple identifier. + /// + /// Note that this will fail for Objective-C methods, blocks, and C++ + /// overloaded operators. The former is named by a Selector rather than a + /// simple identifier, and the latter two do not have names. + // FIXME: Move this down to AnyFunctionCall once checkers have more + // precise callbacks. + const IdentifierInfo *getCalleeIdentifier() const { + const auto *ND = dyn_cast_or_null<NamedDecl>(getDecl()); + if (!ND) + return nullptr; + return ND->getIdentifier(); + } + + /// Returns an appropriate ProgramPoint for this call. + ProgramPoint getProgramPoint(bool IsPreVisit = false, + const ProgramPointTag *Tag = nullptr) const; + + /// Returns a new state with all argument regions invalidated. + /// + /// This accepts an alternate state in case some processing has already + /// occurred. + ProgramStateRef invalidateRegions(unsigned BlockCount, + ProgramStateRef Orig = nullptr) const; + + using FrameBindingTy = std::pair<Loc, SVal>; + using BindingsTy = SmallVectorImpl<FrameBindingTy>; + + /// Populates the given SmallVector with the bindings in the callee's stack + /// frame at the start of this call. + virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const = 0; + + /// Returns a copy of this CallEvent, but using the given state. + template <typename T> + CallEventRef<T> cloneWithState(ProgramStateRef NewState) const; + + /// Returns a copy of this CallEvent, but using the given state. + CallEventRef<> cloneWithState(ProgramStateRef NewState) const { + return cloneWithState<CallEvent>(NewState); + } + + /// Returns true if this is a statement is a function or method call + /// of some kind. + static bool isCallStmt(const Stmt *S); + + /// Returns the result type of a function or method declaration. + /// + /// This will return a null QualType if the result type cannot be determined. + static QualType getDeclaredResultType(const Decl *D); + + /// Returns true if the given decl is known to be variadic. + /// + /// \p D must not be null. + static bool isVariadic(const Decl *D); + + /// Returns AnalysisDeclContext for the callee stack frame. + /// Currently may fail; returns null on failure. + AnalysisDeclContext *getCalleeAnalysisDeclContext() const; + + /// Returns the callee stack frame. That stack frame will only be entered + /// during analysis if the call is inlined, but it may still be useful + /// in intermediate calculations even if the call isn't inlined. + /// May fail; returns null on failure. + const StackFrameContext *getCalleeStackFrame() const; + + /// Returns memory location for a parameter variable within the callee stack + /// frame. May fail; returns null on failure. + const VarRegion *getParameterLocation(unsigned Index) const; + + /// Returns true if on the current path, the argument was constructed by + /// calling a C++ constructor over it. This is an internal detail of the + /// analysis which doesn't necessarily represent the program semantics: + /// if we are supposed to construct an argument directly, we may still + /// not do that because we don't know how (i.e., construction context is + /// unavailable in the CFG or not supported by the analyzer). + bool isArgumentConstructedDirectly(unsigned Index) const { + // This assumes that the object was not yet removed from the state. + return ExprEngine::getObjectUnderConstruction( + getState(), {getOriginExpr(), Index}, getLocationContext()).hasValue(); + } + + /// Some calls have parameter numbering mismatched from argument numbering. + /// This function converts an argument index to the corresponding + /// parameter index. Returns None is the argument doesn't correspond + /// to any parameter variable. + virtual Optional<unsigned> + getAdjustedParameterIndex(unsigned ASTArgumentIndex) const { + return ASTArgumentIndex; + } + + /// Some call event sub-classes conveniently adjust mismatching AST indices + /// to match parameter indices. This function converts an argument index + /// as understood by CallEvent to the argument index as understood by the AST. + virtual unsigned getASTArgumentIndex(unsigned CallArgumentIndex) const { + return CallArgumentIndex; + } + + // Iterator access to formal parameters and their types. +private: + struct GetTypeFn { + QualType operator()(ParmVarDecl *PD) const { return PD->getType(); } + }; + +public: + /// Return call's formal parameters. + /// + /// Remember that the number of formal parameters may not match the number + /// of arguments for all calls. However, the first parameter will always + /// correspond with the argument value returned by \c getArgSVal(0). + virtual ArrayRef<ParmVarDecl *> parameters() const = 0; + + using param_type_iterator = + llvm::mapped_iterator<ArrayRef<ParmVarDecl *>::iterator, GetTypeFn>; + + /// Returns an iterator over the types of the call's formal parameters. + /// + /// This uses the callee decl found by default name lookup rather than the + /// definition because it represents a public interface, and probably has + /// more annotations. + param_type_iterator param_type_begin() const { + return llvm::map_iterator(parameters().begin(), GetTypeFn()); + } + /// \sa param_type_begin() + param_type_iterator param_type_end() const { + return llvm::map_iterator(parameters().end(), GetTypeFn()); + } + + // For debugging purposes only + void dump(raw_ostream &Out) const; + void dump() const; +}; + +/// Represents a call to any sort of function that might have a +/// FunctionDecl. +class AnyFunctionCall : public CallEvent { +protected: + AnyFunctionCall(const Expr *E, ProgramStateRef St, + const LocationContext *LCtx) + : CallEvent(E, St, LCtx) {} + AnyFunctionCall(const Decl *D, ProgramStateRef St, + const LocationContext *LCtx) + : CallEvent(D, St, LCtx) {} + AnyFunctionCall(const AnyFunctionCall &Other) = default; + +public: + // This function is overridden by subclasses, but they must return + // a FunctionDecl. + const FunctionDecl *getDecl() const override { + return cast<FunctionDecl>(CallEvent::getDecl()); + } + + RuntimeDefinition getRuntimeDefinition() const override; + + bool argumentsMayEscape() const override; + + void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const override; + + ArrayRef<ParmVarDecl *> parameters() const override; + + static bool classof(const CallEvent *CA) { + return CA->getKind() >= CE_BEG_FUNCTION_CALLS && + CA->getKind() <= CE_END_FUNCTION_CALLS; + } +}; + +/// Represents a C function or static C++ member function call. +/// +/// Example: \c fun() +class SimpleFunctionCall : public AnyFunctionCall { + friend class CallEventManager; + +protected: + SimpleFunctionCall(const CallExpr *CE, ProgramStateRef St, + const LocationContext *LCtx) + : AnyFunctionCall(CE, St, LCtx) {} + SimpleFunctionCall(const SimpleFunctionCall &Other) = default; + + void cloneTo(void *Dest) const override { + new (Dest) SimpleFunctionCall(*this); + } + +public: + virtual const CallExpr *getOriginExpr() const { + return cast<CallExpr>(AnyFunctionCall::getOriginExpr()); + } + + const FunctionDecl *getDecl() const override; + + unsigned getNumArgs() const override { return getOriginExpr()->getNumArgs(); } + + const Expr *getArgExpr(unsigned Index) const override { + return getOriginExpr()->getArg(Index); + } + + Kind getKind() const override { return CE_Function; } + + static bool classof(const CallEvent *CA) { + return CA->getKind() == CE_Function; + } +}; + +/// Represents a call to a block. +/// +/// Example: <tt>^{ /* ... */ }()</tt> +class BlockCall : public CallEvent { + friend class CallEventManager; + +protected: + BlockCall(const CallExpr *CE, ProgramStateRef St, + const LocationContext *LCtx) + : CallEvent(CE, St, LCtx) {} + BlockCall(const BlockCall &Other) = default; + + void cloneTo(void *Dest) const override { new (Dest) BlockCall(*this); } + + void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; + +public: + virtual const CallExpr *getOriginExpr() const { + return cast<CallExpr>(CallEvent::getOriginExpr()); + } + + unsigned getNumArgs() const override { return getOriginExpr()->getNumArgs(); } + + const Expr *getArgExpr(unsigned Index) const override { + return getOriginExpr()->getArg(Index); + } + + /// Returns the region associated with this instance of the block. + /// + /// This may be NULL if the block's origin is unknown. + const BlockDataRegion *getBlockRegion() const; + + const BlockDecl *getDecl() const override { + const BlockDataRegion *BR = getBlockRegion(); + if (!BR) + return nullptr; + return BR->getDecl(); + } + + bool isConversionFromLambda() const { + const BlockDecl *BD = getDecl(); + if (!BD) + return false; + + return BD->isConversionFromLambda(); + } + + /// For a block converted from a C++ lambda, returns the block + /// VarRegion for the variable holding the captured C++ lambda record. + const VarRegion *getRegionStoringCapturedLambda() const { + assert(isConversionFromLambda()); + const BlockDataRegion *BR = getBlockRegion(); + assert(BR && "Block converted from lambda must have a block region"); + + auto I = BR->referenced_vars_begin(); + assert(I != BR->referenced_vars_end()); + + return I.getCapturedRegion(); + } + + RuntimeDefinition getRuntimeDefinition() const override { + if (!isConversionFromLambda()) + return RuntimeDefinition(getDecl()); + + // Clang converts lambdas to blocks with an implicit user-defined + // conversion operator method on the lambda record that looks (roughly) + // like: + // + // typedef R(^block_type)(P1, P2, ...); + // operator block_type() const { + // auto Lambda = *this; + // return ^(P1 p1, P2 p2, ...){ + // /* return Lambda(p1, p2, ...); */ + // }; + // } + // + // Here R is the return type of the lambda and P1, P2, ... are + // its parameter types. 'Lambda' is a fake VarDecl captured by the block + // that is initialized to a copy of the lambda. + // + // Sema leaves the body of a lambda-converted block empty (it is + // produced by CodeGen), so we can't analyze it directly. Instead, we skip + // the block body and analyze the operator() method on the captured lambda. + const VarDecl *LambdaVD = getRegionStoringCapturedLambda()->getDecl(); + const CXXRecordDecl *LambdaDecl = LambdaVD->getType()->getAsCXXRecordDecl(); + CXXMethodDecl* LambdaCallOperator = LambdaDecl->getLambdaCallOperator(); + + return RuntimeDefinition(LambdaCallOperator); + } + + bool argumentsMayEscape() const override { + return true; + } + + void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const override; + + ArrayRef<ParmVarDecl*> parameters() const override; + + Kind getKind() const override { return CE_Block; } + + static bool classof(const CallEvent *CA) { + return CA->getKind() == CE_Block; + } +}; + +/// Represents a non-static C++ member function call, no matter how +/// it is written. +class CXXInstanceCall : public AnyFunctionCall { +protected: + CXXInstanceCall(const CallExpr *CE, ProgramStateRef St, + const LocationContext *LCtx) + : AnyFunctionCall(CE, St, LCtx) {} + CXXInstanceCall(const FunctionDecl *D, ProgramStateRef St, + const LocationContext *LCtx) + : AnyFunctionCall(D, St, LCtx) {} + CXXInstanceCall(const CXXInstanceCall &Other) = default; + + void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; + +public: + /// Returns the expression representing the implicit 'this' object. + virtual const Expr *getCXXThisExpr() const { return nullptr; } + + /// Returns the value of the implicit 'this' object. + virtual SVal getCXXThisVal() const; + + const FunctionDecl *getDecl() const override; + + RuntimeDefinition getRuntimeDefinition() const override; + + void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const override; + + static bool classof(const CallEvent *CA) { + return CA->getKind() >= CE_BEG_CXX_INSTANCE_CALLS && + CA->getKind() <= CE_END_CXX_INSTANCE_CALLS; + } +}; + +/// Represents a non-static C++ member function call. +/// +/// Example: \c obj.fun() +class CXXMemberCall : public CXXInstanceCall { + friend class CallEventManager; + +protected: + CXXMemberCall(const CXXMemberCallExpr *CE, ProgramStateRef St, + const LocationContext *LCtx) + : CXXInstanceCall(CE, St, LCtx) {} + CXXMemberCall(const CXXMemberCall &Other) = default; + + void cloneTo(void *Dest) const override { new (Dest) CXXMemberCall(*this); } + +public: + virtual const CXXMemberCallExpr *getOriginExpr() const { + return cast<CXXMemberCallExpr>(CXXInstanceCall::getOriginExpr()); + } + + unsigned getNumArgs() const override { + if (const CallExpr *CE = getOriginExpr()) + return CE->getNumArgs(); + return 0; + } + + const Expr *getArgExpr(unsigned Index) const override { + return getOriginExpr()->getArg(Index); + } + + const Expr *getCXXThisExpr() const override; + + RuntimeDefinition getRuntimeDefinition() const override; + + Kind getKind() const override { return CE_CXXMember; } + + static bool classof(const CallEvent *CA) { + return CA->getKind() == CE_CXXMember; + } +}; + +/// Represents a C++ overloaded operator call where the operator is +/// implemented as a non-static member function. +/// +/// Example: <tt>iter + 1</tt> +class CXXMemberOperatorCall : public CXXInstanceCall { + friend class CallEventManager; + +protected: + CXXMemberOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St, + const LocationContext *LCtx) + : CXXInstanceCall(CE, St, LCtx) {} + CXXMemberOperatorCall(const CXXMemberOperatorCall &Other) = default; + + void cloneTo(void *Dest) const override { + new (Dest) CXXMemberOperatorCall(*this); + } + +public: + virtual const CXXOperatorCallExpr *getOriginExpr() const { + return cast<CXXOperatorCallExpr>(CXXInstanceCall::getOriginExpr()); + } + + unsigned getNumArgs() const override { + return getOriginExpr()->getNumArgs() - 1; + } + + const Expr *getArgExpr(unsigned Index) const override { + return getOriginExpr()->getArg(Index + 1); + } + + const Expr *getCXXThisExpr() const override; + + Kind getKind() const override { return CE_CXXMemberOperator; } + + static bool classof(const CallEvent *CA) { + return CA->getKind() == CE_CXXMemberOperator; + } + + Optional<unsigned> + getAdjustedParameterIndex(unsigned ASTArgumentIndex) const override { + // For member operator calls argument 0 on the expression corresponds + // to implicit this-parameter on the declaration. + return (ASTArgumentIndex > 0) ? Optional<unsigned>(ASTArgumentIndex - 1) + : None; + } + + unsigned getASTArgumentIndex(unsigned CallArgumentIndex) const override { + // For member operator calls argument 0 on the expression corresponds + // to implicit this-parameter on the declaration. + return CallArgumentIndex + 1; + } +}; + +/// Represents an implicit call to a C++ destructor. +/// +/// This can occur at the end of a scope (for automatic objects), at the end +/// of a full-expression (for temporaries), or as part of a delete. +class CXXDestructorCall : public CXXInstanceCall { + friend class CallEventManager; + +protected: + using DtorDataTy = llvm::PointerIntPair<const MemRegion *, 1, bool>; + + /// Creates an implicit destructor. + /// + /// \param DD The destructor that will be called. + /// \param Trigger The statement whose completion causes this destructor call. + /// \param Target The object region to be destructed. + /// \param St The path-sensitive state at this point in the program. + /// \param LCtx The location context at this point in the program. + CXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger, + const MemRegion *Target, bool IsBaseDestructor, + ProgramStateRef St, const LocationContext *LCtx) + : CXXInstanceCall(DD, St, LCtx) { + Data = DtorDataTy(Target, IsBaseDestructor).getOpaqueValue(); + Location = Trigger->getEndLoc(); + } + + CXXDestructorCall(const CXXDestructorCall &Other) = default; + + void cloneTo(void *Dest) const override {new (Dest) CXXDestructorCall(*this);} + +public: + SourceRange getSourceRange() const override { return Location; } + unsigned getNumArgs() const override { return 0; } + + RuntimeDefinition getRuntimeDefinition() const override; + + /// Returns the value of the implicit 'this' object. + SVal getCXXThisVal() const override; + + /// Returns true if this is a call to a base class destructor. + bool isBaseDestructor() const { + return DtorDataTy::getFromOpaqueValue(Data).getInt(); + } + + Kind getKind() const override { return CE_CXXDestructor; } + + static bool classof(const CallEvent *CA) { + return CA->getKind() == CE_CXXDestructor; + } +}; + +/// Represents a call to a C++ constructor. +/// +/// Example: \c T(1) +class CXXConstructorCall : public AnyFunctionCall { + friend class CallEventManager; + +protected: + /// Creates a constructor call. + /// + /// \param CE The constructor expression as written in the source. + /// \param Target The region where the object should be constructed. If NULL, + /// a new symbolic region will be used. + /// \param St The path-sensitive state at this point in the program. + /// \param LCtx The location context at this point in the program. + CXXConstructorCall(const CXXConstructExpr *CE, const MemRegion *Target, + ProgramStateRef St, const LocationContext *LCtx) + : AnyFunctionCall(CE, St, LCtx) { + Data = Target; + } + + CXXConstructorCall(const CXXConstructorCall &Other) = default; + + void cloneTo(void *Dest) const override { new (Dest) CXXConstructorCall(*this); } + + void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; + +public: + virtual const CXXConstructExpr *getOriginExpr() const { + return cast<CXXConstructExpr>(AnyFunctionCall::getOriginExpr()); + } + + const CXXConstructorDecl *getDecl() const override { + return getOriginExpr()->getConstructor(); + } + + unsigned getNumArgs() const override { return getOriginExpr()->getNumArgs(); } + + const Expr *getArgExpr(unsigned Index) const override { + return getOriginExpr()->getArg(Index); + } + + /// Returns the value of the implicit 'this' object. + SVal getCXXThisVal() const; + + void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const override; + + Kind getKind() const override { return CE_CXXConstructor; } + + static bool classof(const CallEvent *CA) { + return CA->getKind() == CE_CXXConstructor; + } +}; + +/// Represents the memory allocation call in a C++ new-expression. +/// +/// This is a call to "operator new". +class CXXAllocatorCall : public AnyFunctionCall { + friend class CallEventManager; + +protected: + CXXAllocatorCall(const CXXNewExpr *E, ProgramStateRef St, + const LocationContext *LCtx) + : AnyFunctionCall(E, St, LCtx) {} + CXXAllocatorCall(const CXXAllocatorCall &Other) = default; + + void cloneTo(void *Dest) const override { new (Dest) CXXAllocatorCall(*this); } + +public: + virtual const CXXNewExpr *getOriginExpr() const { + return cast<CXXNewExpr>(AnyFunctionCall::getOriginExpr()); + } + + const FunctionDecl *getDecl() const override { + return getOriginExpr()->getOperatorNew(); + } + + /// Number of non-placement arguments to the call. It is equal to 2 for + /// C++17 aligned operator new() calls that have alignment implicitly + /// passed as the second argument, and to 1 for other operator new() calls. + unsigned getNumImplicitArgs() const { + return getOriginExpr()->passAlignment() ? 2 : 1; + } + + unsigned getNumArgs() const override { + return getOriginExpr()->getNumPlacementArgs() + getNumImplicitArgs(); + } + + const Expr *getArgExpr(unsigned Index) const override { + // The first argument of an allocator call is the size of the allocation. + if (Index < getNumImplicitArgs()) + return nullptr; + return getOriginExpr()->getPlacementArg(Index - getNumImplicitArgs()); + } + + /// Number of placement arguments to the operator new() call. For example, + /// standard std::nothrow operator new and standard placement new both have + /// 1 implicit argument (size) and 1 placement argument, while regular + /// operator new() has 1 implicit argument and 0 placement arguments. + const Expr *getPlacementArgExpr(unsigned Index) const { + return getOriginExpr()->getPlacementArg(Index); + } + + Kind getKind() const override { return CE_CXXAllocator; } + + static bool classof(const CallEvent *CE) { + return CE->getKind() == CE_CXXAllocator; + } +}; + +/// Represents the ways an Objective-C message send can occur. +// +// Note to maintainers: OCM_Message should always be last, since it does not +// need to fit in the Data field's low bits. +enum ObjCMessageKind { + OCM_PropertyAccess, + OCM_Subscript, + OCM_Message +}; + +/// Represents any expression that calls an Objective-C method. +/// +/// This includes all of the kinds listed in ObjCMessageKind. +class ObjCMethodCall : public CallEvent { + friend class CallEventManager; + + const PseudoObjectExpr *getContainingPseudoObjectExpr() const; + +protected: + ObjCMethodCall(const ObjCMessageExpr *Msg, ProgramStateRef St, + const LocationContext *LCtx) + : CallEvent(Msg, St, LCtx) { + Data = nullptr; + } + + ObjCMethodCall(const ObjCMethodCall &Other) = default; + + void cloneTo(void *Dest) const override { new (Dest) ObjCMethodCall(*this); } + + void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; + + /// Check if the selector may have multiple definitions (may have overrides). + virtual bool canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, + Selector Sel) const; + +public: + virtual const ObjCMessageExpr *getOriginExpr() const { + return cast<ObjCMessageExpr>(CallEvent::getOriginExpr()); + } + + const ObjCMethodDecl *getDecl() const override { + return getOriginExpr()->getMethodDecl(); + } + + unsigned getNumArgs() const override { + return getOriginExpr()->getNumArgs(); + } + + const Expr *getArgExpr(unsigned Index) const override { + return getOriginExpr()->getArg(Index); + } + + bool isInstanceMessage() const { + return getOriginExpr()->isInstanceMessage(); + } + + ObjCMethodFamily getMethodFamily() const { + return getOriginExpr()->getMethodFamily(); + } + + Selector getSelector() const { + return getOriginExpr()->getSelector(); + } + + SourceRange getSourceRange() const override; + + /// Returns the value of the receiver at the time of this call. + SVal getReceiverSVal() const; + + /// Return the value of 'self' if available. + SVal getSelfSVal() const; + + /// Get the interface for the receiver. + /// + /// This works whether this is an instance message or a class message. + /// However, it currently just uses the static type of the receiver. + const ObjCInterfaceDecl *getReceiverInterface() const { + return getOriginExpr()->getReceiverInterface(); + } + + /// Checks if the receiver refers to 'self' or 'super'. + bool isReceiverSelfOrSuper() const; + + /// Returns how the message was written in the source (property access, + /// subscript, or explicit message send). + ObjCMessageKind getMessageKind() const; + + /// Returns true if this property access or subscript is a setter (has the + /// form of an assignment). + bool isSetter() const { + switch (getMessageKind()) { + case OCM_Message: + llvm_unreachable("This is not a pseudo-object access!"); + case OCM_PropertyAccess: + return getNumArgs() > 0; + case OCM_Subscript: + return getNumArgs() > 1; + } + llvm_unreachable("Unknown message kind"); + } + + // Returns the property accessed by this method, either explicitly via + // property syntax or implicitly via a getter or setter method. Returns + // nullptr if the call is not a prooperty access. + const ObjCPropertyDecl *getAccessedProperty() const; + + RuntimeDefinition getRuntimeDefinition() const override; + + bool argumentsMayEscape() const override; + + void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const override; + + ArrayRef<ParmVarDecl*> parameters() const override; + + Kind getKind() const override { return CE_ObjCMessage; } + + static bool classof(const CallEvent *CA) { + return CA->getKind() == CE_ObjCMessage; + } +}; + +/// Manages the lifetime of CallEvent objects. +/// +/// CallEventManager provides a way to create arbitrary CallEvents "on the +/// stack" as if they were value objects by keeping a cache of CallEvent-sized +/// memory blocks. The CallEvents created by CallEventManager are only valid +/// for the lifetime of the OwnedCallEvent that holds them; right now these +/// objects cannot be copied and ownership cannot be transferred. +class CallEventManager { + friend class CallEvent; + + llvm::BumpPtrAllocator &Alloc; + SmallVector<void *, 8> Cache; + + using CallEventTemplateTy = SimpleFunctionCall; + + void reclaim(const void *Memory) { + Cache.push_back(const_cast<void *>(Memory)); + } + + /// Returns memory that can be initialized as a CallEvent. + void *allocate() { + if (Cache.empty()) + return Alloc.Allocate<CallEventTemplateTy>(); + else + return Cache.pop_back_val(); + } + + template <typename T, typename Arg> + T *create(Arg A, ProgramStateRef St, const LocationContext *LCtx) { + static_assert(sizeof(T) == sizeof(CallEventTemplateTy), + "CallEvent subclasses are not all the same size"); + return new (allocate()) T(A, St, LCtx); + } + + template <typename T, typename Arg1, typename Arg2> + T *create(Arg1 A1, Arg2 A2, ProgramStateRef St, const LocationContext *LCtx) { + static_assert(sizeof(T) == sizeof(CallEventTemplateTy), + "CallEvent subclasses are not all the same size"); + return new (allocate()) T(A1, A2, St, LCtx); + } + + template <typename T, typename Arg1, typename Arg2, typename Arg3> + T *create(Arg1 A1, Arg2 A2, Arg3 A3, ProgramStateRef St, + const LocationContext *LCtx) { + static_assert(sizeof(T) == sizeof(CallEventTemplateTy), + "CallEvent subclasses are not all the same size"); + return new (allocate()) T(A1, A2, A3, St, LCtx); + } + + template <typename T, typename Arg1, typename Arg2, typename Arg3, + typename Arg4> + T *create(Arg1 A1, Arg2 A2, Arg3 A3, Arg4 A4, ProgramStateRef St, + const LocationContext *LCtx) { + static_assert(sizeof(T) == sizeof(CallEventTemplateTy), + "CallEvent subclasses are not all the same size"); + return new (allocate()) T(A1, A2, A3, A4, St, LCtx); + } + +public: + CallEventManager(llvm::BumpPtrAllocator &alloc) : Alloc(alloc) {} + + /// Gets an outside caller given a callee context. + CallEventRef<> + getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State); + + /// Gets a call event for a function call, Objective-C method call, + /// or a 'new' call. + CallEventRef<> + getCall(const Stmt *S, ProgramStateRef State, + const LocationContext *LC); + + CallEventRef<> + getSimpleCall(const CallExpr *E, ProgramStateRef State, + const LocationContext *LCtx); + + CallEventRef<ObjCMethodCall> + getObjCMethodCall(const ObjCMessageExpr *E, ProgramStateRef State, + const LocationContext *LCtx) { + return create<ObjCMethodCall>(E, State, LCtx); + } + + CallEventRef<CXXConstructorCall> + getCXXConstructorCall(const CXXConstructExpr *E, const MemRegion *Target, + ProgramStateRef State, const LocationContext *LCtx) { + return create<CXXConstructorCall>(E, Target, State, LCtx); + } + + CallEventRef<CXXDestructorCall> + getCXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger, + const MemRegion *Target, bool IsBase, + ProgramStateRef State, const LocationContext *LCtx) { + return create<CXXDestructorCall>(DD, Trigger, Target, IsBase, State, LCtx); + } + + CallEventRef<CXXAllocatorCall> + getCXXAllocatorCall(const CXXNewExpr *E, ProgramStateRef State, + const LocationContext *LCtx) { + return create<CXXAllocatorCall>(E, State, LCtx); + } +}; + +template <typename T> +CallEventRef<T> CallEvent::cloneWithState(ProgramStateRef NewState) const { + assert(isa<T>(*this) && "Cloning to unrelated type"); + static_assert(sizeof(T) == sizeof(CallEvent), + "Subclasses may not add fields"); + + if (NewState == State) + return cast<T>(this); + + CallEventManager &Mgr = State->getStateManager().getCallEventManager(); + T *Copy = static_cast<T *>(Mgr.allocate()); + cloneTo(Copy); + assert(Copy->getKind() == this->getKind() && "Bad copy"); + + Copy->State = NewState; + return Copy; +} + +inline void CallEvent::Release() const { + assert(RefCount > 0 && "Reference count is already zero."); + --RefCount; + + if (RefCount > 0) + return; + + CallEventManager &Mgr = State->getStateManager().getCallEventManager(); + Mgr.reclaim(this); + + this->~CallEvent(); +} + +} // namespace ento + +} // namespace clang + +namespace llvm { + +// Support isa<>, cast<>, and dyn_cast<> for CallEventRef. +template<class T> struct simplify_type< clang::ento::CallEventRef<T>> { + using SimpleType = const T *; + + static SimpleType + getSimplifiedValue(clang::ento::CallEventRef<T> Val) { + return Val.get(); + } +}; + +} // namespace llvm + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLEVENT_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h new file mode 100644 index 00000000..5710ee79 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -0,0 +1,309 @@ +//== CheckerContext.h - Context info for path-sensitive checkers--*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines CheckerContext that provides contextual info for +// path-sensitive checkers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERCONTEXT_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERCONTEXT_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +namespace clang { +namespace ento { + +class CheckerContext { + ExprEngine &Eng; + /// The current exploded(symbolic execution) graph node. + ExplodedNode *Pred; + /// The flag is true if the (state of the execution) has been modified + /// by the checker using this context. For example, a new transition has been + /// added or a bug report issued. + bool Changed; + /// The tagged location, which is used to generate all new nodes. + const ProgramPoint Location; + NodeBuilder &NB; + +public: + /// If we are post visiting a call, this flag will be set if the + /// call was inlined. In all other cases it will be false. + const bool wasInlined; + + CheckerContext(NodeBuilder &builder, + ExprEngine &eng, + ExplodedNode *pred, + const ProgramPoint &loc, + bool wasInlined = false) + : Eng(eng), + Pred(pred), + Changed(false), + Location(loc), + NB(builder), + wasInlined(wasInlined) { + assert(Pred->getState() && + "We should not call the checkers on an empty state."); + } + + AnalysisManager &getAnalysisManager() { + return Eng.getAnalysisManager(); + } + + ConstraintManager &getConstraintManager() { + return Eng.getConstraintManager(); + } + + StoreManager &getStoreManager() { + return Eng.getStoreManager(); + } + + /// Returns the previous node in the exploded graph, which includes + /// the state of the program before the checker ran. Note, checkers should + /// not retain the node in their state since the nodes might get invalidated. + ExplodedNode *getPredecessor() { return Pred; } + const ProgramStateRef &getState() const { return Pred->getState(); } + + /// Check if the checker changed the state of the execution; ex: added + /// a new transition or a bug report. + bool isDifferent() { return Changed; } + + /// Returns the number of times the current block has been visited + /// along the analyzed path. + unsigned blockCount() const { + return NB.getContext().blockCount(); + } + + ASTContext &getASTContext() { + return Eng.getContext(); + } + + const LangOptions &getLangOpts() const { + return Eng.getContext().getLangOpts(); + } + + const LocationContext *getLocationContext() const { + return Pred->getLocationContext(); + } + + const StackFrameContext *getStackFrame() const { + return Pred->getStackFrame(); + } + + /// Return true if the current LocationContext has no caller context. + bool inTopFrame() const { return getLocationContext()->inTopFrame(); } + + BugReporter &getBugReporter() { + return Eng.getBugReporter(); + } + + SourceManager &getSourceManager() { + return getBugReporter().getSourceManager(); + } + + SValBuilder &getSValBuilder() { + return Eng.getSValBuilder(); + } + + SymbolManager &getSymbolManager() { + return getSValBuilder().getSymbolManager(); + } + + ProgramStateManager &getStateManager() { + return Eng.getStateManager(); + } + + AnalysisDeclContext *getCurrentAnalysisDeclContext() const { + return Pred->getLocationContext()->getAnalysisDeclContext(); + } + + /// Get the blockID. + unsigned getBlockID() const { + return NB.getContext().getBlock()->getBlockID(); + } + + /// If the given node corresponds to a PostStore program point, + /// retrieve the location region as it was uttered in the code. + /// + /// This utility can be useful for generating extensive diagnostics, for + /// example, for finding variables that the given symbol was assigned to. + static const MemRegion *getLocationRegionIfPostStore(const ExplodedNode *N) { + ProgramPoint L = N->getLocation(); + if (Optional<PostStore> PSL = L.getAs<PostStore>()) + return reinterpret_cast<const MemRegion*>(PSL->getLocationValue()); + return nullptr; + } + + /// Get the value of arbitrary expressions at this point in the path. + SVal getSVal(const Stmt *S) const { + return Pred->getSVal(S); + } + + /// Returns true if the value of \p E is greater than or equal to \p + /// Val under unsigned comparison + bool isGreaterOrEqual(const Expr *E, unsigned long long Val); + + /// Returns true if the value of \p E is negative. + bool isNegative(const Expr *E); + + /// Generates a new transition in the program state graph + /// (ExplodedGraph). Uses the default CheckerContext predecessor node. + /// + /// @param State The state of the generated node. If not specified, the state + /// will not be changed, but the new node will have the checker's tag. + /// @param Tag The tag is used to uniquely identify the creation site. If no + /// tag is specified, a default tag, unique to the given checker, + /// will be used. Tags are used to prevent states generated at + /// different sites from caching out. + ExplodedNode *addTransition(ProgramStateRef State = nullptr, + const ProgramPointTag *Tag = nullptr) { + return addTransitionImpl(State ? State : getState(), false, nullptr, Tag); + } + + /// Generates a new transition with the given predecessor. + /// Allows checkers to generate a chain of nodes. + /// + /// @param State The state of the generated node. + /// @param Pred The transition will be generated from the specified Pred node + /// to the newly generated node. + /// @param Tag The tag to uniquely identify the creation site. + ExplodedNode *addTransition(ProgramStateRef State, + ExplodedNode *Pred, + const ProgramPointTag *Tag = nullptr) { + return addTransitionImpl(State, false, Pred, Tag); + } + + /// Generate a sink node. Generating a sink stops exploration of the + /// given path. To create a sink node for the purpose of reporting an error, + /// checkers should use generateErrorNode() instead. + ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred, + const ProgramPointTag *Tag = nullptr) { + return addTransitionImpl(State ? State : getState(), true, Pred, Tag); + } + + /// Generate a transition to a node that will be used to report + /// an error. This node will be a sink. That is, it will stop exploration of + /// the given path. + /// + /// @param State The state of the generated node. + /// @param Tag The tag to uniquely identify the creation site. If null, + /// the default tag for the checker will be used. + ExplodedNode *generateErrorNode(ProgramStateRef State = nullptr, + const ProgramPointTag *Tag = nullptr) { + return generateSink(State, Pred, + (Tag ? Tag : Location.getTag())); + } + + /// Generate a transition to a node that will be used to report + /// an error. This node will not be a sink. That is, exploration will + /// continue along this path. + /// + /// @param State The state of the generated node. + /// @param Tag The tag to uniquely identify the creation site. If null, + /// the default tag for the checker will be used. + ExplodedNode * + generateNonFatalErrorNode(ProgramStateRef State = nullptr, + const ProgramPointTag *Tag = nullptr) { + return addTransition(State, (Tag ? Tag : Location.getTag())); + } + + /// Emit the diagnostics report. + void emitReport(std::unique_ptr<BugReport> R) { + Changed = true; + Eng.getBugReporter().emitReport(std::move(R)); + } + + /// Returns the word that should be used to refer to the declaration + /// in the report. + StringRef getDeclDescription(const Decl *D); + + /// Get the declaration of the called function (path-sensitive). + const FunctionDecl *getCalleeDecl(const CallExpr *CE) const; + + /// Get the name of the called function (path-sensitive). + StringRef getCalleeName(const FunctionDecl *FunDecl) const; + + /// Get the identifier of the called function (path-sensitive). + const IdentifierInfo *getCalleeIdentifier(const CallExpr *CE) const { + const FunctionDecl *FunDecl = getCalleeDecl(CE); + if (FunDecl) + return FunDecl->getIdentifier(); + else + return nullptr; + } + + /// Get the name of the called function (path-sensitive). + StringRef getCalleeName(const CallExpr *CE) const { + const FunctionDecl *FunDecl = getCalleeDecl(CE); + return getCalleeName(FunDecl); + } + + /// Returns true if the callee is an externally-visible function in the + /// top-level namespace, such as \c malloc. + /// + /// If a name is provided, the function must additionally match the given + /// name. + /// + /// Note that this deliberately excludes C++ library functions in the \c std + /// namespace, but will include C library functions accessed through the + /// \c std namespace. This also does not check if the function is declared + /// as 'extern "C"', or if it uses C++ name mangling. + static bool isCLibraryFunction(const FunctionDecl *FD, + StringRef Name = StringRef()); + + /// Depending on wither the location corresponds to a macro, return + /// either the macro name or the token spelling. + /// + /// This could be useful when checkers' logic depends on whether a function + /// is called with a given macro argument. For example: + /// s = socket(AF_INET,..) + /// If AF_INET is a macro, the result should be treated as a source of taint. + /// + /// \sa clang::Lexer::getSpelling(), clang::Lexer::getImmediateMacroName(). + StringRef getMacroNameOrSpelling(SourceLocation &Loc); + +private: + ExplodedNode *addTransitionImpl(ProgramStateRef State, + bool MarkAsSink, + ExplodedNode *P = nullptr, + const ProgramPointTag *Tag = nullptr) { + // The analyzer may stop exploring if it sees a state it has previously + // visited ("cache out"). The early return here is a defensive check to + // prevent accidental caching out by checker API clients. Unless there is a + // tag or the client checker has requested that the generated node be + // marked as a sink, we assume that a client requesting a transition to a + // state that is the same as the predecessor state has made a mistake. We + // return the predecessor rather than cache out. + // + // TODO: We could potentially change the return to an assertion to alert + // clients to their mistake, but several checkers (including + // DereferenceChecker, CallAndMessageChecker, and DynamicTypePropagation) + // rely upon the defensive behavior and would need to be updated. + if (!State || (State == Pred->getState() && !Tag && !MarkAsSink)) + return Pred; + + Changed = true; + const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); + if (!P) + P = Pred; + + ExplodedNode *node; + if (MarkAsSink) + node = NB.generateSink(LocalLoc, State, P); + else + node = NB.generateNode(LocalLoc, State, P); + return node; + } +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h new file mode 100644 index 00000000..b53c042a --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h @@ -0,0 +1,69 @@ +//== CheckerHelpers.h - Helper functions for checkers ------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines CheckerVisitor. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERHELPERS_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERHELPERS_H + +#include "clang/AST/Stmt.h" +#include <tuple> + +namespace clang { + +class Expr; +class VarDecl; +class QualType; +class AttributedType; + +namespace ento { + +bool containsMacro(const Stmt *S); +bool containsEnum(const Stmt *S); +bool containsStaticLocal(const Stmt *S); +bool containsBuiltinOffsetOf(const Stmt *S); +template <class T> bool containsStmt(const Stmt *S) { + if (isa<T>(S)) + return true; + + for (const Stmt *Child : S->children()) + if (Child && containsStmt<T>(Child)) + return true; + + return false; +} + +std::pair<const clang::VarDecl *, const clang::Expr *> +parseAssignment(const Stmt *S); + +// Do not reorder! The getMostNullable method relies on the order. +// Optimization: Most pointers expected to be unspecified. When a symbol has an +// unspecified or nonnull type non of the rules would indicate any problem for +// that symbol. For this reason only nullable and contradicted nullability are +// stored for a symbol. When a symbol is already contradicted, it can not be +// casted back to nullable. +enum class Nullability : char { + Contradicted, // Tracked nullability is contradicted by an explicit cast. Do + // not report any nullability related issue for this symbol. + // This nullability is propagated aggressively to avoid false + // positive results. See the comment on getMostNullable method. + Nullable, + Unspecified, + Nonnull +}; + +/// Get nullability annotation for a given type. +Nullability getNullabilityAnnotation(QualType Type); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h new file mode 100644 index 00000000..0a8712ea --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h @@ -0,0 +1,208 @@ +//===- ConstraintManager.h - Constraints on symbolic values. ----*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defined the interface to manage constraints on symbolic values. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CONSTRAINTMANAGER_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CONSTRAINTMANAGER_H + +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/SaveAndRestore.h" +#include <memory> +#include <utility> + +namespace llvm { + +class APSInt; + +} // namespace llvm + +namespace clang { +namespace ento { + +class ProgramStateManager; +class SubEngine; +class SymbolReaper; + +class ConditionTruthVal { + Optional<bool> Val; + +public: + /// Construct a ConditionTruthVal indicating the constraint is constrained + /// to either true or false, depending on the boolean value provided. + ConditionTruthVal(bool constraint) : Val(constraint) {} + + /// Construct a ConstraintVal indicating the constraint is underconstrained. + ConditionTruthVal() = default; + + /// \return Stored value, assuming that the value is known. + /// Crashes otherwise. + bool getValue() const { + return *Val; + } + + /// Return true if the constraint is perfectly constrained to 'true'. + bool isConstrainedTrue() const { + return Val.hasValue() && Val.getValue(); + } + + /// Return true if the constraint is perfectly constrained to 'false'. + bool isConstrainedFalse() const { + return Val.hasValue() && !Val.getValue(); + } + + /// Return true if the constrained is perfectly constrained. + bool isConstrained() const { + return Val.hasValue(); + } + + /// Return true if the constrained is underconstrained and we do not know + /// if the constraint is true of value. + bool isUnderconstrained() const { + return !Val.hasValue(); + } +}; + +class ConstraintManager { +public: + ConstraintManager() = default; + virtual ~ConstraintManager(); + + virtual ProgramStateRef assume(ProgramStateRef state, + DefinedSVal Cond, + bool Assumption) = 0; + + using ProgramStatePair = std::pair<ProgramStateRef, ProgramStateRef>; + + /// Returns a pair of states (StTrue, StFalse) where the given condition is + /// assumed to be true or false, respectively. + ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond) { + ProgramStateRef StTrue = assume(State, Cond, true); + + // If StTrue is infeasible, asserting the falseness of Cond is unnecessary + // because the existing constraints already establish this. + if (!StTrue) { +#ifndef __OPTIMIZE__ + // This check is expensive and should be disabled even in Release+Asserts + // builds. + // FIXME: __OPTIMIZE__ is a GNU extension that Clang implements but MSVC + // does not. Is there a good equivalent there? + assert(assume(State, Cond, false) && "System is over constrained."); +#endif + return ProgramStatePair((ProgramStateRef)nullptr, State); + } + + ProgramStateRef StFalse = assume(State, Cond, false); + if (!StFalse) { + // We are careful to return the original state, /not/ StTrue, + // because we want to avoid having callers generate a new node + // in the ExplodedGraph. + return ProgramStatePair(State, (ProgramStateRef)nullptr); + } + + return ProgramStatePair(StTrue, StFalse); + } + + virtual ProgramStateRef assumeInclusiveRange(ProgramStateRef State, + NonLoc Value, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InBound) = 0; + + virtual ProgramStatePair assumeInclusiveRangeDual(ProgramStateRef State, + NonLoc Value, + const llvm::APSInt &From, + const llvm::APSInt &To) { + ProgramStateRef StInRange = + assumeInclusiveRange(State, Value, From, To, true); + + // If StTrue is infeasible, asserting the falseness of Cond is unnecessary + // because the existing constraints already establish this. + if (!StInRange) + return ProgramStatePair((ProgramStateRef)nullptr, State); + + ProgramStateRef StOutOfRange = + assumeInclusiveRange(State, Value, From, To, false); + if (!StOutOfRange) { + // We are careful to return the original state, /not/ StTrue, + // because we want to avoid having callers generate a new node + // in the ExplodedGraph. + return ProgramStatePair(State, (ProgramStateRef)nullptr); + } + + return ProgramStatePair(StInRange, StOutOfRange); + } + + /// If a symbol is perfectly constrained to a constant, attempt + /// to return the concrete value. + /// + /// Note that a ConstraintManager is not obligated to return a concretized + /// value for a symbol, even if it is perfectly constrained. + virtual const llvm::APSInt* getSymVal(ProgramStateRef state, + SymbolRef sym) const { + return nullptr; + } + + /// Scan all symbols referenced by the constraints. If the symbol is not + /// alive, remove it. + virtual ProgramStateRef removeDeadBindings(ProgramStateRef state, + SymbolReaper& SymReaper) = 0; + + virtual void print(ProgramStateRef state, + raw_ostream &Out, + const char* nl, + const char *sep) = 0; + + virtual void EndPath(ProgramStateRef state) {} + + /// Convenience method to query the state to see if a symbol is null or + /// not null, or if neither assumption can be made. + ConditionTruthVal isNull(ProgramStateRef State, SymbolRef Sym) { + SaveAndRestore<bool> DisableNotify(NotifyAssumeClients, false); + + return checkNull(State, Sym); + } + +protected: + /// A flag to indicate that clients should be notified of assumptions. + /// By default this is the case, but sometimes this needs to be restricted + /// to avoid infinite recursions within the ConstraintManager. + /// + /// Note that this flag allows the ConstraintManager to be re-entrant, + /// but not thread-safe. + bool NotifyAssumeClients = true; + + /// canReasonAbout - Not all ConstraintManagers can accurately reason about + /// all SVal values. This method returns true if the ConstraintManager can + /// reasonably handle a given SVal value. This is typically queried by + /// ExprEngine to determine if the value should be replaced with a + /// conjured symbolic value in order to recover some precision. + virtual bool canReasonAbout(SVal X) const = 0; + + /// Returns whether or not a symbol is known to be null ("true"), known to be + /// non-null ("false"), or may be either ("underconstrained"). + virtual ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym); +}; + +std::unique_ptr<ConstraintManager> +CreateRangeConstraintManager(ProgramStateManager &statemgr, + SubEngine *subengine); + +std::unique_ptr<ConstraintManager> +CreateZ3ConstraintManager(ProgramStateManager &statemgr, SubEngine *subengine); + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CONSTRAINTMANAGER_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h new file mode 100644 index 00000000..a01678ef --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -0,0 +1,574 @@ +//===- CoreEngine.h - Path-Sensitive Dataflow Engine ------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines a generic engine for intraprocedural, path-sensitive, +// dataflow analysis via graph reachability. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H + +#include "clang/AST/Stmt.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include <cassert> +#include <memory> +#include <utility> +#include <vector> + +namespace clang { + +class AnalyzerOptions; +class CXXBindTemporaryExpr; +class Expr; +class LabelDecl; + +namespace ento { + +class FunctionSummariesTy; +class SubEngine; + +//===----------------------------------------------------------------------===// +/// CoreEngine - Implements the core logic of the graph-reachability +/// analysis. It traverses the CFG and generates the ExplodedGraph. +/// Program "states" are treated as opaque void pointers. +/// The template class CoreEngine (which subclasses CoreEngine) +/// provides the matching component to the engine that knows the actual types +/// for states. Note that this engine only dispatches to transfer functions +/// at the statement and block-level. The analyses themselves must implement +/// any transfer function logic and the sub-expression level (if any). +class CoreEngine { + friend class CommonNodeBuilder; + friend class EndOfFunctionNodeBuilder; + friend class ExprEngine; + friend class IndirectGotoNodeBuilder; + friend class NodeBuilder; + friend struct NodeBuilderContext; + friend class SwitchNodeBuilder; + +public: + using BlocksExhausted = + std::vector<std::pair<BlockEdge, const ExplodedNode *>>; + + using BlocksAborted = + std::vector<std::pair<const CFGBlock *, const ExplodedNode *>>; + +private: + SubEngine &SubEng; + + /// G - The simulation graph. Each node is a (location,state) pair. + mutable ExplodedGraph G; + + /// WList - A set of queued nodes that need to be processed by the + /// worklist algorithm. It is up to the implementation of WList to decide + /// the order that nodes are processed. + std::unique_ptr<WorkList> WList; + + /// BCounterFactory - A factory object for created BlockCounter objects. + /// These are used to record for key nodes in the ExplodedGraph the + /// number of times different CFGBlocks have been visited along a path. + BlockCounter::Factory BCounterFactory; + + /// The locations where we stopped doing work because we visited a location + /// too many times. + BlocksExhausted blocksExhausted; + + /// The locations where we stopped because the engine aborted analysis, + /// usually because it could not reason about something. + BlocksAborted blocksAborted; + + /// The information about functions shared by the whole translation unit. + /// (This data is owned by AnalysisConsumer.) + FunctionSummariesTy *FunctionSummaries; + + void generateNode(const ProgramPoint &Loc, + ProgramStateRef State, + ExplodedNode *Pred); + + void HandleBlockEdge(const BlockEdge &E, ExplodedNode *Pred); + void HandleBlockEntrance(const BlockEntrance &E, ExplodedNode *Pred); + void HandleBlockExit(const CFGBlock *B, ExplodedNode *Pred); + + void HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred); + + void HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred); + + void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B, + ExplodedNode *Pred); + void HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, + const CFGBlock *B, ExplodedNode *Pred); + + /// Handle conditional logic for running static initializers. + void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, + ExplodedNode *Pred); + +private: + ExplodedNode *generateCallExitBeginNode(ExplodedNode *N, + const ReturnStmt *RS); + +public: + /// Construct a CoreEngine object to analyze the provided CFG. + CoreEngine(SubEngine &subengine, + FunctionSummariesTy *FS, + AnalyzerOptions &Opts); + + CoreEngine(const CoreEngine &) = delete; + CoreEngine &operator=(const CoreEngine &) = delete; + + /// getGraph - Returns the exploded graph. + ExplodedGraph &getGraph() { return G; } + + /// ExecuteWorkList - Run the worklist algorithm for a maximum number of + /// steps. Returns true if there is still simulation state on the worklist. + bool ExecuteWorkList(const LocationContext *L, unsigned Steps, + ProgramStateRef InitState); + + /// Returns true if there is still simulation state on the worklist. + bool ExecuteWorkListWithInitialState(const LocationContext *L, + unsigned Steps, + ProgramStateRef InitState, + ExplodedNodeSet &Dst); + + /// Dispatch the work list item based on the given location information. + /// Use Pred parameter as the predecessor state. + void dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, + const WorkListUnit& WU); + + // Functions for external checking of whether we have unfinished work + bool wasBlockAborted() const { return !blocksAborted.empty(); } + bool wasBlocksExhausted() const { return !blocksExhausted.empty(); } + bool hasWorkRemaining() const { return wasBlocksExhausted() || + WList->hasWork() || + wasBlockAborted(); } + + /// Inform the CoreEngine that a basic block was aborted because + /// it could not be completely analyzed. + void addAbortedBlock(const ExplodedNode *node, const CFGBlock *block) { + blocksAborted.push_back(std::make_pair(block, node)); + } + + WorkList *getWorkList() const { return WList.get(); } + + BlocksExhausted::const_iterator blocks_exhausted_begin() const { + return blocksExhausted.begin(); + } + + BlocksExhausted::const_iterator blocks_exhausted_end() const { + return blocksExhausted.end(); + } + + BlocksAborted::const_iterator blocks_aborted_begin() const { + return blocksAborted.begin(); + } + + BlocksAborted::const_iterator blocks_aborted_end() const { + return blocksAborted.end(); + } + + /// Enqueue the given set of nodes onto the work list. + void enqueue(ExplodedNodeSet &Set); + + /// Enqueue nodes that were created as a result of processing + /// a statement onto the work list. + void enqueue(ExplodedNodeSet &Set, const CFGBlock *Block, unsigned Idx); + + /// enqueue the nodes corresponding to the end of function onto the + /// end of path / work list. + void enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS); + + /// Enqueue a single node created as a result of statement processing. + void enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx); +}; + +// TODO: Turn into a calss. +struct NodeBuilderContext { + const CoreEngine &Eng; + const CFGBlock *Block; + const LocationContext *LC; + + NodeBuilderContext(const CoreEngine &E, const CFGBlock *B, ExplodedNode *N) + : Eng(E), Block(B), LC(N->getLocationContext()) { assert(B); } + + /// Return the CFGBlock associated with this builder. + const CFGBlock *getBlock() const { return Block; } + + /// Returns the number of times the current basic block has been + /// visited on the exploded graph path. + unsigned blockCount() const { + return Eng.WList->getBlockCounter().getNumVisited( + LC->getStackFrame(), + Block->getBlockID()); + } +}; + +/// \class NodeBuilder +/// This is the simplest builder which generates nodes in the +/// ExplodedGraph. +/// +/// The main benefit of the builder is that it automatically tracks the +/// frontier nodes (or destination set). This is the set of nodes which should +/// be propagated to the next step / builder. They are the nodes which have been +/// added to the builder (either as the input node set or as the newly +/// constructed nodes) but did not have any outgoing transitions added. +class NodeBuilder { + virtual void anchor(); + +protected: + const NodeBuilderContext &C; + + /// Specifies if the builder results have been finalized. For example, if it + /// is set to false, autotransitions are yet to be generated. + bool Finalized; + + bool HasGeneratedNodes = false; + + /// The frontier set - a set of nodes which need to be propagated after + /// the builder dies. + ExplodedNodeSet &Frontier; + + /// Checks if the results are ready. + virtual bool checkResults() { + return Finalized; + } + + bool hasNoSinksInFrontier() { + for (const auto I : Frontier) + if (I->isSink()) + return false; + return true; + } + + /// Allow subclasses to finalize results before result_begin() is executed. + virtual void finalizeResults() {} + + ExplodedNode *generateNodeImpl(const ProgramPoint &PP, + ProgramStateRef State, + ExplodedNode *Pred, + bool MarkAsSink = false); + +public: + NodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, + const NodeBuilderContext &Ctx, bool F = true) + : C(Ctx), Finalized(F), Frontier(DstSet) { + Frontier.Add(SrcNode); + } + + NodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, + const NodeBuilderContext &Ctx, bool F = true) + : C(Ctx), Finalized(F), Frontier(DstSet) { + Frontier.insert(SrcSet); + assert(hasNoSinksInFrontier()); + } + + virtual ~NodeBuilder() = default; + + /// Generates a node in the ExplodedGraph. + ExplodedNode *generateNode(const ProgramPoint &PP, + ProgramStateRef State, + ExplodedNode *Pred) { + return generateNodeImpl(PP, State, Pred, false); + } + + /// Generates a sink in the ExplodedGraph. + /// + /// When a node is marked as sink, the exploration from the node is stopped - + /// the node becomes the last node on the path and certain kinds of bugs are + /// suppressed. + ExplodedNode *generateSink(const ProgramPoint &PP, + ProgramStateRef State, + ExplodedNode *Pred) { + return generateNodeImpl(PP, State, Pred, true); + } + + const ExplodedNodeSet &getResults() { + finalizeResults(); + assert(checkResults()); + return Frontier; + } + + using iterator = ExplodedNodeSet::iterator; + + /// Iterators through the results frontier. + iterator begin() { + finalizeResults(); + assert(checkResults()); + return Frontier.begin(); + } + + iterator end() { + finalizeResults(); + return Frontier.end(); + } + + const NodeBuilderContext &getContext() { return C; } + bool hasGeneratedNodes() { return HasGeneratedNodes; } + + void takeNodes(const ExplodedNodeSet &S) { + for (const auto I : S) + Frontier.erase(I); + } + + void takeNodes(ExplodedNode *N) { Frontier.erase(N); } + void addNodes(const ExplodedNodeSet &S) { Frontier.insert(S); } + void addNodes(ExplodedNode *N) { Frontier.Add(N); } +}; + +/// \class NodeBuilderWithSinks +/// This node builder keeps track of the generated sink nodes. +class NodeBuilderWithSinks: public NodeBuilder { + void anchor() override; + +protected: + SmallVector<ExplodedNode*, 2> sinksGenerated; + ProgramPoint &Location; + +public: + NodeBuilderWithSinks(ExplodedNode *Pred, ExplodedNodeSet &DstSet, + const NodeBuilderContext &Ctx, ProgramPoint &L) + : NodeBuilder(Pred, DstSet, Ctx), Location(L) {} + + ExplodedNode *generateNode(ProgramStateRef State, + ExplodedNode *Pred, + const ProgramPointTag *Tag = nullptr) { + const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); + return NodeBuilder::generateNode(LocalLoc, State, Pred); + } + + ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred, + const ProgramPointTag *Tag = nullptr) { + const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); + ExplodedNode *N = NodeBuilder::generateSink(LocalLoc, State, Pred); + if (N && N->isSink()) + sinksGenerated.push_back(N); + return N; + } + + const SmallVectorImpl<ExplodedNode*> &getSinks() const { + return sinksGenerated; + } +}; + +/// \class StmtNodeBuilder +/// This builder class is useful for generating nodes that resulted from +/// visiting a statement. The main difference from its parent NodeBuilder is +/// that it creates a statement specific ProgramPoint. +class StmtNodeBuilder: public NodeBuilder { + NodeBuilder *EnclosingBldr; + +public: + /// Constructs a StmtNodeBuilder. If the builder is going to process + /// nodes currently owned by another builder(with larger scope), use + /// Enclosing builder to transfer ownership. + StmtNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, + const NodeBuilderContext &Ctx, + NodeBuilder *Enclosing = nullptr) + : NodeBuilder(SrcNode, DstSet, Ctx), EnclosingBldr(Enclosing) { + if (EnclosingBldr) + EnclosingBldr->takeNodes(SrcNode); + } + + StmtNodeBuilder(ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, + const NodeBuilderContext &Ctx, + NodeBuilder *Enclosing = nullptr) + : NodeBuilder(SrcSet, DstSet, Ctx), EnclosingBldr(Enclosing) { + if (EnclosingBldr) + for (const auto I : SrcSet) + EnclosingBldr->takeNodes(I); + } + + ~StmtNodeBuilder() override; + + using NodeBuilder::generateNode; + using NodeBuilder::generateSink; + + ExplodedNode *generateNode(const Stmt *S, + ExplodedNode *Pred, + ProgramStateRef St, + const ProgramPointTag *tag = nullptr, + ProgramPoint::Kind K = ProgramPoint::PostStmtKind){ + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), tag); + return NodeBuilder::generateNode(L, St, Pred); + } + + ExplodedNode *generateSink(const Stmt *S, + ExplodedNode *Pred, + ProgramStateRef St, + const ProgramPointTag *tag = nullptr, + ProgramPoint::Kind K = ProgramPoint::PostStmtKind){ + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), tag); + return NodeBuilder::generateSink(L, St, Pred); + } +}; + +/// BranchNodeBuilder is responsible for constructing the nodes +/// corresponding to the two branches of the if statement - true and false. +class BranchNodeBuilder: public NodeBuilder { + const CFGBlock *DstT; + const CFGBlock *DstF; + + bool InFeasibleTrue; + bool InFeasibleFalse; + + void anchor() override; + +public: + BranchNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, + const NodeBuilderContext &C, + const CFGBlock *dstT, const CFGBlock *dstF) + : NodeBuilder(SrcNode, DstSet, C), DstT(dstT), DstF(dstF), + InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { + // The branch node builder does not generate autotransitions. + // If there are no successors it means that both branches are infeasible. + takeNodes(SrcNode); + } + + BranchNodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, + const NodeBuilderContext &C, + const CFGBlock *dstT, const CFGBlock *dstF) + : NodeBuilder(SrcSet, DstSet, C), DstT(dstT), DstF(dstF), + InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { + takeNodes(SrcSet); + } + + ExplodedNode *generateNode(ProgramStateRef State, bool branch, + ExplodedNode *Pred); + + const CFGBlock *getTargetBlock(bool branch) const { + return branch ? DstT : DstF; + } + + void markInfeasible(bool branch) { + if (branch) + InFeasibleTrue = true; + else + InFeasibleFalse = true; + } + + bool isFeasible(bool branch) { + return branch ? !InFeasibleTrue : !InFeasibleFalse; + } +}; + +class IndirectGotoNodeBuilder { + CoreEngine& Eng; + const CFGBlock *Src; + const CFGBlock &DispatchBlock; + const Expr *E; + ExplodedNode *Pred; + +public: + IndirectGotoNodeBuilder(ExplodedNode *pred, const CFGBlock *src, + const Expr *e, const CFGBlock *dispatch, CoreEngine* eng) + : Eng(*eng), Src(src), DispatchBlock(*dispatch), E(e), Pred(pred) {} + + class iterator { + friend class IndirectGotoNodeBuilder; + + CFGBlock::const_succ_iterator I; + + iterator(CFGBlock::const_succ_iterator i) : I(i) {} + + public: + iterator &operator++() { ++I; return *this; } + bool operator!=(const iterator &X) const { return I != X.I; } + + const LabelDecl *getLabel() const { + return cast<LabelStmt>((*I)->getLabel())->getDecl(); + } + + const CFGBlock *getBlock() const { + return *I; + } + }; + + iterator begin() { return iterator(DispatchBlock.succ_begin()); } + iterator end() { return iterator(DispatchBlock.succ_end()); } + + ExplodedNode *generateNode(const iterator &I, + ProgramStateRef State, + bool isSink = false); + + const Expr *getTarget() const { return E; } + + ProgramStateRef getState() const { return Pred->State; } + + const LocationContext *getLocationContext() const { + return Pred->getLocationContext(); + } +}; + +class SwitchNodeBuilder { + CoreEngine& Eng; + const CFGBlock *Src; + const Expr *Condition; + ExplodedNode *Pred; + +public: + SwitchNodeBuilder(ExplodedNode *pred, const CFGBlock *src, + const Expr *condition, CoreEngine* eng) + : Eng(*eng), Src(src), Condition(condition), Pred(pred) {} + + class iterator { + friend class SwitchNodeBuilder; + + CFGBlock::const_succ_reverse_iterator I; + + iterator(CFGBlock::const_succ_reverse_iterator i) : I(i) {} + + public: + iterator &operator++() { ++I; return *this; } + bool operator!=(const iterator &X) const { return I != X.I; } + bool operator==(const iterator &X) const { return I == X.I; } + + const CaseStmt *getCase() const { + return cast<CaseStmt>((*I)->getLabel()); + } + + const CFGBlock *getBlock() const { + return *I; + } + }; + + iterator begin() { return iterator(Src->succ_rbegin()+1); } + iterator end() { return iterator(Src->succ_rend()); } + + const SwitchStmt *getSwitch() const { + return cast<SwitchStmt>(Src->getTerminator()); + } + + ExplodedNode *generateCaseStmtNode(const iterator &I, + ProgramStateRef State); + + ExplodedNode *generateDefaultCaseNode(ProgramStateRef State, + bool isSink = false); + + const Expr *getCondition() const { return Condition; } + + ProgramStateRef getState() const { return Pred->State; } + + const LocationContext *getLocationContext() const { + return Pred->getLocationContext(); + } +}; + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h new file mode 100644 index 00000000..9bb1e213 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h @@ -0,0 +1,51 @@ +//== DynamicTypeInfo.h - Runtime type information ----------------*- 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 +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEINFO_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEINFO_H + +#include "clang/AST/Type.h" + +namespace clang { +namespace ento { + +/// Stores the currently inferred strictest bound on the runtime type +/// of a region in a given state along the analysis path. +class DynamicTypeInfo { +private: + QualType T; + bool CanBeASubClass; + +public: + + DynamicTypeInfo() : T(QualType()) {} + DynamicTypeInfo(QualType WithType, bool CanBeSub = true) + : T(WithType), CanBeASubClass(CanBeSub) {} + + /// Return false if no dynamic type info is available. + bool isValid() const { return !T.isNull(); } + + /// Returns the currently inferred upper bound on the runtime type. + QualType getType() const { return T; } + + /// Returns false if the type information is precise (the type T is + /// the only type in the lattice), true otherwise. + bool canBeASubClass() const { return CanBeASubClass; } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.Add(T); + ID.AddInteger((unsigned)CanBeASubClass); + } + bool operator==(const DynamicTypeInfo &X) const { + return T == X.T && CanBeASubClass == X.CanBeASubClass; + } +}; + +} // end ento +} // end clang + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h new file mode 100644 index 00000000..6608f26b --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h @@ -0,0 +1,63 @@ +//===- DynamicTypeMap.h - Dynamic type map ----------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file provides APIs for tracking dynamic type information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/ImmutableMap.h" +#include "clang/AST/Type.h" + +namespace clang { +namespace ento { + +class MemRegion; + +/// The GDM component containing the dynamic type info. This is a map from a +/// symbol to its most likely type. +struct DynamicTypeMap {}; + +using DynamicTypeMapImpl = + llvm::ImmutableMap<const MemRegion *, DynamicTypeInfo>; + +template <> +struct ProgramStateTrait<DynamicTypeMap> + : public ProgramStatePartialTrait<DynamicTypeMapImpl> { + static void *GDMIndex(); +}; + +/// Get dynamic type information for a region. +DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, + const MemRegion *Reg); + +/// Set dynamic type information of the region; return the new state. +ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, + DynamicTypeInfo NewTy); + +/// Set dynamic type information of the region; return the new state. +inline ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, + const MemRegion *Reg, QualType NewTy, + bool CanBeSubClassed = true) { + return setDynamicTypeInfo(State, Reg, + DynamicTypeInfo(NewTy, CanBeSubClassed)); +} + +void printDynamicTypeInfo(ProgramStateRef State, raw_ostream &Out, + const char *NL, const char *Sep); + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h new file mode 100644 index 00000000..6fc589b8 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h @@ -0,0 +1,125 @@ +//===- Environment.h - Map from Stmt* to Locations/Values -------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defined the Environment and EnvironmentManager classes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ENVIRONMENT_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ENVIRONMENT_H + +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/ImmutableMap.h" +#include <utility> + +namespace clang { + +class Stmt; + +namespace ento { + +class SValBuilder; +class SymbolReaper; + +/// An entry in the environment consists of a Stmt and an LocationContext. +/// This allows the environment to manage context-sensitive bindings, +/// which is essentially for modeling recursive function analysis, among +/// other things. +class EnvironmentEntry : public std::pair<const Stmt *, + const StackFrameContext *> { +public: + EnvironmentEntry(const Stmt *s, const LocationContext *L); + + const Stmt *getStmt() const { return first; } + const LocationContext *getLocationContext() const { return second; } + + /// Profile an EnvironmentEntry for inclusion in a FoldingSet. + static void Profile(llvm::FoldingSetNodeID &ID, + const EnvironmentEntry &E) { + ID.AddPointer(E.getStmt()); + ID.AddPointer(E.getLocationContext()); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + Profile(ID, *this); + } +}; + +/// An immutable map from EnvironemntEntries to SVals. +class Environment { +private: + friend class EnvironmentManager; + + using BindingsTy = llvm::ImmutableMap<EnvironmentEntry, SVal>; + + BindingsTy ExprBindings; + + Environment(BindingsTy eb) : ExprBindings(eb) {} + + SVal lookupExpr(const EnvironmentEntry &E) const; + +public: + using iterator = BindingsTy::iterator; + + iterator begin() const { return ExprBindings.begin(); } + iterator end() const { return ExprBindings.end(); } + + /// Fetches the current binding of the expression in the + /// Environment. + SVal getSVal(const EnvironmentEntry &E, SValBuilder &svalBuilder) const; + + /// Profile - Profile the contents of an Environment object for use + /// in a FoldingSet. + static void Profile(llvm::FoldingSetNodeID& ID, const Environment* env) { + env->ExprBindings.Profile(ID); + } + + /// Profile - Used to profile the contents of this object for inclusion + /// in a FoldingSet. + void Profile(llvm::FoldingSetNodeID& ID) const { + Profile(ID, this); + } + + bool operator==(const Environment& RHS) const { + return ExprBindings == RHS.ExprBindings; + } + + void print(raw_ostream &Out, const char *NL, const char *Sep, + const ASTContext &Context, + const LocationContext *WithLC = nullptr) const; +}; + +class EnvironmentManager { +private: + using FactoryTy = Environment::BindingsTy::Factory; + + FactoryTy F; + +public: + EnvironmentManager(llvm::BumpPtrAllocator &Allocator) : F(Allocator) {} + + Environment getInitialEnvironment() { + return Environment(F.getEmptyMap()); + } + + /// Bind a symbolic value to the given environment entry. + Environment bindExpr(Environment Env, const EnvironmentEntry &E, SVal V, + bool Invalidate); + + Environment removeDeadBindings(Environment Env, + SymbolReaper &SymReaper, + ProgramStateRef state); +}; + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ENVIRONMENT_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h new file mode 100644 index 00000000..727d04cb --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h @@ -0,0 +1,504 @@ +//===- ExplodedGraph.h - Local, Path-Sens. "Exploded Graph" -----*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the template classes ExplodedNode and ExplodedGraph, +// which represent a path-sensitive, intra-procedural "exploded graph." +// See "Precise interprocedural dataflow analysis via graph reachability" +// by Reps, Horwitz, and Sagiv +// (http://portal.acm.org/citation.cfm?id=199462) for the definition of an +// exploded graph. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H + +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Analysis/Support/BumpVector.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/GraphTraits.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Compiler.h" +#include <cassert> +#include <cstdint> +#include <memory> +#include <utility> +#include <vector> + +namespace clang { + +class CFG; +class Decl; +class Expr; +class ParentMap; +class Stmt; + +namespace ento { + +class ExplodedGraph; + +//===----------------------------------------------------------------------===// +// ExplodedGraph "implementation" classes. These classes are not typed to +// contain a specific kind of state. Typed-specialized versions are defined +// on top of these classes. +//===----------------------------------------------------------------------===// + +// ExplodedNode is not constified all over the engine because we need to add +// successors to it at any time after creating it. + +class ExplodedNode : public llvm::FoldingSetNode { + friend class BranchNodeBuilder; + friend class CoreEngine; + friend class EndOfFunctionNodeBuilder; + friend class ExplodedGraph; + friend class IndirectGotoNodeBuilder; + friend class NodeBuilder; + friend class SwitchNodeBuilder; + + /// Efficiently stores a list of ExplodedNodes, or an optional flag. + /// + /// NodeGroup provides opaque storage for a list of ExplodedNodes, optimizing + /// for the case when there is only one node in the group. This is a fairly + /// common case in an ExplodedGraph, where most nodes have only one + /// predecessor and many have only one successor. It can also be used to + /// store a flag rather than a node list, which ExplodedNode uses to mark + /// whether a node is a sink. If the flag is set, the group is implicitly + /// empty and no nodes may be added. + class NodeGroup { + // Conceptually a discriminated union. If the low bit is set, the node is + // a sink. If the low bit is not set, the pointer refers to the storage + // for the nodes in the group. + // This is not a PointerIntPair in order to keep the storage type opaque. + uintptr_t P; + + public: + NodeGroup(bool Flag = false) : P(Flag) { + assert(getFlag() == Flag); + } + + ExplodedNode * const *begin() const; + + ExplodedNode * const *end() const; + + unsigned size() const; + + bool empty() const { return P == 0 || getFlag() != 0; } + + /// Adds a node to the list. + /// + /// The group must not have been created with its flag set. + void addNode(ExplodedNode *N, ExplodedGraph &G); + + /// Replaces the single node in this group with a new node. + /// + /// Note that this should only be used when you know the group was not + /// created with its flag set, and that the group is empty or contains + /// only a single node. + void replaceNode(ExplodedNode *node); + + /// Returns whether this group was created with its flag set. + bool getFlag() const { + return (P & 1); + } + }; + + /// Location - The program location (within a function body) associated + /// with this node. + const ProgramPoint Location; + + /// State - The state associated with this node. + ProgramStateRef State; + + /// Preds - The predecessors of this node. + NodeGroup Preds; + + /// Succs - The successors of this node. + NodeGroup Succs; + +public: + explicit ExplodedNode(const ProgramPoint &loc, ProgramStateRef state, + bool IsSink) + : Location(loc), State(std::move(state)), Succs(IsSink) { + assert(isSink() == IsSink); + } + + /// getLocation - Returns the edge associated with the given node. + ProgramPoint getLocation() const { return Location; } + + const LocationContext *getLocationContext() const { + return getLocation().getLocationContext(); + } + + const StackFrameContext *getStackFrame() const { + return getLocation().getStackFrame(); + } + + const Decl &getCodeDecl() const { return *getLocationContext()->getDecl(); } + + CFG &getCFG() const { return *getLocationContext()->getCFG(); } + + ParentMap &getParentMap() const {return getLocationContext()->getParentMap();} + + template <typename T> + T &getAnalysis() const { + return *getLocationContext()->getAnalysis<T>(); + } + + const ProgramStateRef &getState() const { return State; } + + template <typename T> + Optional<T> getLocationAs() const LLVM_LVALUE_FUNCTION { + return Location.getAs<T>(); + } + + /// Get the value of an arbitrary expression at this node. + SVal getSVal(const Stmt *S) const { + return getState()->getSVal(S, getLocationContext()); + } + + static void Profile(llvm::FoldingSetNodeID &ID, + const ProgramPoint &Loc, + const ProgramStateRef &state, + bool IsSink) { + ID.Add(Loc); + ID.AddPointer(state.get()); + ID.AddBoolean(IsSink); + } + + void Profile(llvm::FoldingSetNodeID& ID) const { + // We avoid copy constructors by not using accessors. + Profile(ID, Location, State, isSink()); + } + + /// addPredeccessor - Adds a predecessor to the current node, and + /// in tandem add this node as a successor of the other node. + void addPredecessor(ExplodedNode *V, ExplodedGraph &G); + + unsigned succ_size() const { return Succs.size(); } + unsigned pred_size() const { return Preds.size(); } + bool succ_empty() const { return Succs.empty(); } + bool pred_empty() const { return Preds.empty(); } + + bool isSink() const { return Succs.getFlag(); } + + bool hasSinglePred() const { + return (pred_size() == 1); + } + + ExplodedNode *getFirstPred() { + return pred_empty() ? nullptr : *(pred_begin()); + } + + const ExplodedNode *getFirstPred() const { + return const_cast<ExplodedNode*>(this)->getFirstPred(); + } + + ExplodedNode *getFirstSucc() { + return succ_empty() ? nullptr : *(succ_begin()); + } + + const ExplodedNode *getFirstSucc() const { + return const_cast<ExplodedNode*>(this)->getFirstSucc(); + } + + // Iterators over successor and predecessor vertices. + using succ_iterator = ExplodedNode * const *; + using const_succ_iterator = const ExplodedNode * const *; + using pred_iterator = ExplodedNode * const *; + using const_pred_iterator = const ExplodedNode * const *; + + pred_iterator pred_begin() { return Preds.begin(); } + pred_iterator pred_end() { return Preds.end(); } + + const_pred_iterator pred_begin() const { + return const_cast<ExplodedNode*>(this)->pred_begin(); + } + const_pred_iterator pred_end() const { + return const_cast<ExplodedNode*>(this)->pred_end(); + } + + succ_iterator succ_begin() { return Succs.begin(); } + succ_iterator succ_end() { return Succs.end(); } + + const_succ_iterator succ_begin() const { + return const_cast<ExplodedNode*>(this)->succ_begin(); + } + const_succ_iterator succ_end() const { + return const_cast<ExplodedNode*>(this)->succ_end(); + } + + int64_t getID(ExplodedGraph *G) const; + + /// The node is trivial if it has only one successor, only one predecessor, + /// it's predecessor has only one successor, + /// and its program state is the same as the program state of the previous + /// node. + /// Trivial nodes may be skipped while printing exploded graph. + bool isTrivial() const; + +private: + void replaceSuccessor(ExplodedNode *node) { Succs.replaceNode(node); } + void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); } +}; + +using InterExplodedGraphMap = + llvm::DenseMap<const ExplodedNode *, const ExplodedNode *>; + +class ExplodedGraph { +protected: + friend class CoreEngine; + + // Type definitions. + using NodeVector = std::vector<ExplodedNode *>; + + /// The roots of the simulation graph. Usually there will be only + /// one, but clients are free to establish multiple subgraphs within a single + /// SimulGraph. Moreover, these subgraphs can often merge when paths from + /// different roots reach the same state at the same program location. + NodeVector Roots; + + /// The nodes in the simulation graph which have been + /// specially marked as the endpoint of an abstract simulation path. + NodeVector EndNodes; + + /// Nodes - The nodes in the graph. + llvm::FoldingSet<ExplodedNode> Nodes; + + /// BVC - Allocator and context for allocating nodes and their predecessor + /// and successor groups. + BumpVectorContext BVC; + + /// NumNodes - The number of nodes in the graph. + unsigned NumNodes = 0; + + /// A list of recently allocated nodes that can potentially be recycled. + NodeVector ChangedNodes; + + /// A list of nodes that can be reused. + NodeVector FreeNodes; + + /// Determines how often nodes are reclaimed. + /// + /// If this is 0, nodes will never be reclaimed. + unsigned ReclaimNodeInterval = 0; + + /// Counter to determine when to reclaim nodes. + unsigned ReclaimCounter; + +public: + ExplodedGraph(); + ~ExplodedGraph(); + + /// Retrieve the node associated with a (Location,State) pair, + /// where the 'Location' is a ProgramPoint in the CFG. If no node for + /// this pair exists, it is created. IsNew is set to true if + /// the node was freshly created. + ExplodedNode *getNode(const ProgramPoint &L, ProgramStateRef State, + bool IsSink = false, + bool* IsNew = nullptr); + + /// Create a node for a (Location, State) pair, + /// but don't store it for deduplication later. This + /// is useful when copying an already completed + /// ExplodedGraph for further processing. + ExplodedNode *createUncachedNode(const ProgramPoint &L, + ProgramStateRef State, + bool IsSink = false); + + std::unique_ptr<ExplodedGraph> MakeEmptyGraph() const { + return llvm::make_unique<ExplodedGraph>(); + } + + /// addRoot - Add an untyped node to the set of roots. + ExplodedNode *addRoot(ExplodedNode *V) { + Roots.push_back(V); + return V; + } + + /// addEndOfPath - Add an untyped node to the set of EOP nodes. + ExplodedNode *addEndOfPath(ExplodedNode *V) { + EndNodes.push_back(V); + return V; + } + + unsigned num_roots() const { return Roots.size(); } + unsigned num_eops() const { return EndNodes.size(); } + + bool empty() const { return NumNodes == 0; } + unsigned size() const { return NumNodes; } + + void reserve(unsigned NodeCount) { Nodes.reserve(NodeCount); } + + // Iterators. + using NodeTy = ExplodedNode; + using AllNodesTy = llvm::FoldingSet<ExplodedNode>; + using roots_iterator = NodeVector::iterator; + using const_roots_iterator = NodeVector::const_iterator; + using eop_iterator = NodeVector::iterator; + using const_eop_iterator = NodeVector::const_iterator; + using node_iterator = AllNodesTy::iterator; + using const_node_iterator = AllNodesTy::const_iterator; + + node_iterator nodes_begin() { return Nodes.begin(); } + + node_iterator nodes_end() { return Nodes.end(); } + + const_node_iterator nodes_begin() const { return Nodes.begin(); } + + const_node_iterator nodes_end() const { return Nodes.end(); } + + roots_iterator roots_begin() { return Roots.begin(); } + + roots_iterator roots_end() { return Roots.end(); } + + const_roots_iterator roots_begin() const { return Roots.begin(); } + + const_roots_iterator roots_end() const { return Roots.end(); } + + eop_iterator eop_begin() { return EndNodes.begin(); } + + eop_iterator eop_end() { return EndNodes.end(); } + + const_eop_iterator eop_begin() const { return EndNodes.begin(); } + + const_eop_iterator eop_end() const { return EndNodes.end(); } + + llvm::BumpPtrAllocator & getAllocator() { return BVC.getAllocator(); } + BumpVectorContext &getNodeAllocator() { return BVC; } + + using NodeMap = llvm::DenseMap<const ExplodedNode *, ExplodedNode *>; + + /// Creates a trimmed version of the graph that only contains paths leading + /// to the given nodes. + /// + /// \param Nodes The nodes which must appear in the final graph. Presumably + /// these are end-of-path nodes (i.e. they have no successors). + /// \param[out] ForwardMap A optional map from nodes in this graph to nodes in + /// the returned graph. + /// \param[out] InverseMap An optional map from nodes in the returned graph to + /// nodes in this graph. + /// \returns The trimmed graph + std::unique_ptr<ExplodedGraph> + trim(ArrayRef<const NodeTy *> Nodes, + InterExplodedGraphMap *ForwardMap = nullptr, + InterExplodedGraphMap *InverseMap = nullptr) const; + + /// Enable tracking of recently allocated nodes for potential reclamation + /// when calling reclaimRecentlyAllocatedNodes(). + void enableNodeReclamation(unsigned Interval) { + ReclaimCounter = ReclaimNodeInterval = Interval; + } + + /// Reclaim "uninteresting" nodes created since the last time this method + /// was called. + void reclaimRecentlyAllocatedNodes(); + + /// Returns true if nodes for the given expression kind are always + /// kept around. + static bool isInterestingLValueExpr(const Expr *Ex); + +private: + bool shouldCollect(const ExplodedNode *node); + void collectNode(ExplodedNode *node); +}; + +class ExplodedNodeSet { + using ImplTy = llvm::SmallSetVector<ExplodedNode *, 4>; + ImplTy Impl; + +public: + ExplodedNodeSet(ExplodedNode *N) { + assert(N && !static_cast<ExplodedNode*>(N)->isSink()); + Impl.insert(N); + } + + ExplodedNodeSet() = default; + + void Add(ExplodedNode *N) { + if (N && !static_cast<ExplodedNode*>(N)->isSink()) Impl.insert(N); + } + + using iterator = ImplTy::iterator; + using const_iterator = ImplTy::const_iterator; + + unsigned size() const { return Impl.size(); } + bool empty() const { return Impl.empty(); } + bool erase(ExplodedNode *N) { return Impl.remove(N); } + + void clear() { Impl.clear(); } + + void insert(const ExplodedNodeSet &S) { + assert(&S != this); + if (empty()) + Impl = S.Impl; + else + Impl.insert(S.begin(), S.end()); + } + + iterator begin() { return Impl.begin(); } + iterator end() { return Impl.end(); } + + const_iterator begin() const { return Impl.begin(); } + const_iterator end() const { return Impl.end(); } +}; + +} // namespace ento + +} // namespace clang + +// GraphTraits + +namespace llvm { + template <> struct GraphTraits<clang::ento::ExplodedGraph *> { + using GraphTy = clang::ento::ExplodedGraph *; + using NodeRef = clang::ento::ExplodedNode *; + using ChildIteratorType = clang::ento::ExplodedNode::succ_iterator; + using nodes_iterator = llvm::df_iterator<GraphTy>; + + static NodeRef getEntryNode(const GraphTy G) { + return *G->roots_begin(); + } + + static bool predecessorOfTrivial(NodeRef N) { + return N->succ_size() == 1 && N->getFirstSucc()->isTrivial(); + } + + static ChildIteratorType child_begin(NodeRef N) { + if (predecessorOfTrivial(N)) + return child_begin(*N->succ_begin()); + return N->succ_begin(); + } + + static ChildIteratorType child_end(NodeRef N) { + if (predecessorOfTrivial(N)) + return child_end(N->getFirstSucc()); + return N->succ_end(); + } + + static nodes_iterator nodes_begin(const GraphTy G) { + return df_begin(G); + } + + static nodes_iterator nodes_end(const GraphTy G) { + return df_end(G); + } + }; +} // namespace llvm + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h new file mode 100644 index 00000000..605e1c78 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -0,0 +1,848 @@ +//===- ExprEngine.h - Path-Sensitive Expression-Level Dataflow --*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines a meta-engine for path-sensitive dataflow analysis that +// is built on CoreEngine, but provides the boilerplate to execute transfer +// functions and build the ExplodedGraph at the expression level. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPRENGINE_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPRENGINE_H + +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/DomainSpecific/ObjCNoReturn.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" +#include "llvm/ADT/ArrayRef.h" +#include <cassert> +#include <utility> + +namespace clang { + +class AnalysisDeclContextManager; +class AnalyzerOptions; +class ASTContext; +class ConstructionContext; +class CXXBindTemporaryExpr; +class CXXCatchStmt; +class CXXConstructExpr; +class CXXDeleteExpr; +class CXXNewExpr; +class CXXThisExpr; +class Decl; +class DeclStmt; +class GCCAsmStmt; +class LambdaExpr; +class LocationContext; +class MaterializeTemporaryExpr; +class MSAsmStmt; +class NamedDecl; +class ObjCAtSynchronizedStmt; +class ObjCForCollectionStmt; +class ObjCIvarRefExpr; +class ObjCMessageExpr; +class ReturnStmt; +class Stmt; + +namespace cross_tu { + +class CrossTranslationUnitContext; + +} // namespace cross_tu + +namespace ento { + +class BasicValueFactory; +class CallEvent; +class CheckerManager; +class ConstraintManager; +class CXXTempObjectRegion; +class MemRegion; +class RegionAndSymbolInvalidationTraits; +class SymbolManager; + +class ExprEngine : public SubEngine { +public: + /// The modes of inlining, which override the default analysis-wide settings. + enum InliningModes { + /// Follow the default settings for inlining callees. + Inline_Regular = 0, + + /// Do minimal inlining of callees. + Inline_Minimal = 0x1 + }; + + /// Hints for figuring out of a call should be inlined during evalCall(). + struct EvalCallOptions { + /// This call is a constructor or a destructor for which we do not currently + /// compute the this-region correctly. + bool IsCtorOrDtorWithImproperlyModeledTargetRegion = false; + + /// This call is a constructor or a destructor for a single element within + /// an array, a part of array construction or destruction. + bool IsArrayCtorOrDtor = false; + + /// This call is a constructor or a destructor of a temporary value. + bool IsTemporaryCtorOrDtor = false; + + /// This call is a constructor for a temporary that is lifetime-extended + /// by binding it to a reference-type field within an aggregate, + /// for example 'A { const C &c; }; A a = { C() };' + bool IsTemporaryLifetimeExtendedViaAggregate = false; + + EvalCallOptions() {} + }; + +private: + cross_tu::CrossTranslationUnitContext &CTU; + + AnalysisManager &AMgr; + + AnalysisDeclContextManager &AnalysisDeclContexts; + + CoreEngine Engine; + + /// G - the simulation graph. + ExplodedGraph &G; + + /// StateMgr - Object that manages the data for all created states. + ProgramStateManager StateMgr; + + /// SymMgr - Object that manages the symbol information. + SymbolManager &SymMgr; + + /// MRMgr - MemRegionManager object that creates memory regions. + MemRegionManager &MRMgr; + + /// svalBuilder - SValBuilder object that creates SVals from expressions. + SValBuilder &svalBuilder; + + unsigned int currStmtIdx = 0; + const NodeBuilderContext *currBldrCtx = nullptr; + + /// Helper object to determine if an Objective-C message expression + /// implicitly never returns. + ObjCNoReturn ObjCNoRet; + + /// The BugReporter associated with this engine. It is important that + /// this object be placed at the very end of member variables so that its + /// destructor is called before the rest of the ExprEngine is destroyed. + GRBugReporter BR; + + /// The functions which have been analyzed through inlining. This is owned by + /// AnalysisConsumer. It can be null. + SetOfConstDecls *VisitedCallees; + + /// The flag, which specifies the mode of inlining for the engine. + InliningModes HowToInline; + +public: + ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, AnalysisManager &mgr, + SetOfConstDecls *VisitedCalleesIn, + FunctionSummariesTy *FS, InliningModes HowToInlineIn); + + ~ExprEngine() override; + + /// Returns true if there is still simulation state on the worklist. + bool ExecuteWorkList(const LocationContext *L, unsigned Steps = 150000) { + return Engine.ExecuteWorkList(L, Steps, nullptr); + } + + /// Execute the work list with an initial state. Nodes that reaches the exit + /// of the function are added into the Dst set, which represent the exit + /// state of the function call. Returns true if there is still simulation + /// state on the worklist. + bool ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps, + ProgramStateRef InitState, + ExplodedNodeSet &Dst) { + return Engine.ExecuteWorkListWithInitialState(L, Steps, InitState, Dst); + } + + /// getContext - Return the ASTContext associated with this analysis. + ASTContext &getContext() const { return AMgr.getASTContext(); } + + AnalysisManager &getAnalysisManager() override { return AMgr; } + + AnalysisDeclContextManager &getAnalysisDeclContextManager() { + return AMgr.getAnalysisDeclContextManager(); + } + + CheckerManager &getCheckerManager() const { + return *AMgr.getCheckerManager(); + } + + SValBuilder &getSValBuilder() { return svalBuilder; } + + BugReporter &getBugReporter() { return BR; } + + cross_tu::CrossTranslationUnitContext * + getCrossTranslationUnitContext() override { + return &CTU; + } + + const NodeBuilderContext &getBuilderContext() { + assert(currBldrCtx); + return *currBldrCtx; + } + + const Stmt *getStmt() const; + + void GenerateAutoTransition(ExplodedNode *N); + void enqueueEndOfPath(ExplodedNodeSet &S); + void GenerateCallExitNode(ExplodedNode *N); + + + /// Dump graph to the specified filename. + /// If filename is empty, generate a temporary one. + /// \return The filename the graph is written into. + std::string DumpGraph(bool trim = false, StringRef Filename=""); + + /// Dump the graph consisting of the given nodes to a specified filename. + /// Generate a temporary filename if it's not provided. + /// \return The filename the graph is written into. + std::string DumpGraph(ArrayRef<const ExplodedNode *> Nodes, + StringRef Filename = ""); + + /// Visualize the ExplodedGraph created by executing the simulation. + void ViewGraph(bool trim = false); + + /// Visualize a trimmed ExplodedGraph that only contains paths to the given + /// nodes. + void ViewGraph(ArrayRef<const ExplodedNode *> Nodes); + + /// getInitialState - Return the initial state used for the root vertex + /// in the ExplodedGraph. + ProgramStateRef getInitialState(const LocationContext *InitLoc) override; + + ExplodedGraph &getGraph() { return G; } + const ExplodedGraph &getGraph() const { return G; } + + /// Run the analyzer's garbage collection - remove dead symbols and + /// bindings from the state. + /// + /// Checkers can participate in this process with two callbacks: + /// \c checkLiveSymbols and \c checkDeadSymbols. See the CheckerDocumentation + /// class for more information. + /// + /// \param Node The predecessor node, from which the processing should start. + /// \param Out The returned set of output nodes. + /// \param ReferenceStmt The statement which is about to be processed. + /// Everything needed for this statement should be considered live. + /// A null statement means that everything in child LocationContexts + /// is dead. + /// \param LC The location context of the \p ReferenceStmt. A null location + /// context means that we have reached the end of analysis and that + /// all statements and local variables should be considered dead. + /// \param DiagnosticStmt Used as a location for any warnings that should + /// occur while removing the dead (e.g. leaks). By default, the + /// \p ReferenceStmt is used. + /// \param K Denotes whether this is a pre- or post-statement purge. This + /// must only be ProgramPoint::PostStmtPurgeDeadSymbolsKind if an + /// entire location context is being cleared, in which case the + /// \p ReferenceStmt must either be a ReturnStmt or \c NULL. Otherwise, + /// it must be ProgramPoint::PreStmtPurgeDeadSymbolsKind (the default) + /// and \p ReferenceStmt must be valid (non-null). + void removeDead(ExplodedNode *Node, ExplodedNodeSet &Out, + const Stmt *ReferenceStmt, const LocationContext *LC, + const Stmt *DiagnosticStmt = nullptr, + ProgramPoint::Kind K = ProgramPoint::PreStmtPurgeDeadSymbolsKind); + + /// processCFGElement - Called by CoreEngine. Used to generate new successor + /// nodes by processing the 'effects' of a CFG element. + void processCFGElement(const CFGElement E, ExplodedNode *Pred, + unsigned StmtIdx, NodeBuilderContext *Ctx) override; + + void ProcessStmt(const Stmt *S, ExplodedNode *Pred); + + void ProcessLoopExit(const Stmt* S, ExplodedNode *Pred); + + void ProcessInitializer(const CFGInitializer I, ExplodedNode *Pred); + + void ProcessImplicitDtor(const CFGImplicitDtor D, ExplodedNode *Pred); + + void ProcessNewAllocator(const CXXNewExpr *NE, ExplodedNode *Pred); + + void ProcessAutomaticObjDtor(const CFGAutomaticObjDtor D, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + void ProcessDeleteDtor(const CFGDeleteDtor D, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + void ProcessBaseDtor(const CFGBaseDtor D, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + void ProcessMemberDtor(const CFGMemberDtor D, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + void ProcessTemporaryDtor(const CFGTemporaryDtor D, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + + /// Called by CoreEngine when processing the entrance of a CFGBlock. + void processCFGBlockEntrance(const BlockEdge &L, + NodeBuilderWithSinks &nodeBuilder, + ExplodedNode *Pred) override; + + /// ProcessBranch - Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a branch condition. + void processBranch(const Stmt *Condition, + NodeBuilderContext& BuilderCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) override; + + /// Called by CoreEngine. + /// Used to generate successor nodes for temporary destructors depending + /// on whether the corresponding constructor was visited. + void processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, + NodeBuilderContext &BldCtx, + ExplodedNode *Pred, ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) override; + + /// Called by CoreEngine. Used to processing branching behavior + /// at static initializers. + void processStaticInitializer(const DeclStmt *DS, + NodeBuilderContext& BuilderCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) override; + + /// processIndirectGoto - Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a computed goto jump. + void processIndirectGoto(IndirectGotoNodeBuilder& builder) override; + + /// ProcessSwitch - Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a switch statement. + void processSwitch(SwitchNodeBuilder& builder) override; + + /// Called by CoreEngine. Used to notify checkers that processing a + /// function has begun. Called for both inlined and and top-level functions. + void processBeginOfFunction(NodeBuilderContext &BC, + ExplodedNode *Pred, ExplodedNodeSet &Dst, + const BlockEdge &L) override; + + /// Called by CoreEngine. Used to notify checkers that processing a + /// function has ended. Called for both inlined and and top-level functions. + void processEndOfFunction(NodeBuilderContext& BC, + ExplodedNode *Pred, + const ReturnStmt *RS = nullptr) override; + + /// Remove dead bindings/symbols before exiting a function. + void removeDeadOnEndOfFunction(NodeBuilderContext& BC, + ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// Generate the entry node of the callee. + void processCallEnter(NodeBuilderContext& BC, CallEnter CE, + ExplodedNode *Pred) override; + + /// Generate the sequence of nodes that simulate the call exit and the post + /// visit for CallExpr. + void processCallExit(ExplodedNode *Pred) override; + + /// Called by CoreEngine when the analysis worklist has terminated. + void processEndWorklist() override; + + /// evalAssume - Callback function invoked by the ConstraintManager when + /// making assumptions about state values. + ProgramStateRef processAssume(ProgramStateRef state, SVal cond, + bool assumption) override; + + /// processRegionChanges - Called by ProgramStateManager whenever a change is made + /// to the store. Used to update checkers that track region values. + ProgramStateRef + processRegionChanges(ProgramStateRef state, + const InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call) override; + + /// printState - Called by ProgramStateManager to print checker-specific data. + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, + const char *Sep, + const LocationContext *LCtx = nullptr) override; + + ProgramStateManager &getStateManager() override { return StateMgr; } + + StoreManager &getStoreManager() { return StateMgr.getStoreManager(); } + + ConstraintManager &getConstraintManager() { + return StateMgr.getConstraintManager(); + } + + // FIXME: Remove when we migrate over to just using SValBuilder. + BasicValueFactory &getBasicVals() { + return StateMgr.getBasicVals(); + } + + SymbolManager &getSymbolManager() { return SymMgr; } + MemRegionManager &getRegionManager() { return MRMgr; } + + + // Functions for external checking of whether we have unfinished work + bool wasBlocksExhausted() const { return Engine.wasBlocksExhausted(); } + bool hasEmptyWorkList() const { return !Engine.getWorkList()->hasWork(); } + bool hasWorkRemaining() const { return Engine.hasWorkRemaining(); } + + const CoreEngine &getCoreEngine() const { return Engine; } + +public: + /// Visit - Transfer function logic for all statements. Dispatches to + /// other functions that handle specific kinds of statements. + void Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst); + + /// VisitArraySubscriptExpr - Transfer function for array accesses. + void VisitArraySubscriptExpr(const ArraySubscriptExpr *Ex, + ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitGCCAsmStmt - Transfer function logic for inline asm. + void VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitMSAsmStmt - Transfer function logic for MS inline asm. + void VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitBlockExpr - Transfer function logic for BlockExprs. + void VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitLambdaExpr - Transfer function logic for LambdaExprs. + void VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitBinaryOperator - Transfer function logic for binary operators. + void VisitBinaryOperator(const BinaryOperator* B, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + + /// VisitCall - Transfer function for function calls. + void VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitCast - Transfer function logic for all casts (implicit and explicit). + void VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitCompoundLiteralExpr - Transfer function logic for compound literals. + void VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + + /// Transfer function logic for DeclRefExprs and BlockDeclRefExprs. + void VisitCommonDeclRefExpr(const Expr *DR, const NamedDecl *D, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + + /// VisitDeclStmt - Transfer function logic for DeclStmts. + void VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitGuardedExpr - Transfer function logic for ?, __builtin_choose + void VisitGuardedExpr(const Expr *Ex, const Expr *L, const Expr *R, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + + void VisitInitListExpr(const InitListExpr *E, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitLogicalExpr - Transfer function logic for '&&', '||' + void VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitMemberExpr - Transfer function for member expressions. + void VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitAtomicExpr - Transfer function for builtin atomic expressions + void VisitAtomicExpr(const AtomicExpr *E, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// Transfer function logic for ObjCAtSynchronizedStmts. + void VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + + /// Transfer function logic for computing the lvalue of an Objective-C ivar. + void VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *DR, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitObjCForCollectionStmt - Transfer function logic for + /// ObjCForCollectionStmt. + void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + + void VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitReturnStmt - Transfer function logic for return statements. + void VisitReturnStmt(const ReturnStmt *R, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitOffsetOfExpr - Transfer function for offsetof. + void VisitOffsetOfExpr(const OffsetOfExpr *Ex, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitUnaryExprOrTypeTraitExpr - Transfer function for sizeof. + void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + + /// VisitUnaryOperator - Transfer function logic for unary operators. + void VisitUnaryOperator(const UnaryOperator* B, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// Handle ++ and -- (both pre- and post-increment). + void VisitIncrementDecrementOperator(const UnaryOperator* U, + ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, + ExplodedNodeSet &PreVisit, + ExplodedNodeSet &Dst); + + void VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + void VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, + ExplodedNodeSet & Dst); + + void VisitCXXConstructExpr(const CXXConstructExpr *E, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, + const Stmt *S, bool IsBaseDtor, + ExplodedNode *Pred, ExplodedNodeSet &Dst, + const EvalCallOptions &Options); + + void VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, + ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + void VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + void VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// Create a C++ temporary object for an rvalue. + void CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, + ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// evalEagerlyAssumeBinOpBifurcation - Given the nodes in 'Src', eagerly assume symbolic + /// expressions of the form 'x != 0' and generate new nodes (stored in Dst) + /// with those assumptions. + void evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, + const Expr *Ex); + + static std::pair<const ProgramPointTag *, const ProgramPointTag *> + geteagerlyAssumeBinOpBifurcationTags(); + + SVal evalMinus(SVal X) { + return X.isValid() ? svalBuilder.evalMinus(X.castAs<NonLoc>()) : X; + } + + SVal evalComplement(SVal X) { + return X.isValid() ? svalBuilder.evalComplement(X.castAs<NonLoc>()) : X; + } + + ProgramStateRef handleLValueBitCast(ProgramStateRef state, const Expr *Ex, + const LocationContext *LCtx, QualType T, + QualType ExTy, const CastExpr *CastE, + StmtNodeBuilder &Bldr, + ExplodedNode *Pred); + + ProgramStateRef handleLVectorSplat(ProgramStateRef state, + const LocationContext *LCtx, + const CastExpr *CastE, + StmtNodeBuilder &Bldr, + ExplodedNode *Pred); + + void handleUOExtension(ExplodedNodeSet::iterator I, + const UnaryOperator* U, + StmtNodeBuilder &Bldr); + +public: + SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, + NonLoc L, NonLoc R, QualType T) { + return svalBuilder.evalBinOpNN(state, op, L, R, T); + } + + SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, + NonLoc L, SVal R, QualType T) { + return R.isValid() ? svalBuilder.evalBinOpNN(state, op, L, + R.castAs<NonLoc>(), T) : R; + } + + SVal evalBinOp(ProgramStateRef ST, BinaryOperator::Opcode Op, + SVal LHS, SVal RHS, QualType T) { + return svalBuilder.evalBinOp(ST, Op, LHS, RHS, T); + } + + /// By looking at a certain item that may be potentially part of an object's + /// ConstructionContext, retrieve such object's location. A particular + /// statement can be transparently passed as \p Item in most cases. + static Optional<SVal> + getObjectUnderConstruction(ProgramStateRef State, + const ConstructionContextItem &Item, + const LocationContext *LC); + +protected: + /// evalBind - Handle the semantics of binding a value to a specific location. + /// This method is used by evalStore, VisitDeclStmt, and others. + void evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *Pred, + SVal location, SVal Val, bool atDeclInit = false, + const ProgramPoint *PP = nullptr); + + /// Call PointerEscape callback when a value escapes as a result of bind. + ProgramStateRef processPointerEscapedOnBind(ProgramStateRef State, + SVal Loc, + SVal Val, + const LocationContext *LCtx) override; + /// Call PointerEscape callback when a value escapes as a result of + /// region invalidation. + /// \param[in] ITraits Specifies invalidation traits for regions/symbols. + ProgramStateRef notifyCheckersOfPointerEscape( + ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + const CallEvent *Call, + RegionAndSymbolInvalidationTraits &ITraits) override; + + /// A simple wrapper when you only need to notify checkers of pointer-escape + /// of a single value. + ProgramStateRef escapeValue(ProgramStateRef State, SVal V, + PointerEscapeKind K) const; + +public: + // FIXME: 'tag' should be removed, and a LocationContext should be used + // instead. + // FIXME: Comment on the meaning of the arguments, when 'St' may not + // be the same as Pred->state, and when 'location' may not be the + // same as state->getLValue(Ex). + /// Simulate a read of the result of Ex. + void evalLoad(ExplodedNodeSet &Dst, + const Expr *NodeEx, /* Eventually will be a CFGStmt */ + const Expr *BoundExpr, + ExplodedNode *Pred, + ProgramStateRef St, + SVal location, + const ProgramPointTag *tag = nullptr, + QualType LoadTy = QualType()); + + // FIXME: 'tag' should be removed, and a LocationContext should be used + // instead. + void evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, const Expr *StoreE, + ExplodedNode *Pred, ProgramStateRef St, SVal TargetLV, SVal Val, + const ProgramPointTag *tag = nullptr); + + /// Return the CFG element corresponding to the worklist element + /// that is currently being processed by ExprEngine. + CFGElement getCurrentCFGElement() { + return (*currBldrCtx->getBlock())[currStmtIdx]; + } + + /// Create a new state in which the call return value is binded to the + /// call origin expression. + ProgramStateRef bindReturnValue(const CallEvent &Call, + const LocationContext *LCtx, + ProgramStateRef State); + + /// Evaluate a call, running pre- and post-call checks and allowing checkers + /// to be responsible for handling the evaluation of the call itself. + void evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, + const CallEvent &Call); + + /// Default implementation of call evaluation. + void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred, + const CallEvent &Call, + const EvalCallOptions &CallOpts = {}); + +private: + ProgramStateRef finishArgumentConstruction(ProgramStateRef State, + const CallEvent &Call); + void finishArgumentConstruction(ExplodedNodeSet &Dst, ExplodedNode *Pred, + const CallEvent &Call); + + void evalLoadCommon(ExplodedNodeSet &Dst, + const Expr *NodeEx, /* Eventually will be a CFGStmt */ + const Expr *BoundEx, + ExplodedNode *Pred, + ProgramStateRef St, + SVal location, + const ProgramPointTag *tag, + QualType LoadTy); + + void evalLocation(ExplodedNodeSet &Dst, + const Stmt *NodeEx, /* This will eventually be a CFGStmt */ + const Stmt *BoundEx, + ExplodedNode *Pred, + ProgramStateRef St, + SVal location, + bool isLoad); + + /// Count the stack depth and determine if the call is recursive. + void examineStackFrames(const Decl *D, const LocationContext *LCtx, + bool &IsRecursive, unsigned &StackDepth); + + enum CallInlinePolicy { + CIP_Allowed, + CIP_DisallowedOnce, + CIP_DisallowedAlways + }; + + /// See if a particular call should be inlined, by only looking + /// at the call event and the current state of analysis. + CallInlinePolicy mayInlineCallKind(const CallEvent &Call, + const ExplodedNode *Pred, + AnalyzerOptions &Opts, + const EvalCallOptions &CallOpts); + + /// Checks our policies and decides weither the given call should be inlined. + bool shouldInlineCall(const CallEvent &Call, const Decl *D, + const ExplodedNode *Pred, + const EvalCallOptions &CallOpts = {}); + + bool inlineCall(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, + ExplodedNode *Pred, ProgramStateRef State); + + /// Conservatively evaluate call by invalidating regions and binding + /// a conjured return value. + void conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, + ExplodedNode *Pred, ProgramStateRef State); + + /// Either inline or process the call conservatively (or both), based + /// on DynamicDispatchBifurcation data. + void BifurcateCall(const MemRegion *BifurReg, + const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, + ExplodedNode *Pred); + + bool replayWithoutInlining(ExplodedNode *P, const LocationContext *CalleeLC); + + /// Models a trivial copy or move constructor or trivial assignment operator + /// call with a simple bind. + void performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, + const CallEvent &Call); + + /// If the value of the given expression \p InitWithAdjustments is a NonLoc, + /// copy it into a new temporary object region, and replace the value of the + /// expression with that. + /// + /// If \p Result is provided, the new region will be bound to this expression + /// instead of \p InitWithAdjustments. + /// + /// Returns the temporary region with adjustments into the optional + /// OutRegionWithAdjustments out-parameter if a new region was indeed needed, + /// otherwise sets it to nullptr. + ProgramStateRef createTemporaryRegionIfNeeded( + ProgramStateRef State, const LocationContext *LC, + const Expr *InitWithAdjustments, const Expr *Result = nullptr, + const SubRegion **OutRegionWithAdjustments = nullptr); + + /// Returns a region representing the first element of a (possibly + /// multi-dimensional) array, for the purposes of element construction or + /// destruction. + /// + /// On return, \p Ty will be set to the base type of the array. + /// + /// If the type is not an array type at all, the original value is returned. + /// Otherwise the "IsArray" flag is set. + static SVal makeZeroElementRegion(ProgramStateRef State, SVal LValue, + QualType &Ty, bool &IsArray); + + /// For a DeclStmt or CXXInitCtorInitializer, walk backward in the current CFG + /// block to find the constructor expression that directly constructed into + /// the storage for this statement. Returns null if the constructor for this + /// statement created a temporary object region rather than directly + /// constructing into an existing region. + const CXXConstructExpr *findDirectConstructorForCurrentCFGElement(); + + /// Update the program state with all the path-sensitive information + /// that's necessary to perform construction of an object with a given + /// syntactic construction context. If the construction context is unavailable + /// or unusable for any reason, a dummy temporary region is returned, and the + /// IsConstructorWithImproperlyModeledTargetRegion flag is set in \p CallOpts. + /// Returns the updated program state and the new object's this-region. + std::pair<ProgramStateRef, SVal> prepareForObjectConstruction( + const Expr *E, ProgramStateRef State, const LocationContext *LCtx, + const ConstructionContext *CC, EvalCallOptions &CallOpts); + + /// Store the location of a C++ object corresponding to a statement + /// until the statement is actually encountered. For example, if a DeclStmt + /// has CXXConstructExpr as its initializer, the object would be considered + /// to be "under construction" between CXXConstructExpr and DeclStmt. + /// This allows, among other things, to keep bindings to variable's fields + /// made within the constructor alive until its declaration actually + /// goes into scope. + static ProgramStateRef + addObjectUnderConstruction(ProgramStateRef State, + const ConstructionContextItem &Item, + const LocationContext *LC, SVal V); + + /// Mark the object sa fully constructed, cleaning up the state trait + /// that tracks objects under construction. + static ProgramStateRef + finishObjectConstruction(ProgramStateRef State, + const ConstructionContextItem &Item, + const LocationContext *LC); + + /// If the given expression corresponds to a temporary that was used for + /// passing into an elidable copy/move constructor and that constructor + /// was actually elided, track that we also need to elide the destructor. + static ProgramStateRef elideDestructor(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC); + + /// Stop tracking the destructor that corresponds to an elided constructor. + static ProgramStateRef + cleanupElidedDestructor(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC); + + /// Returns true if the given expression corresponds to a temporary that + /// was constructed for passing into an elidable copy/move constructor + /// and that constructor was actually elided. + static bool isDestructorElided(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC); + + /// Check if all objects under construction have been fully constructed + /// for the given context range (including FromLC, not including ToLC). + /// This is useful for assertions. Also checks if elided destructors + /// were cleaned up. + static bool areAllObjectsFullyConstructed(ProgramStateRef State, + const LocationContext *FromLC, + const LocationContext *ToLC); +}; + +/// Traits for storing the call processing policy inside GDM. +/// The GDM stores the corresponding CallExpr pointer. +// FIXME: This does not use the nice trait macros because it must be accessible +// from multiple translation units. +struct ReplayWithoutInlining{}; +template <> +struct ProgramStateTrait<ReplayWithoutInlining> : + public ProgramStatePartialTrait<const void*> { + static void *GDMIndex(); +}; + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPRENGINE_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h new file mode 100644 index 00000000..53b4bf60 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h @@ -0,0 +1,142 @@ +//===- FunctionSummary.h - Stores summaries of functions. -------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines a summary of a function gathered/used by static analysis. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_FUNCTIONSUMMARY_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_FUNCTIONSUMMARY_H + +#include "clang/AST/Decl.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallBitVector.h" +#include <cassert> +#include <deque> +#include <utility> + +namespace clang { +namespace ento { + +using SetOfDecls = std::deque<Decl *>; +using SetOfConstDecls = llvm::DenseSet<const Decl *>; + +class FunctionSummariesTy { + class FunctionSummary { + public: + /// Marks the IDs of the basic blocks visited during the analyzes. + llvm::SmallBitVector VisitedBasicBlocks; + + /// Total number of blocks in the function. + unsigned TotalBasicBlocks : 30; + + /// True if this function has been checked against the rules for which + /// functions may be inlined. + unsigned InlineChecked : 1; + + /// True if this function may be inlined. + unsigned MayInline : 1; + + /// The number of times the function has been inlined. + unsigned TimesInlined : 32; + + FunctionSummary() + : TotalBasicBlocks(0), InlineChecked(0), MayInline(0), + TimesInlined(0) {} + }; + + using MapTy = llvm::DenseMap<const Decl *, FunctionSummary>; + MapTy Map; + +public: + MapTy::iterator findOrInsertSummary(const Decl *D) { + MapTy::iterator I = Map.find(D); + if (I != Map.end()) + return I; + + using KVPair = std::pair<const Decl *, FunctionSummary>; + + I = Map.insert(KVPair(D, FunctionSummary())).first; + assert(I != Map.end()); + return I; + } + + void markMayInline(const Decl *D) { + MapTy::iterator I = findOrInsertSummary(D); + I->second.InlineChecked = 1; + I->second.MayInline = 1; + } + + void markShouldNotInline(const Decl *D) { + MapTy::iterator I = findOrInsertSummary(D); + I->second.InlineChecked = 1; + I->second.MayInline = 0; + } + + void markReachedMaxBlockCount(const Decl *D) { + markShouldNotInline(D); + } + + Optional<bool> mayInline(const Decl *D) { + MapTy::const_iterator I = Map.find(D); + if (I != Map.end() && I->second.InlineChecked) + return I->second.MayInline; + return None; + } + + void markVisitedBasicBlock(unsigned ID, const Decl* D, unsigned TotalIDs) { + MapTy::iterator I = findOrInsertSummary(D); + llvm::SmallBitVector &Blocks = I->second.VisitedBasicBlocks; + assert(ID < TotalIDs); + if (TotalIDs > Blocks.size()) { + Blocks.resize(TotalIDs); + I->second.TotalBasicBlocks = TotalIDs; + } + Blocks.set(ID); + } + + unsigned getNumVisitedBasicBlocks(const Decl* D) { + MapTy::const_iterator I = Map.find(D); + if (I != Map.end()) + return I->second.VisitedBasicBlocks.count(); + return 0; + } + + unsigned getNumTimesInlined(const Decl* D) { + MapTy::const_iterator I = Map.find(D); + if (I != Map.end()) + return I->second.TimesInlined; + return 0; + } + + void bumpNumTimesInlined(const Decl* D) { + MapTy::iterator I = findOrInsertSummary(D); + I->second.TimesInlined++; + } + + /// Get the percentage of the reachable blocks. + unsigned getPercentBlocksReachable(const Decl *D) { + MapTy::const_iterator I = Map.find(D); + if (I != Map.end()) + return ((I->second.VisitedBasicBlocks.count() * 100) / + I->second.TotalBasicBlocks); + return 0; + } + + unsigned getTotalNumBasicBlocks(); + unsigned getTotalNumVisitedBasicBlocks(); +}; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_FUNCTIONSUMMARY_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h new file mode 100644 index 00000000..d25d2643 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h @@ -0,0 +1,49 @@ +//===--- LoopUnrolling.h - Unroll loops -------------------------*- 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 +// +//===----------------------------------------------------------------------===// +/// +/// This header contains the declarations of functions which are used to decide +/// which loops should be completely unrolled and mark their corresponding +/// CFGBlocks. It is done by tracking a stack of loops in the ProgramState. This +/// way specific loops can be marked as completely unrolled. For considering a +/// loop to be completely unrolled it has to fulfill the following requirements: +/// - Currently only forStmts can be considered. +/// - The bound has to be known. +/// - The counter variable has not escaped before/in the body of the loop and +/// changed only in the increment statement corresponding to the loop. It also +/// has to be initialized by a literal in the corresponding initStmt. +/// - Does not contain goto, switch and returnStmt. +/// +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_LOOPUNROLLING_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_LOOPUNROLLING_H + +#include "clang/Analysis/CFG.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +namespace clang { +namespace ento { +class AnalysisManager; + +/// Returns if the given State indicates that is inside a completely unrolled +/// loop. +bool isUnrolledState(ProgramStateRef State); + +/// Updates the stack of loops contained by the ProgramState. +ProgramStateRef updateLoopStack(const Stmt *LoopStmt, ASTContext &ASTCtx, + ExplodedNode* Pred, unsigned maxVisitOnPath); + +/// Updates the given ProgramState. In current implementation it removes the top +/// element of the stack of loops. +ProgramStateRef processLoopEnd(const Stmt *LoopStmt, ProgramStateRef State); + +} // end namespace ento +} // end namespace clang + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h new file mode 100644 index 00000000..7484a51b --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h @@ -0,0 +1,35 @@ +//===--- LoopWidening.h - Widen loops ---------------------------*- 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 +// +//===----------------------------------------------------------------------===// +/// +/// This header contains the declarations of functions which are used to widen +/// loops which do not otherwise exit. The widening is done by invalidating +/// anything which might be modified by the body of the loop. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_LOOPWIDENING_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_LOOPWIDENING_H + +#include "clang/Analysis/CFG.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" + +namespace clang { +namespace ento { + +/// Get the states that result from widening the loop. +/// +/// Widen the loop by invalidating anything that might be modified +/// by the loop body in any iteration. +ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState, + const LocationContext *LCtx, + unsigned BlockCount, const Stmt *LoopStmt); + +} // end namespace ento +} // end namespace clang + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h new file mode 100644 index 00000000..071e3508 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -0,0 +1,1488 @@ +//==- MemRegion.h - Abstract memory regions for static analysis -*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines MemRegion and its subclasses. MemRegion defines a +// partially-typed abstraction of memory useful for path-sensitive dataflow +// analyses. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_MEMREGION_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_MEMREGION_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerIntPair.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include <cassert> +#include <cstdint> +#include <limits> +#include <string> +#include <utility> + +namespace clang { + +class AnalysisDeclContext; +class CXXRecordDecl; +class Decl; +class LocationContext; +class StackFrameContext; + +namespace ento { + +class CodeTextRegion; +class MemRegion; +class MemRegionManager; +class MemSpaceRegion; +class SValBuilder; +class SymbolicRegion; +class VarRegion; + +/// Represent a region's offset within the top level base region. +class RegionOffset { + /// The base region. + const MemRegion *R = nullptr; + + /// The bit offset within the base region. Can be negative. + int64_t Offset; + +public: + // We're using a const instead of an enumeration due to the size required; + // Visual Studio will only create enumerations of size int, not long long. + static const int64_t Symbolic = std::numeric_limits<int64_t>::max(); + + RegionOffset() = default; + RegionOffset(const MemRegion *r, int64_t off) : R(r), Offset(off) {} + + const MemRegion *getRegion() const { return R; } + + bool hasSymbolicOffset() const { return Offset == Symbolic; } + + int64_t getOffset() const { + assert(!hasSymbolicOffset()); + return Offset; + } + + bool isValid() const { return R; } +}; + +//===----------------------------------------------------------------------===// +// Base region classes. +//===----------------------------------------------------------------------===// + +/// MemRegion - The root abstract class for all memory regions. +class MemRegion : public llvm::FoldingSetNode { +public: + enum Kind { +#define REGION(Id, Parent) Id ## Kind, +#define REGION_RANGE(Id, First, Last) BEGIN_##Id = First, END_##Id = Last, +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + }; + +private: + const Kind kind; + mutable Optional<RegionOffset> cachedOffset; + +protected: + MemRegion(Kind k) : kind(k) {} + virtual ~MemRegion(); + +public: + ASTContext &getContext() const; + + virtual void Profile(llvm::FoldingSetNodeID& ID) const = 0; + + virtual MemRegionManager* getMemRegionManager() const = 0; + + const MemSpaceRegion *getMemorySpace() const; + + const MemRegion *getBaseRegion() const; + + /// Recursively retrieve the region of the most derived class instance of + /// regions of C++ base class instances. + const MemRegion *getMostDerivedObjectRegion() const; + + /// Check if the region is a subregion of the given region. + /// Each region is a subregion of itself. + virtual bool isSubRegionOf(const MemRegion *R) const; + + const MemRegion *StripCasts(bool StripBaseAndDerivedCasts = true) const; + + /// If this is a symbolic region, returns the region. Otherwise, + /// goes up the base chain looking for the first symbolic base region. + const SymbolicRegion *getSymbolicBase() const; + + bool hasGlobalsOrParametersStorage() const; + + bool hasStackStorage() const; + + bool hasStackNonParametersStorage() const; + + bool hasStackParametersStorage() const; + + /// Compute the offset within the top level memory object. + RegionOffset getAsOffset() const; + + /// Get a string representation of a region for debug use. + std::string getString() const; + + virtual void dumpToStream(raw_ostream &os) const; + + void dump() const; + + /// Returns true if this region can be printed in a user-friendly way. + virtual bool canPrintPretty() const; + + /// Print the region for use in diagnostics. + virtual void printPretty(raw_ostream &os) const; + + /// Returns true if this region's textual representation can be used + /// as part of a larger expression. + virtual bool canPrintPrettyAsExpr() const; + + /// Print the region as expression. + /// + /// When this region represents a subexpression, the method is for printing + /// an expression containing it. + virtual void printPrettyAsExpr(raw_ostream &os) const; + + Kind getKind() const { return kind; } + + template<typename RegionTy> const RegionTy* getAs() const; + + virtual bool isBoundable() const { return false; } + + /// Get descriptive name for memory region. The name is obtained from + /// the variable/field declaration retrieved from the memory region. + /// Regions that point to an element of an array are returned as: "arr[0]". + /// Regions that point to a struct are returned as: "st.var". + // + /// \param UseQuotes Set if the name should be quoted. + /// + /// \returns variable name for memory region + std::string getDescriptiveName(bool UseQuotes = true) const; + + /// Retrieve source range from memory region. The range retrieval + /// is based on the decl obtained from the memory region. + /// For a VarRegion the range of the base region is returned. + /// For a FieldRegion the range of the field is returned. + /// If no declaration is found, an empty source range is returned. + /// The client is responsible for checking if the returned range is valid. + /// + /// \returns source range for declaration retrieved from memory region + SourceRange sourceRange() const; +}; + +/// MemSpaceRegion - A memory region that represents a "memory space"; +/// for example, the set of global variables, the stack frame, etc. +class MemSpaceRegion : public MemRegion { +protected: + MemRegionManager *Mgr; + + MemSpaceRegion(MemRegionManager *mgr, Kind k) : MemRegion(k), Mgr(mgr) { + assert(classof(this)); + assert(mgr); + } + + MemRegionManager* getMemRegionManager() const override { return Mgr; } + +public: + bool isBoundable() const override { return false; } + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + static bool classof(const MemRegion *R) { + Kind k = R->getKind(); + return k >= BEGIN_MEMSPACES && k <= END_MEMSPACES; + } +}; + +/// CodeSpaceRegion - The memory space that holds the executable code of +/// functions and blocks. +class CodeSpaceRegion : public MemSpaceRegion { + friend class MemRegionManager; + + CodeSpaceRegion(MemRegionManager *mgr) + : MemSpaceRegion(mgr, CodeSpaceRegionKind) {} + +public: + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion *R) { + return R->getKind() == CodeSpaceRegionKind; + } +}; + +class GlobalsSpaceRegion : public MemSpaceRegion { + virtual void anchor(); + +protected: + GlobalsSpaceRegion(MemRegionManager *mgr, Kind k) : MemSpaceRegion(mgr, k) { + assert(classof(this)); + } + +public: + static bool classof(const MemRegion *R) { + Kind k = R->getKind(); + return k >= BEGIN_GLOBAL_MEMSPACES && k <= END_GLOBAL_MEMSPACES; + } +}; + +/// The region of the static variables within the current CodeTextRegion +/// scope. +/// +/// Currently, only the static locals are placed there, so we know that these +/// variables do not get invalidated by calls to other functions. +class StaticGlobalSpaceRegion : public GlobalsSpaceRegion { + friend class MemRegionManager; + + const CodeTextRegion *CR; + + StaticGlobalSpaceRegion(MemRegionManager *mgr, const CodeTextRegion *cr) + : GlobalsSpaceRegion(mgr, StaticGlobalSpaceRegionKind), CR(cr) { + assert(cr); + } + +public: + void Profile(llvm::FoldingSetNodeID &ID) const override; + + void dumpToStream(raw_ostream &os) const override; + + const CodeTextRegion *getCodeRegion() const { return CR; } + + static bool classof(const MemRegion *R) { + return R->getKind() == StaticGlobalSpaceRegionKind; + } +}; + +/// The region for all the non-static global variables. +/// +/// This class is further split into subclasses for efficient implementation of +/// invalidating a set of related global values as is done in +/// RegionStoreManager::invalidateRegions (instead of finding all the dependent +/// globals, we invalidate the whole parent region). +class NonStaticGlobalSpaceRegion : public GlobalsSpaceRegion { + void anchor() override; + +protected: + NonStaticGlobalSpaceRegion(MemRegionManager *mgr, Kind k) + : GlobalsSpaceRegion(mgr, k) { + assert(classof(this)); + } + +public: + static bool classof(const MemRegion *R) { + Kind k = R->getKind(); + return k >= BEGIN_NON_STATIC_GLOBAL_MEMSPACES && + k <= END_NON_STATIC_GLOBAL_MEMSPACES; + } +}; + +/// The region containing globals which are defined in system/external +/// headers and are considered modifiable by system calls (ex: errno). +class GlobalSystemSpaceRegion : public NonStaticGlobalSpaceRegion { + friend class MemRegionManager; + + GlobalSystemSpaceRegion(MemRegionManager *mgr) + : NonStaticGlobalSpaceRegion(mgr, GlobalSystemSpaceRegionKind) {} + +public: + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion *R) { + return R->getKind() == GlobalSystemSpaceRegionKind; + } +}; + +/// The region containing globals which are considered not to be modified +/// or point to data which could be modified as a result of a function call +/// (system or internal). Ex: Const global scalars would be modeled as part of +/// this region. This region also includes most system globals since they have +/// low chance of being modified. +class GlobalImmutableSpaceRegion : public NonStaticGlobalSpaceRegion { + friend class MemRegionManager; + + GlobalImmutableSpaceRegion(MemRegionManager *mgr) + : NonStaticGlobalSpaceRegion(mgr, GlobalImmutableSpaceRegionKind) {} + +public: + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion *R) { + return R->getKind() == GlobalImmutableSpaceRegionKind; + } +}; + +/// The region containing globals which can be modified by calls to +/// "internally" defined functions - (for now just) functions other then system +/// calls. +class GlobalInternalSpaceRegion : public NonStaticGlobalSpaceRegion { + friend class MemRegionManager; + + GlobalInternalSpaceRegion(MemRegionManager *mgr) + : NonStaticGlobalSpaceRegion(mgr, GlobalInternalSpaceRegionKind) {} + +public: + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion *R) { + return R->getKind() == GlobalInternalSpaceRegionKind; + } +}; + +class HeapSpaceRegion : public MemSpaceRegion { + friend class MemRegionManager; + + HeapSpaceRegion(MemRegionManager *mgr) + : MemSpaceRegion(mgr, HeapSpaceRegionKind) {} + +public: + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion *R) { + return R->getKind() == HeapSpaceRegionKind; + } +}; + +class UnknownSpaceRegion : public MemSpaceRegion { + friend class MemRegionManager; + + UnknownSpaceRegion(MemRegionManager *mgr) + : MemSpaceRegion(mgr, UnknownSpaceRegionKind) {} + +public: + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion *R) { + return R->getKind() == UnknownSpaceRegionKind; + } +}; + +class StackSpaceRegion : public MemSpaceRegion { + virtual void anchor(); + + const StackFrameContext *SFC; + +protected: + StackSpaceRegion(MemRegionManager *mgr, Kind k, const StackFrameContext *sfc) + : MemSpaceRegion(mgr, k), SFC(sfc) { + assert(classof(this)); + assert(sfc); + } + +public: + const StackFrameContext *getStackFrame() const { return SFC; } + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + static bool classof(const MemRegion *R) { + Kind k = R->getKind(); + return k >= BEGIN_STACK_MEMSPACES && k <= END_STACK_MEMSPACES; + } +}; + +class StackLocalsSpaceRegion : public StackSpaceRegion { + friend class MemRegionManager; + + StackLocalsSpaceRegion(MemRegionManager *mgr, const StackFrameContext *sfc) + : StackSpaceRegion(mgr, StackLocalsSpaceRegionKind, sfc) {} + +public: + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion *R) { + return R->getKind() == StackLocalsSpaceRegionKind; + } +}; + +class StackArgumentsSpaceRegion : public StackSpaceRegion { +private: + friend class MemRegionManager; + + StackArgumentsSpaceRegion(MemRegionManager *mgr, const StackFrameContext *sfc) + : StackSpaceRegion(mgr, StackArgumentsSpaceRegionKind, sfc) {} + +public: + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion *R) { + return R->getKind() == StackArgumentsSpaceRegionKind; + } +}; + +/// SubRegion - A region that subsets another larger region. Most regions +/// are subclasses of SubRegion. +class SubRegion : public MemRegion { + virtual void anchor(); + +protected: + const MemRegion* superRegion; + + SubRegion(const MemRegion *sReg, Kind k) : MemRegion(k), superRegion(sReg) { + assert(classof(this)); + assert(sReg); + } + +public: + const MemRegion* getSuperRegion() const { + return superRegion; + } + + /// getExtent - Returns the size of the region in bytes. + virtual DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const { + return UnknownVal(); + } + + MemRegionManager* getMemRegionManager() const override; + + bool isSubRegionOf(const MemRegion* R) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() > END_MEMSPACES; + } +}; + +//===----------------------------------------------------------------------===// +// MemRegion subclasses. +//===----------------------------------------------------------------------===// + +/// AllocaRegion - A region that represents an untyped blob of bytes created +/// by a call to 'alloca'. +class AllocaRegion : public SubRegion { + friend class MemRegionManager; + + // Block counter. Used to distinguish different pieces of memory allocated by + // alloca at the same call site. + unsigned Cnt; + + const Expr *Ex; + + AllocaRegion(const Expr *ex, unsigned cnt, const MemSpaceRegion *superRegion) + : SubRegion(superRegion, AllocaRegionKind), Cnt(cnt), Ex(ex) { + assert(Ex); + } + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const Expr *Ex, + unsigned Cnt, const MemRegion *superRegion); + +public: + const Expr *getExpr() const { return Ex; } + + bool isBoundable() const override { return true; } + + DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; + + void Profile(llvm::FoldingSetNodeID& ID) const override; + + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == AllocaRegionKind; + } +}; + +/// TypedRegion - An abstract class representing regions that are typed. +class TypedRegion : public SubRegion { + void anchor() override; + +protected: + TypedRegion(const MemRegion *sReg, Kind k) : SubRegion(sReg, k) { + assert(classof(this)); + } + +public: + virtual QualType getLocationType() const = 0; + + QualType getDesugaredLocationType(ASTContext &Context) const { + return getLocationType().getDesugaredType(Context); + } + + bool isBoundable() const override { return true; } + + static bool classof(const MemRegion* R) { + unsigned k = R->getKind(); + return k >= BEGIN_TYPED_REGIONS && k <= END_TYPED_REGIONS; + } +}; + +/// TypedValueRegion - An abstract class representing regions having a typed value. +class TypedValueRegion : public TypedRegion { + void anchor() override; + +protected: + TypedValueRegion(const MemRegion* sReg, Kind k) : TypedRegion(sReg, k) { + assert(classof(this)); + } + +public: + virtual QualType getValueType() const = 0; + + QualType getLocationType() const override { + // FIXME: We can possibly optimize this later to cache this value. + QualType T = getValueType(); + ASTContext &ctx = getContext(); + if (T->getAs<ObjCObjectType>()) + return ctx.getObjCObjectPointerType(T); + return ctx.getPointerType(getValueType()); + } + + QualType getDesugaredValueType(ASTContext &Context) const { + QualType T = getValueType(); + return T.getTypePtrOrNull() ? T.getDesugaredType(Context) : T; + } + + DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; + + static bool classof(const MemRegion* R) { + unsigned k = R->getKind(); + return k >= BEGIN_TYPED_VALUE_REGIONS && k <= END_TYPED_VALUE_REGIONS; + } +}; + +class CodeTextRegion : public TypedRegion { + void anchor() override; + +protected: + CodeTextRegion(const MemSpaceRegion *sreg, Kind k) : TypedRegion(sreg, k) { + assert(classof(this)); + } + +public: + bool isBoundable() const override { return false; } + + static bool classof(const MemRegion* R) { + Kind k = R->getKind(); + return k >= BEGIN_CODE_TEXT_REGIONS && k <= END_CODE_TEXT_REGIONS; + } +}; + +/// FunctionCodeRegion - A region that represents code texts of function. +class FunctionCodeRegion : public CodeTextRegion { + friend class MemRegionManager; + + const NamedDecl *FD; + + FunctionCodeRegion(const NamedDecl *fd, const CodeSpaceRegion* sreg) + : CodeTextRegion(sreg, FunctionCodeRegionKind), FD(fd) { + assert(isa<ObjCMethodDecl>(fd) || isa<FunctionDecl>(fd)); + } + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const NamedDecl *FD, + const MemRegion*); + +public: + QualType getLocationType() const override { + const ASTContext &Ctx = getContext(); + if (const auto *D = dyn_cast<FunctionDecl>(FD)) { + return Ctx.getPointerType(D->getType()); + } + + assert(isa<ObjCMethodDecl>(FD)); + assert(false && "Getting the type of ObjCMethod is not supported yet"); + + // TODO: We might want to return a different type here (ex: id (*ty)(...)) + // depending on how it is used. + return {}; + } + + const NamedDecl *getDecl() const { + return FD; + } + + void dumpToStream(raw_ostream &os) const override; + + void Profile(llvm::FoldingSetNodeID& ID) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == FunctionCodeRegionKind; + } +}; + +/// BlockCodeRegion - A region that represents code texts of blocks (closures). +/// Blocks are represented with two kinds of regions. BlockCodeRegions +/// represent the "code", while BlockDataRegions represent instances of blocks, +/// which correspond to "code+data". The distinction is important, because +/// like a closure a block captures the values of externally referenced +/// variables. +class BlockCodeRegion : public CodeTextRegion { + friend class MemRegionManager; + + const BlockDecl *BD; + AnalysisDeclContext *AC; + CanQualType locTy; + + BlockCodeRegion(const BlockDecl *bd, CanQualType lTy, + AnalysisDeclContext *ac, const CodeSpaceRegion* sreg) + : CodeTextRegion(sreg, BlockCodeRegionKind), BD(bd), AC(ac), locTy(lTy) { + assert(bd); + assert(ac); + assert(lTy->getTypePtr()->isBlockPointerType()); + } + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const BlockDecl *BD, + CanQualType, const AnalysisDeclContext*, + const MemRegion*); + +public: + QualType getLocationType() const override { + return locTy; + } + + const BlockDecl *getDecl() const { + return BD; + } + + AnalysisDeclContext *getAnalysisDeclContext() const { return AC; } + + void dumpToStream(raw_ostream &os) const override; + + void Profile(llvm::FoldingSetNodeID& ID) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == BlockCodeRegionKind; + } +}; + +/// BlockDataRegion - A region that represents a block instance. +/// Blocks are represented with two kinds of regions. BlockCodeRegions +/// represent the "code", while BlockDataRegions represent instances of blocks, +/// which correspond to "code+data". The distinction is important, because +/// like a closure a block captures the values of externally referenced +/// variables. +class BlockDataRegion : public TypedRegion { + friend class MemRegionManager; + + const BlockCodeRegion *BC; + const LocationContext *LC; // Can be null + unsigned BlockCount; + void *ReferencedVars = nullptr; + void *OriginalVars = nullptr; + + BlockDataRegion(const BlockCodeRegion *bc, const LocationContext *lc, + unsigned count, const MemSpaceRegion *sreg) + : TypedRegion(sreg, BlockDataRegionKind), BC(bc), LC(lc), + BlockCount(count) { + assert(bc); + assert(lc); + assert(isa<GlobalImmutableSpaceRegion>(sreg) || + isa<StackLocalsSpaceRegion>(sreg) || + isa<UnknownSpaceRegion>(sreg)); + } + + static void ProfileRegion(llvm::FoldingSetNodeID&, const BlockCodeRegion *, + const LocationContext *, unsigned, + const MemRegion *); + +public: + const BlockCodeRegion *getCodeRegion() const { return BC; } + + const BlockDecl *getDecl() const { return BC->getDecl(); } + + QualType getLocationType() const override { return BC->getLocationType(); } + + class referenced_vars_iterator { + const MemRegion * const *R; + const MemRegion * const *OriginalR; + + public: + explicit referenced_vars_iterator(const MemRegion * const *r, + const MemRegion * const *originalR) + : R(r), OriginalR(originalR) {} + + const VarRegion *getCapturedRegion() const { + return cast<VarRegion>(*R); + } + + const VarRegion *getOriginalRegion() const { + return cast<VarRegion>(*OriginalR); + } + + bool operator==(const referenced_vars_iterator &I) const { + assert((R == nullptr) == (I.R == nullptr)); + return I.R == R; + } + + bool operator!=(const referenced_vars_iterator &I) const { + assert((R == nullptr) == (I.R == nullptr)); + return I.R != R; + } + + referenced_vars_iterator &operator++() { + ++R; + ++OriginalR; + return *this; + } + }; + + /// Return the original region for a captured region, if + /// one exists. + const VarRegion *getOriginalRegion(const VarRegion *VR) const; + + referenced_vars_iterator referenced_vars_begin() const; + referenced_vars_iterator referenced_vars_end() const; + + void dumpToStream(raw_ostream &os) const override; + + void Profile(llvm::FoldingSetNodeID& ID) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == BlockDataRegionKind; + } + +private: + void LazyInitializeReferencedVars(); + std::pair<const VarRegion *, const VarRegion *> + getCaptureRegions(const VarDecl *VD); +}; + +/// SymbolicRegion - A special, "non-concrete" region. Unlike other region +/// classes, SymbolicRegion represents a region that serves as an alias for +/// either a real region, a NULL pointer, etc. It essentially is used to +/// map the concept of symbolic values into the domain of regions. Symbolic +/// regions do not need to be typed. +class SymbolicRegion : public SubRegion { + friend class MemRegionManager; + + const SymbolRef sym; + + SymbolicRegion(const SymbolRef s, const MemSpaceRegion *sreg) + : SubRegion(sreg, SymbolicRegionKind), sym(s) { + // Because pointer arithmetic is represented by ElementRegion layers, + // the base symbol here should not contain any arithmetic. + assert(s && isa<SymbolData>(s)); + assert(s->getType()->isAnyPointerType() || + s->getType()->isReferenceType() || + s->getType()->isBlockPointerType()); + assert(isa<UnknownSpaceRegion>(sreg) || isa<HeapSpaceRegion>(sreg)); + } + +public: + SymbolRef getSymbol() const { return sym; } + + bool isBoundable() const override { return true; } + + DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; + + void Profile(llvm::FoldingSetNodeID& ID) const override; + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, + SymbolRef sym, + const MemRegion* superRegion); + + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == SymbolicRegionKind; + } +}; + +/// StringRegion - Region associated with a StringLiteral. +class StringRegion : public TypedValueRegion { + friend class MemRegionManager; + + const StringLiteral *Str; + + StringRegion(const StringLiteral *str, const GlobalInternalSpaceRegion *sreg) + : TypedValueRegion(sreg, StringRegionKind), Str(str) { + assert(str); + } + + static void ProfileRegion(llvm::FoldingSetNodeID &ID, + const StringLiteral *Str, + const MemRegion *superRegion); + +public: + const StringLiteral *getStringLiteral() const { return Str; } + + QualType getValueType() const override { return Str->getType(); } + + DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; + + bool isBoundable() const override { return false; } + + void Profile(llvm::FoldingSetNodeID& ID) const override { + ProfileRegion(ID, Str, superRegion); + } + + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == StringRegionKind; + } +}; + +/// The region associated with an ObjCStringLiteral. +class ObjCStringRegion : public TypedValueRegion { + friend class MemRegionManager; + + const ObjCStringLiteral *Str; + + ObjCStringRegion(const ObjCStringLiteral *str, + const GlobalInternalSpaceRegion *sreg) + : TypedValueRegion(sreg, ObjCStringRegionKind), Str(str) { + assert(str); + } + + static void ProfileRegion(llvm::FoldingSetNodeID &ID, + const ObjCStringLiteral *Str, + const MemRegion *superRegion); + +public: + const ObjCStringLiteral *getObjCStringLiteral() const { return Str; } + + QualType getValueType() const override { return Str->getType(); } + + bool isBoundable() const override { return false; } + + void Profile(llvm::FoldingSetNodeID& ID) const override { + ProfileRegion(ID, Str, superRegion); + } + + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == ObjCStringRegionKind; + } +}; + +/// CompoundLiteralRegion - A memory region representing a compound literal. +/// Compound literals are essentially temporaries that are stack allocated +/// or in the global constant pool. +class CompoundLiteralRegion : public TypedValueRegion { + friend class MemRegionManager; + + const CompoundLiteralExpr *CL; + + CompoundLiteralRegion(const CompoundLiteralExpr *cl, + const MemSpaceRegion *sReg) + : TypedValueRegion(sReg, CompoundLiteralRegionKind), CL(cl) { + assert(cl); + assert(isa<GlobalInternalSpaceRegion>(sReg) || + isa<StackLocalsSpaceRegion>(sReg)); + } + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, + const CompoundLiteralExpr *CL, + const MemRegion* superRegion); + +public: + QualType getValueType() const override { return CL->getType(); } + + bool isBoundable() const override { return !CL->isFileScope(); } + + void Profile(llvm::FoldingSetNodeID& ID) const override; + + void dumpToStream(raw_ostream &os) const override; + + const CompoundLiteralExpr *getLiteralExpr() const { return CL; } + + static bool classof(const MemRegion* R) { + return R->getKind() == CompoundLiteralRegionKind; + } +}; + +class DeclRegion : public TypedValueRegion { +protected: + const ValueDecl *D; + + DeclRegion(const ValueDecl *d, const MemRegion *sReg, Kind k) + : TypedValueRegion(sReg, k), D(d) { + assert(classof(this)); + assert(d && d->isCanonicalDecl()); + } + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D, + const MemRegion* superRegion, Kind k); + +public: + const ValueDecl *getDecl() const { return D; } + void Profile(llvm::FoldingSetNodeID& ID) const override; + + static bool classof(const MemRegion* R) { + unsigned k = R->getKind(); + return k >= BEGIN_DECL_REGIONS && k <= END_DECL_REGIONS; + } +}; + +class VarRegion : public DeclRegion { + friend class MemRegionManager; + + // Constructors and private methods. + VarRegion(const VarDecl *vd, const MemRegion *sReg) + : DeclRegion(vd, sReg, VarRegionKind) { + // VarRegion appears in unknown space when it's a block variable as seen + // from a block using it, when this block is analyzed at top-level. + // Other block variables appear within block data regions, + // which, unlike everything else on this list, are not memory spaces. + assert(isa<GlobalsSpaceRegion>(sReg) || isa<StackSpaceRegion>(sReg) || + isa<BlockDataRegion>(sReg) || isa<UnknownSpaceRegion>(sReg)); + } + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const VarDecl *VD, + const MemRegion *superRegion) { + DeclRegion::ProfileRegion(ID, VD, superRegion, VarRegionKind); + } + +public: + void Profile(llvm::FoldingSetNodeID& ID) const override; + + const VarDecl *getDecl() const { return cast<VarDecl>(D); } + + const StackFrameContext *getStackFrame() const; + + QualType getValueType() const override { + // FIXME: We can cache this if needed. + return getDecl()->getType(); + } + + void dumpToStream(raw_ostream &os) const override; + + bool canPrintPrettyAsExpr() const override; + + void printPrettyAsExpr(raw_ostream &os) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == VarRegionKind; + } +}; + +/// CXXThisRegion - Represents the region for the implicit 'this' parameter +/// in a call to a C++ method. This region doesn't represent the object +/// referred to by 'this', but rather 'this' itself. +class CXXThisRegion : public TypedValueRegion { + friend class MemRegionManager; + + CXXThisRegion(const PointerType *thisPointerTy, + const StackArgumentsSpaceRegion *sReg) + : TypedValueRegion(sReg, CXXThisRegionKind), + ThisPointerTy(thisPointerTy) { + assert(ThisPointerTy->getPointeeType()->getAsCXXRecordDecl() && + "Invalid region type!"); + } + + static void ProfileRegion(llvm::FoldingSetNodeID &ID, + const PointerType *PT, + const MemRegion *sReg); + +public: + void Profile(llvm::FoldingSetNodeID &ID) const override; + + QualType getValueType() const override { + return QualType(ThisPointerTy, 0); + } + + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == CXXThisRegionKind; + } + +private: + const PointerType *ThisPointerTy; +}; + +class FieldRegion : public DeclRegion { + friend class MemRegionManager; + + FieldRegion(const FieldDecl *fd, const SubRegion* sReg) + : DeclRegion(fd, sReg, FieldRegionKind) {} + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const FieldDecl *FD, + const MemRegion* superRegion) { + DeclRegion::ProfileRegion(ID, FD, superRegion, FieldRegionKind); + } + +public: + const FieldDecl *getDecl() const { return cast<FieldDecl>(D); } + + QualType getValueType() const override { + // FIXME: We can cache this if needed. + return getDecl()->getType(); + } + + DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; + + void dumpToStream(raw_ostream &os) const override; + + bool canPrintPretty() const override; + void printPretty(raw_ostream &os) const override; + bool canPrintPrettyAsExpr() const override; + void printPrettyAsExpr(raw_ostream &os) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == FieldRegionKind; + } +}; + +class ObjCIvarRegion : public DeclRegion { + friend class MemRegionManager; + + ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg); + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const ObjCIvarDecl *ivd, + const MemRegion* superRegion); + +public: + const ObjCIvarDecl *getDecl() const; + QualType getValueType() const override; + + bool canPrintPrettyAsExpr() const override; + void printPrettyAsExpr(raw_ostream &os) const override; + + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == ObjCIvarRegionKind; + } +}; + +//===----------------------------------------------------------------------===// +// Auxiliary data classes for use with MemRegions. +//===----------------------------------------------------------------------===// + +class RegionRawOffset { + friend class ElementRegion; + + const MemRegion *Region; + CharUnits Offset; + + RegionRawOffset(const MemRegion* reg, CharUnits offset = CharUnits::Zero()) + : Region(reg), Offset(offset) {} + +public: + // FIXME: Eventually support symbolic offsets. + CharUnits getOffset() const { return Offset; } + const MemRegion *getRegion() const { return Region; } + + void dumpToStream(raw_ostream &os) const; + void dump() const; +}; + +/// ElementRegion is used to represent both array elements and casts. +class ElementRegion : public TypedValueRegion { + friend class MemRegionManager; + + QualType ElementType; + NonLoc Index; + + ElementRegion(QualType elementType, NonLoc Idx, const SubRegion *sReg) + : TypedValueRegion(sReg, ElementRegionKind), ElementType(elementType), + Index(Idx) { + assert((!Idx.getAs<nonloc::ConcreteInt>() || + Idx.castAs<nonloc::ConcreteInt>().getValue().isSigned()) && + "The index must be signed"); + assert(!elementType.isNull() && !elementType->isVoidType() && + "Invalid region type!"); + } + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, QualType elementType, + SVal Idx, const MemRegion* superRegion); + +public: + NonLoc getIndex() const { return Index; } + + QualType getValueType() const override { return ElementType; } + + QualType getElementType() const { return ElementType; } + + /// Compute the offset within the array. The array might also be a subobject. + RegionRawOffset getAsArrayOffset() const; + + void dumpToStream(raw_ostream &os) const override; + + void Profile(llvm::FoldingSetNodeID& ID) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == ElementRegionKind; + } +}; + +// C++ temporary object associated with an expression. +class CXXTempObjectRegion : public TypedValueRegion { + friend class MemRegionManager; + + Expr const *Ex; + + CXXTempObjectRegion(Expr const *E, MemSpaceRegion const *sReg) + : TypedValueRegion(sReg, CXXTempObjectRegionKind), Ex(E) { + assert(E); + assert(isa<StackLocalsSpaceRegion>(sReg) || + isa<GlobalInternalSpaceRegion>(sReg)); + } + + static void ProfileRegion(llvm::FoldingSetNodeID &ID, + Expr const *E, const MemRegion *sReg); + +public: + const Expr *getExpr() const { return Ex; } + + QualType getValueType() const override { return Ex->getType(); } + + void dumpToStream(raw_ostream &os) const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == CXXTempObjectRegionKind; + } +}; + +// CXXBaseObjectRegion represents a base object within a C++ object. It is +// identified by the base class declaration and the region of its parent object. +class CXXBaseObjectRegion : public TypedValueRegion { + friend class MemRegionManager; + + llvm::PointerIntPair<const CXXRecordDecl *, 1, bool> Data; + + CXXBaseObjectRegion(const CXXRecordDecl *RD, bool IsVirtual, + const SubRegion *SReg) + : TypedValueRegion(SReg, CXXBaseObjectRegionKind), Data(RD, IsVirtual) { + assert(RD); + } + + static void ProfileRegion(llvm::FoldingSetNodeID &ID, const CXXRecordDecl *RD, + bool IsVirtual, const MemRegion *SReg); + +public: + const CXXRecordDecl *getDecl() const { return Data.getPointer(); } + bool isVirtual() const { return Data.getInt(); } + + QualType getValueType() const override; + + void dumpToStream(raw_ostream &os) const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + bool canPrintPrettyAsExpr() const override; + + void printPrettyAsExpr(raw_ostream &os) const override; + + static bool classof(const MemRegion *region) { + return region->getKind() == CXXBaseObjectRegionKind; + } +}; + +// CXXDerivedObjectRegion represents a derived-class object that surrounds +// a C++ object. It is identified by the derived class declaration and the +// region of its parent object. It is a bit counter-intuitive (but not otherwise +// unseen) that this region represents a larger segment of memory that its +// super-region. +class CXXDerivedObjectRegion : public TypedValueRegion { + friend class MemRegionManager; + + const CXXRecordDecl *DerivedD; + + CXXDerivedObjectRegion(const CXXRecordDecl *DerivedD, const SubRegion *SReg) + : TypedValueRegion(SReg, CXXDerivedObjectRegionKind), DerivedD(DerivedD) { + assert(DerivedD); + // In case of a concrete region, it should always be possible to model + // the base-to-derived cast by undoing a previous derived-to-base cast, + // otherwise the cast is most likely ill-formed. + assert(SReg->getSymbolicBase() && + "Should have unwrapped a base region instead!"); + } + + static void ProfileRegion(llvm::FoldingSetNodeID &ID, const CXXRecordDecl *RD, + const MemRegion *SReg); + +public: + const CXXRecordDecl *getDecl() const { return DerivedD; } + + QualType getValueType() const override; + + void dumpToStream(raw_ostream &os) const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + bool canPrintPrettyAsExpr() const override; + + void printPrettyAsExpr(raw_ostream &os) const override; + + static bool classof(const MemRegion *region) { + return region->getKind() == CXXDerivedObjectRegionKind; + } +}; + +template<typename RegionTy> +const RegionTy* MemRegion::getAs() const { + if (const auto *RT = dyn_cast<RegionTy>(this)) + return RT; + + return nullptr; +} + +//===----------------------------------------------------------------------===// +// MemRegionManager - Factory object for creating regions. +//===----------------------------------------------------------------------===// + +class MemRegionManager { + ASTContext &C; + llvm::BumpPtrAllocator& A; + llvm::FoldingSet<MemRegion> Regions; + + GlobalInternalSpaceRegion *InternalGlobals = nullptr; + GlobalSystemSpaceRegion *SystemGlobals = nullptr; + GlobalImmutableSpaceRegion *ImmutableGlobals = nullptr; + + llvm::DenseMap<const StackFrameContext *, StackLocalsSpaceRegion *> + StackLocalsSpaceRegions; + llvm::DenseMap<const StackFrameContext *, StackArgumentsSpaceRegion *> + StackArgumentsSpaceRegions; + llvm::DenseMap<const CodeTextRegion *, StaticGlobalSpaceRegion *> + StaticsGlobalSpaceRegions; + + HeapSpaceRegion *heap = nullptr; + UnknownSpaceRegion *unknown = nullptr; + CodeSpaceRegion *code = nullptr; + +public: + MemRegionManager(ASTContext &c, llvm::BumpPtrAllocator &a) : C(c), A(a) {} + ~MemRegionManager(); + + ASTContext &getContext() { return C; } + + llvm::BumpPtrAllocator &getAllocator() { return A; } + + /// getStackLocalsRegion - Retrieve the memory region associated with the + /// specified stack frame. + const StackLocalsSpaceRegion * + getStackLocalsRegion(const StackFrameContext *STC); + + /// getStackArgumentsRegion - Retrieve the memory region associated with + /// function/method arguments of the specified stack frame. + const StackArgumentsSpaceRegion * + getStackArgumentsRegion(const StackFrameContext *STC); + + /// getGlobalsRegion - Retrieve the memory region associated with + /// global variables. + const GlobalsSpaceRegion *getGlobalsRegion( + MemRegion::Kind K = MemRegion::GlobalInternalSpaceRegionKind, + const CodeTextRegion *R = nullptr); + + /// getHeapRegion - Retrieve the memory region associated with the + /// generic "heap". + const HeapSpaceRegion *getHeapRegion(); + + /// getUnknownRegion - Retrieve the memory region associated with unknown + /// memory space. + const UnknownSpaceRegion *getUnknownRegion(); + + const CodeSpaceRegion *getCodeRegion(); + + /// getAllocaRegion - Retrieve a region associated with a call to alloca(). + const AllocaRegion *getAllocaRegion(const Expr *Ex, unsigned Cnt, + const LocationContext *LC); + + /// getCompoundLiteralRegion - Retrieve the region associated with a + /// given CompoundLiteral. + const CompoundLiteralRegion* + getCompoundLiteralRegion(const CompoundLiteralExpr *CL, + const LocationContext *LC); + + /// getCXXThisRegion - Retrieve the [artificial] region associated with the + /// parameter 'this'. + const CXXThisRegion *getCXXThisRegion(QualType thisPointerTy, + const LocationContext *LC); + + /// Retrieve or create a "symbolic" memory region. + const SymbolicRegion* getSymbolicRegion(SymbolRef Sym); + + /// Return a unique symbolic region belonging to heap memory space. + const SymbolicRegion *getSymbolicHeapRegion(SymbolRef sym); + + const StringRegion *getStringRegion(const StringLiteral *Str); + + const ObjCStringRegion *getObjCStringRegion(const ObjCStringLiteral *Str); + + /// getVarRegion - Retrieve or create the memory region associated with + /// a specified VarDecl and LocationContext. + const VarRegion* getVarRegion(const VarDecl *D, const LocationContext *LC); + + /// getVarRegion - Retrieve or create the memory region associated with + /// a specified VarDecl and super region. + const VarRegion *getVarRegion(const VarDecl *D, const MemRegion *superR); + + /// getElementRegion - Retrieve the memory region associated with the + /// associated element type, index, and super region. + const ElementRegion *getElementRegion(QualType elementType, NonLoc Idx, + const SubRegion *superRegion, + ASTContext &Ctx); + + const ElementRegion *getElementRegionWithSuper(const ElementRegion *ER, + const SubRegion *superRegion) { + return getElementRegion(ER->getElementType(), ER->getIndex(), + superRegion, ER->getContext()); + } + + /// getFieldRegion - Retrieve or create the memory region associated with + /// a specified FieldDecl. 'superRegion' corresponds to the containing + /// memory region (which typically represents the memory representing + /// a structure or class). + const FieldRegion *getFieldRegion(const FieldDecl *fd, + const SubRegion* superRegion); + + const FieldRegion *getFieldRegionWithSuper(const FieldRegion *FR, + const SubRegion *superRegion) { + return getFieldRegion(FR->getDecl(), superRegion); + } + + /// getObjCIvarRegion - Retrieve or create the memory region associated with + /// a specified Objective-c instance variable. 'superRegion' corresponds + /// to the containing region (which typically represents the Objective-C + /// object). + const ObjCIvarRegion *getObjCIvarRegion(const ObjCIvarDecl *ivd, + const SubRegion* superRegion); + + const CXXTempObjectRegion *getCXXTempObjectRegion(Expr const *Ex, + LocationContext const *LC); + + /// Create a CXXBaseObjectRegion with the given base class for region + /// \p Super. + /// + /// The type of \p Super is assumed be a class deriving from \p BaseClass. + const CXXBaseObjectRegion * + getCXXBaseObjectRegion(const CXXRecordDecl *BaseClass, const SubRegion *Super, + bool IsVirtual); + + /// Create a CXXBaseObjectRegion with the same CXXRecordDecl but a different + /// super region. + const CXXBaseObjectRegion * + getCXXBaseObjectRegionWithSuper(const CXXBaseObjectRegion *baseReg, + const SubRegion *superRegion) { + return getCXXBaseObjectRegion(baseReg->getDecl(), superRegion, + baseReg->isVirtual()); + } + + /// Create a CXXDerivedObjectRegion with the given derived class for region + /// \p Super. This should not be used for casting an existing + /// CXXBaseObjectRegion back to the derived type; instead, CXXBaseObjectRegion + /// should be removed. + const CXXDerivedObjectRegion * + getCXXDerivedObjectRegion(const CXXRecordDecl *BaseClass, + const SubRegion *Super); + + const FunctionCodeRegion *getFunctionCodeRegion(const NamedDecl *FD); + const BlockCodeRegion *getBlockCodeRegion(const BlockDecl *BD, + CanQualType locTy, + AnalysisDeclContext *AC); + + /// getBlockDataRegion - Get the memory region associated with an instance + /// of a block. Unlike many other MemRegions, the LocationContext* + /// argument is allowed to be NULL for cases where we have no known + /// context. + const BlockDataRegion *getBlockDataRegion(const BlockCodeRegion *bc, + const LocationContext *lc, + unsigned blockCount); + + /// Create a CXXTempObjectRegion for temporaries which are lifetime-extended + /// by static references. This differs from getCXXTempObjectRegion in the + /// super-region used. + const CXXTempObjectRegion *getCXXStaticTempObjectRegion(const Expr *Ex); + +private: + template <typename RegionTy, typename SuperTy, + typename Arg1Ty> + RegionTy* getSubRegion(const Arg1Ty arg1, + const SuperTy* superRegion); + + template <typename RegionTy, typename SuperTy, + typename Arg1Ty, typename Arg2Ty> + RegionTy* getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2, + const SuperTy* superRegion); + + template <typename RegionTy, typename SuperTy, + typename Arg1Ty, typename Arg2Ty, typename Arg3Ty> + RegionTy* getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2, + const Arg3Ty arg3, + const SuperTy* superRegion); + + template <typename REG> + const REG* LazyAllocate(REG*& region); + + template <typename REG, typename ARG> + const REG* LazyAllocate(REG*& region, ARG a); +}; + +//===----------------------------------------------------------------------===// +// Out-of-line member definitions. +//===----------------------------------------------------------------------===// + +inline ASTContext &MemRegion::getContext() const { + return getMemRegionManager()->getContext(); +} + +//===----------------------------------------------------------------------===// +// Means for storing region/symbol handling traits. +//===----------------------------------------------------------------------===// + +/// Information about invalidation for a particular region/symbol. +class RegionAndSymbolInvalidationTraits { + using StorageTypeForKinds = unsigned char; + + llvm::DenseMap<const MemRegion *, StorageTypeForKinds> MRTraitsMap; + llvm::DenseMap<SymbolRef, StorageTypeForKinds> SymTraitsMap; + + using const_region_iterator = + llvm::DenseMap<const MemRegion *, StorageTypeForKinds>::const_iterator; + using const_symbol_iterator = + llvm::DenseMap<SymbolRef, StorageTypeForKinds>::const_iterator; + +public: + /// Describes different invalidation traits. + enum InvalidationKinds { + /// Tells that a region's contents is not changed. + TK_PreserveContents = 0x1, + + /// Suppress pointer-escaping of a region. + TK_SuppressEscape = 0x2, + + // Do not invalidate super region. + TK_DoNotInvalidateSuperRegion = 0x4, + + /// When applied to a MemSpaceRegion, indicates the entire memory space + /// should be invalidated. + TK_EntireMemSpace = 0x8 + + // Do not forget to extend StorageTypeForKinds if number of traits exceed + // the number of bits StorageTypeForKinds can store. + }; + + void setTrait(SymbolRef Sym, InvalidationKinds IK); + void setTrait(const MemRegion *MR, InvalidationKinds IK); + bool hasTrait(SymbolRef Sym, InvalidationKinds IK) const; + bool hasTrait(const MemRegion *MR, InvalidationKinds IK) const; +}; + +//===----------------------------------------------------------------------===// +// Pretty-printing regions. +//===----------------------------------------------------------------------===// +inline raw_ostream &operator<<(raw_ostream &os, const MemRegion *R) { + R->dumpToStream(os); + return os; +} + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_MEMREGION_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h new file mode 100644 index 00000000..3b1c638f --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -0,0 +1,909 @@ +//== ProgramState.h - Path-sensitive "State" for tracking values -*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the state of the program along the analysisa path. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATE_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATE_H + +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/Support/Allocator.h" +#include <utility> + +namespace llvm { +class APSInt; +} + +namespace clang { +class ASTContext; + +namespace ento { + +class AnalysisManager; +class CallEvent; +class CallEventManager; + +typedef std::unique_ptr<ConstraintManager>(*ConstraintManagerCreator)( + ProgramStateManager &, SubEngine *); +typedef std::unique_ptr<StoreManager>(*StoreManagerCreator)( + ProgramStateManager &); +typedef llvm::ImmutableMap<const SubRegion*, TaintTagType> TaintedSubRegions; + +//===----------------------------------------------------------------------===// +// ProgramStateTrait - Traits used by the Generic Data Map of a ProgramState. +//===----------------------------------------------------------------------===// + +template <typename T> struct ProgramStatePartialTrait; + +template <typename T> struct ProgramStateTrait { + typedef typename T::data_type data_type; + static inline void *MakeVoidPtr(data_type D) { return (void*) D; } + static inline data_type MakeData(void *const* P) { + return P ? (data_type) *P : (data_type) 0; + } +}; + +/// \class ProgramState +/// ProgramState - This class encapsulates: +/// +/// 1. A mapping from expressions to values (Environment) +/// 2. A mapping from locations to values (Store) +/// 3. Constraints on symbolic values (GenericDataMap) +/// +/// Together these represent the "abstract state" of a program. +/// +/// ProgramState is intended to be used as a functional object; that is, +/// once it is created and made "persistent" in a FoldingSet, its +/// values will never change. +class ProgramState : public llvm::FoldingSetNode { +public: + typedef llvm::ImmutableSet<llvm::APSInt*> IntSetTy; + typedef llvm::ImmutableMap<void*, void*> GenericDataMap; + +private: + void operator=(const ProgramState& R) = delete; + + friend class ProgramStateManager; + friend class ExplodedGraph; + friend class ExplodedNode; + + ProgramStateManager *stateMgr; + Environment Env; // Maps a Stmt to its current SVal. + Store store; // Maps a location to its current value. + GenericDataMap GDM; // Custom data stored by a client of this class. + unsigned refCount; + + /// makeWithStore - Return a ProgramState with the same values as the current + /// state with the exception of using the specified Store. + ProgramStateRef makeWithStore(const StoreRef &store) const; + + void setStore(const StoreRef &storeRef); + +public: + /// This ctor is used when creating the first ProgramState object. + ProgramState(ProgramStateManager *mgr, const Environment& env, + StoreRef st, GenericDataMap gdm); + + /// Copy ctor - We must explicitly define this or else the "Next" ptr + /// in FoldingSetNode will also get copied. + ProgramState(const ProgramState &RHS); + + ~ProgramState(); + + int64_t getID() const; + + /// Return the ProgramStateManager associated with this state. + ProgramStateManager &getStateManager() const { + return *stateMgr; + } + + AnalysisManager &getAnalysisManager() const; + + /// Return the ConstraintManager. + ConstraintManager &getConstraintManager() const; + + /// getEnvironment - Return the environment associated with this state. + /// The environment is the mapping from expressions to values. + const Environment& getEnvironment() const { return Env; } + + /// Return the store associated with this state. The store + /// is a mapping from locations to values. + Store getStore() const { return store; } + + + /// getGDM - Return the generic data map associated with this state. + GenericDataMap getGDM() const { return GDM; } + + void setGDM(GenericDataMap gdm) { GDM = gdm; } + + /// Profile - Profile the contents of a ProgramState object for use in a + /// FoldingSet. Two ProgramState objects are considered equal if they + /// have the same Environment, Store, and GenericDataMap. + static void Profile(llvm::FoldingSetNodeID& ID, const ProgramState *V) { + V->Env.Profile(ID); + ID.AddPointer(V->store); + V->GDM.Profile(ID); + } + + /// Profile - Used to profile the contents of this object for inclusion + /// in a FoldingSet. + void Profile(llvm::FoldingSetNodeID& ID) const { + Profile(ID, this); + } + + BasicValueFactory &getBasicVals() const; + SymbolManager &getSymbolManager() const; + + //==---------------------------------------------------------------------==// + // Constraints on values. + //==---------------------------------------------------------------------==// + // + // Each ProgramState records constraints on symbolic values. These constraints + // are managed using the ConstraintManager associated with a ProgramStateManager. + // As constraints gradually accrue on symbolic values, added constraints + // may conflict and indicate that a state is infeasible (as no real values + // could satisfy all the constraints). This is the principal mechanism + // for modeling path-sensitivity in ExprEngine/ProgramState. + // + // Various "assume" methods form the interface for adding constraints to + // symbolic values. A call to 'assume' indicates an assumption being placed + // on one or symbolic values. 'assume' methods take the following inputs: + // + // (1) A ProgramState object representing the current state. + // + // (2) The assumed constraint (which is specific to a given "assume" method). + // + // (3) A binary value "Assumption" that indicates whether the constraint is + // assumed to be true or false. + // + // The output of "assume*" is a new ProgramState object with the added constraints. + // If no new state is feasible, NULL is returned. + // + + /// Assumes that the value of \p cond is zero (if \p assumption is "false") + /// or non-zero (if \p assumption is "true"). + /// + /// This returns a new state with the added constraint on \p cond. + /// If no new state is feasible, NULL is returned. + LLVM_NODISCARD ProgramStateRef assume(DefinedOrUnknownSVal cond, + bool assumption) const; + + /// Assumes both "true" and "false" for \p cond, and returns both + /// corresponding states (respectively). + /// + /// This is more efficient than calling assume() twice. Note that one (but not + /// both) of the returned states may be NULL. + LLVM_NODISCARD std::pair<ProgramStateRef, ProgramStateRef> + assume(DefinedOrUnknownSVal cond) const; + + LLVM_NODISCARD ProgramStateRef + assumeInBound(DefinedOrUnknownSVal idx, DefinedOrUnknownSVal upperBound, + bool assumption, QualType IndexType = QualType()) const; + + /// Assumes that the value of \p Val is bounded with [\p From; \p To] + /// (if \p assumption is "true") or it is fully out of this range + /// (if \p assumption is "false"). + /// + /// This returns a new state with the added constraint on \p cond. + /// If no new state is feasible, NULL is returned. + LLVM_NODISCARD ProgramStateRef assumeInclusiveRange(DefinedOrUnknownSVal Val, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool assumption) const; + + /// Assumes given range both "true" and "false" for \p Val, and returns both + /// corresponding states (respectively). + /// + /// This is more efficient than calling assume() twice. Note that one (but not + /// both) of the returned states may be NULL. + LLVM_NODISCARD std::pair<ProgramStateRef, ProgramStateRef> + assumeInclusiveRange(DefinedOrUnknownSVal Val, const llvm::APSInt &From, + const llvm::APSInt &To) const; + + /// Check if the given SVal is not constrained to zero and is not + /// a zero constant. + ConditionTruthVal isNonNull(SVal V) const; + + /// Check if the given SVal is constrained to zero or is a zero + /// constant. + ConditionTruthVal isNull(SVal V) const; + + /// \return Whether values \p Lhs and \p Rhs are equal. + ConditionTruthVal areEqual(SVal Lhs, SVal Rhs) const; + + /// Utility method for getting regions. + const VarRegion* getRegion(const VarDecl *D, const LocationContext *LC) const; + + //==---------------------------------------------------------------------==// + // Binding and retrieving values to/from the environment and symbolic store. + //==---------------------------------------------------------------------==// + + /// Create a new state by binding the value 'V' to the statement 'S' in the + /// state's environment. + LLVM_NODISCARD ProgramStateRef BindExpr(const Stmt *S, + const LocationContext *LCtx, SVal V, + bool Invalidate = true) const; + + LLVM_NODISCARD ProgramStateRef bindLoc(Loc location, SVal V, + const LocationContext *LCtx, + bool notifyChanges = true) const; + + LLVM_NODISCARD ProgramStateRef bindLoc(SVal location, SVal V, + const LocationContext *LCtx) const; + + /// Initializes the region of memory represented by \p loc with an initial + /// value. Once initialized, all values loaded from any sub-regions of that + /// region will be equal to \p V, unless overwritten later by the program. + /// This method should not be used on regions that are already initialized. + /// If you need to indicate that memory contents have suddenly become unknown + /// within a certain region of memory, consider invalidateRegions(). + LLVM_NODISCARD ProgramStateRef + bindDefaultInitial(SVal loc, SVal V, const LocationContext *LCtx) const; + + /// Performs C++ zero-initialization procedure on the region of memory + /// represented by \p loc. + LLVM_NODISCARD ProgramStateRef + bindDefaultZero(SVal loc, const LocationContext *LCtx) const; + + LLVM_NODISCARD ProgramStateRef killBinding(Loc LV) const; + + /// Returns the state with bindings for the given regions + /// cleared from the store. + /// + /// Optionally invalidates global regions as well. + /// + /// \param Regions the set of regions to be invalidated. + /// \param E the expression that caused the invalidation. + /// \param BlockCount The number of times the current basic block has been + // visited. + /// \param CausesPointerEscape the flag is set to true when + /// the invalidation entails escape of a symbol (representing a + /// pointer). For example, due to it being passed as an argument in a + /// call. + /// \param IS the set of invalidated symbols. + /// \param Call if non-null, the invalidated regions represent parameters to + /// the call and should be considered directly invalidated. + /// \param ITraits information about special handling for a particular + /// region/symbol. + LLVM_NODISCARD ProgramStateRef + invalidateRegions(ArrayRef<const MemRegion *> Regions, const Expr *E, + unsigned BlockCount, const LocationContext *LCtx, + bool CausesPointerEscape, InvalidatedSymbols *IS = nullptr, + const CallEvent *Call = nullptr, + RegionAndSymbolInvalidationTraits *ITraits = nullptr) const; + + LLVM_NODISCARD ProgramStateRef + invalidateRegions(ArrayRef<SVal> Regions, const Expr *E, + unsigned BlockCount, const LocationContext *LCtx, + bool CausesPointerEscape, InvalidatedSymbols *IS = nullptr, + const CallEvent *Call = nullptr, + RegionAndSymbolInvalidationTraits *ITraits = nullptr) const; + + /// enterStackFrame - Returns the state for entry to the given stack frame, + /// preserving the current state. + LLVM_NODISCARD ProgramStateRef enterStackFrame( + const CallEvent &Call, const StackFrameContext *CalleeCtx) const; + + /// Get the lvalue for a base class object reference. + Loc getLValue(const CXXBaseSpecifier &BaseSpec, const SubRegion *Super) const; + + /// Get the lvalue for a base class object reference. + Loc getLValue(const CXXRecordDecl *BaseClass, const SubRegion *Super, + bool IsVirtual) const; + + /// Get the lvalue for a variable reference. + Loc getLValue(const VarDecl *D, const LocationContext *LC) const; + + Loc getLValue(const CompoundLiteralExpr *literal, + const LocationContext *LC) const; + + /// Get the lvalue for an ivar reference. + SVal getLValue(const ObjCIvarDecl *decl, SVal base) const; + + /// Get the lvalue for a field reference. + SVal getLValue(const FieldDecl *decl, SVal Base) const; + + /// Get the lvalue for an indirect field reference. + SVal getLValue(const IndirectFieldDecl *decl, SVal Base) const; + + /// Get the lvalue for an array index. + SVal getLValue(QualType ElementType, SVal Idx, SVal Base) const; + + /// Returns the SVal bound to the statement 'S' in the state's environment. + SVal getSVal(const Stmt *S, const LocationContext *LCtx) const; + + SVal getSValAsScalarOrLoc(const Stmt *Ex, const LocationContext *LCtx) const; + + /// Return the value bound to the specified location. + /// Returns UnknownVal() if none found. + SVal getSVal(Loc LV, QualType T = QualType()) const; + + /// Returns the "raw" SVal bound to LV before any value simplfication. + SVal getRawSVal(Loc LV, QualType T= QualType()) const; + + /// Return the value bound to the specified location. + /// Returns UnknownVal() if none found. + SVal getSVal(const MemRegion* R, QualType T = QualType()) const; + + /// Return the value bound to the specified location, assuming + /// that the value is a scalar integer or an enumeration or a pointer. + /// Returns UnknownVal() if none found or the region is not known to hold + /// a value of such type. + SVal getSValAsScalarOrLoc(const MemRegion *R) const; + + using region_iterator = const MemRegion **; + + /// Visits the symbols reachable from the given SVal using the provided + /// SymbolVisitor. + /// + /// This is a convenience API. Consider using ScanReachableSymbols class + /// directly when making multiple scans on the same state with the same + /// visitor to avoid repeated initialization cost. + /// \sa ScanReachableSymbols + bool scanReachableSymbols(SVal val, SymbolVisitor& visitor) const; + + /// Visits the symbols reachable from the regions in the given + /// MemRegions range using the provided SymbolVisitor. + bool scanReachableSymbols(llvm::iterator_range<region_iterator> Reachable, + SymbolVisitor &visitor) const; + + template <typename CB> CB scanReachableSymbols(SVal val) const; + template <typename CB> CB + scanReachableSymbols(llvm::iterator_range<region_iterator> Reachable) const; + + /// Create a new state in which the statement is marked as tainted. + LLVM_NODISCARD ProgramStateRef + addTaint(const Stmt *S, const LocationContext *LCtx, + TaintTagType Kind = TaintTagGeneric) const; + + /// Create a new state in which the value is marked as tainted. + LLVM_NODISCARD ProgramStateRef + addTaint(SVal V, TaintTagType Kind = TaintTagGeneric) const; + + /// Create a new state in which the symbol is marked as tainted. + LLVM_NODISCARD ProgramStateRef addTaint(SymbolRef S, + TaintTagType Kind = TaintTagGeneric) const; + + /// Create a new state in which the region symbol is marked as tainted. + LLVM_NODISCARD ProgramStateRef + addTaint(const MemRegion *R, TaintTagType Kind = TaintTagGeneric) const; + + /// Create a new state in a which a sub-region of a given symbol is tainted. + /// This might be necessary when referring to regions that can not have an + /// individual symbol, e.g. if they are represented by the default binding of + /// a LazyCompoundVal. + LLVM_NODISCARD ProgramStateRef + addPartialTaint(SymbolRef ParentSym, const SubRegion *SubRegion, + TaintTagType Kind = TaintTagGeneric) const; + + /// Check if the statement is tainted in the current state. + bool isTainted(const Stmt *S, const LocationContext *LCtx, + TaintTagType Kind = TaintTagGeneric) const; + bool isTainted(SVal V, TaintTagType Kind = TaintTagGeneric) const; + bool isTainted(SymbolRef Sym, TaintTagType Kind = TaintTagGeneric) const; + bool isTainted(const MemRegion *Reg, TaintTagType Kind=TaintTagGeneric) const; + + //==---------------------------------------------------------------------==// + // Accessing the Generic Data Map (GDM). + //==---------------------------------------------------------------------==// + + void *const* FindGDM(void *K) const; + + template <typename T> + LLVM_NODISCARD ProgramStateRef + add(typename ProgramStateTrait<T>::key_type K) const; + + template <typename T> + typename ProgramStateTrait<T>::data_type + get() const { + return ProgramStateTrait<T>::MakeData(FindGDM(ProgramStateTrait<T>::GDMIndex())); + } + + template<typename T> + typename ProgramStateTrait<T>::lookup_type + get(typename ProgramStateTrait<T>::key_type key) const { + void *const* d = FindGDM(ProgramStateTrait<T>::GDMIndex()); + return ProgramStateTrait<T>::Lookup(ProgramStateTrait<T>::MakeData(d), key); + } + + template <typename T> + typename ProgramStateTrait<T>::context_type get_context() const; + + template <typename T> + LLVM_NODISCARD ProgramStateRef + remove(typename ProgramStateTrait<T>::key_type K) const; + + template <typename T> + LLVM_NODISCARD ProgramStateRef + remove(typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::context_type C) const; + + template <typename T> LLVM_NODISCARD ProgramStateRef remove() const; + + template <typename T> + LLVM_NODISCARD ProgramStateRef + set(typename ProgramStateTrait<T>::data_type D) const; + + template <typename T> + LLVM_NODISCARD ProgramStateRef + set(typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::value_type E) const; + + template <typename T> + LLVM_NODISCARD ProgramStateRef + set(typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::value_type E, + typename ProgramStateTrait<T>::context_type C) const; + + template<typename T> + bool contains(typename ProgramStateTrait<T>::key_type key) const { + void *const* d = FindGDM(ProgramStateTrait<T>::GDMIndex()); + return ProgramStateTrait<T>::Contains(ProgramStateTrait<T>::MakeData(d), key); + } + + // Pretty-printing. + void print(raw_ostream &Out, const char *nl = "\n", const char *sep = "", + const LocationContext *CurrentLC = nullptr) const; + void printDOT(raw_ostream &Out, + const LocationContext *CurrentLC = nullptr) const; + void printTaint(raw_ostream &Out, const char *nl = "\n") const; + + void dump() const; + void dumpTaint() const; + +private: + friend void ProgramStateRetain(const ProgramState *state); + friend void ProgramStateRelease(const ProgramState *state); + + /// \sa invalidateValues() + /// \sa invalidateRegions() + ProgramStateRef + invalidateRegionsImpl(ArrayRef<SVal> Values, + const Expr *E, unsigned BlockCount, + const LocationContext *LCtx, + bool ResultsInSymbolEscape, + InvalidatedSymbols *IS, + RegionAndSymbolInvalidationTraits *HTraits, + const CallEvent *Call) const; +}; + +//===----------------------------------------------------------------------===// +// ProgramStateManager - Factory object for ProgramStates. +//===----------------------------------------------------------------------===// + +class ProgramStateManager { + friend class ProgramState; + friend void ProgramStateRelease(const ProgramState *state); +private: + /// Eng - The SubEngine that owns this state manager. + SubEngine *Eng; /* Can be null. */ + + EnvironmentManager EnvMgr; + std::unique_ptr<StoreManager> StoreMgr; + std::unique_ptr<ConstraintManager> ConstraintMgr; + + ProgramState::GenericDataMap::Factory GDMFactory; + TaintedSubRegions::Factory TSRFactory; + + typedef llvm::DenseMap<void*,std::pair<void*,void (*)(void*)> > GDMContextsTy; + GDMContextsTy GDMContexts; + + /// StateSet - FoldingSet containing all the states created for analyzing + /// a particular function. This is used to unique states. + llvm::FoldingSet<ProgramState> StateSet; + + /// Object that manages the data for all created SVals. + std::unique_ptr<SValBuilder> svalBuilder; + + /// Manages memory for created CallEvents. + std::unique_ptr<CallEventManager> CallEventMgr; + + /// A BumpPtrAllocator to allocate states. + llvm::BumpPtrAllocator &Alloc; + + /// A vector of ProgramStates that we can reuse. + std::vector<ProgramState *> freeStates; + +public: + ProgramStateManager(ASTContext &Ctx, + StoreManagerCreator CreateStoreManager, + ConstraintManagerCreator CreateConstraintManager, + llvm::BumpPtrAllocator& alloc, + SubEngine *subeng); + + ~ProgramStateManager(); + + ProgramStateRef getInitialState(const LocationContext *InitLoc); + + ASTContext &getContext() { return svalBuilder->getContext(); } + const ASTContext &getContext() const { return svalBuilder->getContext(); } + + BasicValueFactory &getBasicVals() { + return svalBuilder->getBasicValueFactory(); + } + + SValBuilder &getSValBuilder() { + return *svalBuilder; + } + + SymbolManager &getSymbolManager() { + return svalBuilder->getSymbolManager(); + } + const SymbolManager &getSymbolManager() const { + return svalBuilder->getSymbolManager(); + } + + llvm::BumpPtrAllocator& getAllocator() { return Alloc; } + + MemRegionManager& getRegionManager() { + return svalBuilder->getRegionManager(); + } + const MemRegionManager &getRegionManager() const { + return svalBuilder->getRegionManager(); + } + + CallEventManager &getCallEventManager() { return *CallEventMgr; } + + StoreManager &getStoreManager() { return *StoreMgr; } + ConstraintManager &getConstraintManager() { return *ConstraintMgr; } + SubEngine &getOwningEngine() { return *Eng; } + + ProgramStateRef removeDeadBindings(ProgramStateRef St, + const StackFrameContext *LCtx, + SymbolReaper& SymReaper); + +public: + + SVal ArrayToPointer(Loc Array, QualType ElementTy) { + return StoreMgr->ArrayToPointer(Array, ElementTy); + } + + // Methods that manipulate the GDM. + ProgramStateRef addGDM(ProgramStateRef St, void *Key, void *Data); + ProgramStateRef removeGDM(ProgramStateRef state, void *Key); + + // Methods that query & manipulate the Store. + + void iterBindings(ProgramStateRef state, StoreManager::BindingsHandler& F) { + StoreMgr->iterBindings(state->getStore(), F); + } + + ProgramStateRef getPersistentState(ProgramState &Impl); + ProgramStateRef getPersistentStateWithGDM(ProgramStateRef FromState, + ProgramStateRef GDMState); + + bool haveEqualEnvironments(ProgramStateRef S1, ProgramStateRef S2) { + return S1->Env == S2->Env; + } + + bool haveEqualStores(ProgramStateRef S1, ProgramStateRef S2) { + return S1->store == S2->store; + } + + //==---------------------------------------------------------------------==// + // Generic Data Map methods. + //==---------------------------------------------------------------------==// + // + // ProgramStateManager and ProgramState support a "generic data map" that allows + // different clients of ProgramState objects to embed arbitrary data within a + // ProgramState object. The generic data map is essentially an immutable map + // from a "tag" (that acts as the "key" for a client) and opaque values. + // Tags/keys and values are simply void* values. The typical way that clients + // generate unique tags are by taking the address of a static variable. + // Clients are responsible for ensuring that data values referred to by a + // the data pointer are immutable (and thus are essentially purely functional + // data). + // + // The templated methods below use the ProgramStateTrait<T> class + // to resolve keys into the GDM and to return data values to clients. + // + + // Trait based GDM dispatch. + template <typename T> + ProgramStateRef set(ProgramStateRef st, typename ProgramStateTrait<T>::data_type D) { + return addGDM(st, ProgramStateTrait<T>::GDMIndex(), + ProgramStateTrait<T>::MakeVoidPtr(D)); + } + + template<typename T> + ProgramStateRef set(ProgramStateRef st, + typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::value_type V, + typename ProgramStateTrait<T>::context_type C) { + + return addGDM(st, ProgramStateTrait<T>::GDMIndex(), + ProgramStateTrait<T>::MakeVoidPtr(ProgramStateTrait<T>::Set(st->get<T>(), K, V, C))); + } + + template <typename T> + ProgramStateRef add(ProgramStateRef st, + typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::context_type C) { + return addGDM(st, ProgramStateTrait<T>::GDMIndex(), + ProgramStateTrait<T>::MakeVoidPtr(ProgramStateTrait<T>::Add(st->get<T>(), K, C))); + } + + template <typename T> + ProgramStateRef remove(ProgramStateRef st, + typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::context_type C) { + + return addGDM(st, ProgramStateTrait<T>::GDMIndex(), + ProgramStateTrait<T>::MakeVoidPtr(ProgramStateTrait<T>::Remove(st->get<T>(), K, C))); + } + + template <typename T> + ProgramStateRef remove(ProgramStateRef st) { + return removeGDM(st, ProgramStateTrait<T>::GDMIndex()); + } + + void *FindGDMContext(void *index, + void *(*CreateContext)(llvm::BumpPtrAllocator&), + void (*DeleteContext)(void*)); + + template <typename T> + typename ProgramStateTrait<T>::context_type get_context() { + void *p = FindGDMContext(ProgramStateTrait<T>::GDMIndex(), + ProgramStateTrait<T>::CreateContext, + ProgramStateTrait<T>::DeleteContext); + + return ProgramStateTrait<T>::MakeContext(p); + } + + void EndPath(ProgramStateRef St) { + ConstraintMgr->EndPath(St); + } +}; + + +//===----------------------------------------------------------------------===// +// Out-of-line method definitions for ProgramState. +//===----------------------------------------------------------------------===// + +inline ConstraintManager &ProgramState::getConstraintManager() const { + return stateMgr->getConstraintManager(); +} + +inline const VarRegion* ProgramState::getRegion(const VarDecl *D, + const LocationContext *LC) const +{ + return getStateManager().getRegionManager().getVarRegion(D, LC); +} + +inline ProgramStateRef ProgramState::assume(DefinedOrUnknownSVal Cond, + bool Assumption) const { + if (Cond.isUnknown()) + return this; + + return getStateManager().ConstraintMgr + ->assume(this, Cond.castAs<DefinedSVal>(), Assumption); +} + +inline std::pair<ProgramStateRef , ProgramStateRef > +ProgramState::assume(DefinedOrUnknownSVal Cond) const { + if (Cond.isUnknown()) + return std::make_pair(this, this); + + return getStateManager().ConstraintMgr + ->assumeDual(this, Cond.castAs<DefinedSVal>()); +} + +inline ProgramStateRef ProgramState::assumeInclusiveRange( + DefinedOrUnknownSVal Val, const llvm::APSInt &From, const llvm::APSInt &To, + bool Assumption) const { + if (Val.isUnknown()) + return this; + + assert(Val.getAs<NonLoc>() && "Only NonLocs are supported!"); + + return getStateManager().ConstraintMgr->assumeInclusiveRange( + this, Val.castAs<NonLoc>(), From, To, Assumption); +} + +inline std::pair<ProgramStateRef, ProgramStateRef> +ProgramState::assumeInclusiveRange(DefinedOrUnknownSVal Val, + const llvm::APSInt &From, + const llvm::APSInt &To) const { + if (Val.isUnknown()) + return std::make_pair(this, this); + + assert(Val.getAs<NonLoc>() && "Only NonLocs are supported!"); + + return getStateManager().ConstraintMgr->assumeInclusiveRangeDual( + this, Val.castAs<NonLoc>(), From, To); +} + +inline ProgramStateRef ProgramState::bindLoc(SVal LV, SVal V, const LocationContext *LCtx) const { + if (Optional<Loc> L = LV.getAs<Loc>()) + return bindLoc(*L, V, LCtx); + return this; +} + +inline Loc ProgramState::getLValue(const CXXBaseSpecifier &BaseSpec, + const SubRegion *Super) const { + const auto *Base = BaseSpec.getType()->getAsCXXRecordDecl(); + return loc::MemRegionVal( + getStateManager().getRegionManager().getCXXBaseObjectRegion( + Base, Super, BaseSpec.isVirtual())); +} + +inline Loc ProgramState::getLValue(const CXXRecordDecl *BaseClass, + const SubRegion *Super, + bool IsVirtual) const { + return loc::MemRegionVal( + getStateManager().getRegionManager().getCXXBaseObjectRegion( + BaseClass, Super, IsVirtual)); +} + +inline Loc ProgramState::getLValue(const VarDecl *VD, + const LocationContext *LC) const { + return getStateManager().StoreMgr->getLValueVar(VD, LC); +} + +inline Loc ProgramState::getLValue(const CompoundLiteralExpr *literal, + const LocationContext *LC) const { + return getStateManager().StoreMgr->getLValueCompoundLiteral(literal, LC); +} + +inline SVal ProgramState::getLValue(const ObjCIvarDecl *D, SVal Base) const { + return getStateManager().StoreMgr->getLValueIvar(D, Base); +} + +inline SVal ProgramState::getLValue(const FieldDecl *D, SVal Base) const { + return getStateManager().StoreMgr->getLValueField(D, Base); +} + +inline SVal ProgramState::getLValue(const IndirectFieldDecl *D, + SVal Base) const { + StoreManager &SM = *getStateManager().StoreMgr; + for (const auto *I : D->chain()) { + Base = SM.getLValueField(cast<FieldDecl>(I), Base); + } + + return Base; +} + +inline SVal ProgramState::getLValue(QualType ElementType, SVal Idx, SVal Base) const{ + if (Optional<NonLoc> N = Idx.getAs<NonLoc>()) + return getStateManager().StoreMgr->getLValueElement(ElementType, *N, Base); + return UnknownVal(); +} + +inline SVal ProgramState::getSVal(const Stmt *Ex, + const LocationContext *LCtx) const{ + return Env.getSVal(EnvironmentEntry(Ex, LCtx), + *getStateManager().svalBuilder); +} + +inline SVal +ProgramState::getSValAsScalarOrLoc(const Stmt *S, + const LocationContext *LCtx) const { + if (const Expr *Ex = dyn_cast<Expr>(S)) { + QualType T = Ex->getType(); + if (Ex->isGLValue() || Loc::isLocType(T) || + T->isIntegralOrEnumerationType()) + return getSVal(S, LCtx); + } + + return UnknownVal(); +} + +inline SVal ProgramState::getRawSVal(Loc LV, QualType T) const { + return getStateManager().StoreMgr->getBinding(getStore(), LV, T); +} + +inline SVal ProgramState::getSVal(const MemRegion* R, QualType T) const { + return getStateManager().StoreMgr->getBinding(getStore(), + loc::MemRegionVal(R), + T); +} + +inline BasicValueFactory &ProgramState::getBasicVals() const { + return getStateManager().getBasicVals(); +} + +inline SymbolManager &ProgramState::getSymbolManager() const { + return getStateManager().getSymbolManager(); +} + +template<typename T> +ProgramStateRef ProgramState::add(typename ProgramStateTrait<T>::key_type K) const { + return getStateManager().add<T>(this, K, get_context<T>()); +} + +template <typename T> +typename ProgramStateTrait<T>::context_type ProgramState::get_context() const { + return getStateManager().get_context<T>(); +} + +template<typename T> +ProgramStateRef ProgramState::remove(typename ProgramStateTrait<T>::key_type K) const { + return getStateManager().remove<T>(this, K, get_context<T>()); +} + +template<typename T> +ProgramStateRef ProgramState::remove(typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::context_type C) const { + return getStateManager().remove<T>(this, K, C); +} + +template <typename T> +ProgramStateRef ProgramState::remove() const { + return getStateManager().remove<T>(this); +} + +template<typename T> +ProgramStateRef ProgramState::set(typename ProgramStateTrait<T>::data_type D) const { + return getStateManager().set<T>(this, D); +} + +template<typename T> +ProgramStateRef ProgramState::set(typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::value_type E) const { + return getStateManager().set<T>(this, K, E, get_context<T>()); +} + +template<typename T> +ProgramStateRef ProgramState::set(typename ProgramStateTrait<T>::key_type K, + typename ProgramStateTrait<T>::value_type E, + typename ProgramStateTrait<T>::context_type C) const { + return getStateManager().set<T>(this, K, E, C); +} + +template <typename CB> +CB ProgramState::scanReachableSymbols(SVal val) const { + CB cb(this); + scanReachableSymbols(val, cb); + return cb; +} + +template <typename CB> +CB ProgramState::scanReachableSymbols( + llvm::iterator_range<region_iterator> Reachable) const { + CB cb(this); + scanReachableSymbols(Reachable, cb); + return cb; +} + +/// \class ScanReachableSymbols +/// A utility class that visits the reachable symbols using a custom +/// SymbolVisitor. Terminates recursive traversal when the visitor function +/// returns false. +class ScanReachableSymbols { + typedef llvm::DenseSet<const void*> VisitedItems; + + VisitedItems visited; + ProgramStateRef state; + SymbolVisitor &visitor; +public: + ScanReachableSymbols(ProgramStateRef st, SymbolVisitor &v) + : state(std::move(st)), visitor(v) {} + + bool scan(nonloc::LazyCompoundVal val); + bool scan(nonloc::CompoundVal val); + bool scan(SVal val); + bool scan(const MemRegion *R); + bool scan(const SymExpr *sym); +}; + +} // end ento namespace + +} // end clang namespace + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h new file mode 100644 index 00000000..da82a55e --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h @@ -0,0 +1,328 @@ +//ProgramStateTrait.h - Partial implementations of ProgramStateTrait -*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines partial implementations of template specializations of +// the class ProgramStateTrait<>. ProgramStateTrait<> is used by ProgramState +// to implement set/get methods for manipulating a ProgramState's +// generic data map. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATETRAIT_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATETRAIT_H + +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/Support/Allocator.h" +#include <cstdint> + +namespace clang { +namespace ento { + + template <typename T> struct ProgramStatePartialTrait; + + /// Declares a program state trait for type \p Type called \p Name, and + /// introduce a type named \c NameTy. + /// The macro should not be used inside namespaces. + #define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type) \ + namespace { \ + class Name {}; \ + using Name ## Ty = Type; \ + } \ + namespace clang { \ + namespace ento { \ + template <> \ + struct ProgramStateTrait<Name> \ + : public ProgramStatePartialTrait<Name ## Ty> { \ + static void *GDMIndex() { static int Index; return &Index; } \ + }; \ + } \ + } + + /// Declares a factory for objects of type \p Type in the program state + /// manager. The type must provide a ::Factory sub-class. Commonly used for + /// ImmutableMap, ImmutableSet, ImmutableList. The macro should not be used + /// inside namespaces. + #define REGISTER_FACTORY_WITH_PROGRAMSTATE(Type) \ + namespace clang { \ + namespace ento { \ + template <> \ + struct ProgramStateTrait<Type> \ + : public ProgramStatePartialTrait<Type> { \ + static void *GDMIndex() { static int Index; return &Index; } \ + }; \ + } \ + } + + /// Helper for registering a map trait. + /// + /// If the map type were written directly in the invocation of + /// REGISTER_TRAIT_WITH_PROGRAMSTATE, the comma in the template arguments + /// would be treated as a macro argument separator, which is wrong. + /// This allows the user to specify a map type in a way that the preprocessor + /// can deal with. + #define CLANG_ENTO_PROGRAMSTATE_MAP(Key, Value) llvm::ImmutableMap<Key, Value> + + /// Declares an immutable map of type \p NameTy, suitable for placement into + /// the ProgramState. This is implementing using llvm::ImmutableMap. + /// + /// \code + /// State = State->set<Name>(K, V); + /// const Value *V = State->get<Name>(K); // Returns NULL if not in the map. + /// State = State->remove<Name>(K); + /// NameTy Map = State->get<Name>(); + /// \endcode + /// + /// The macro should not be used inside namespaces, or for traits that must + /// be accessible from more than one translation unit. + #define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value) \ + REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, \ + CLANG_ENTO_PROGRAMSTATE_MAP(Key, Value)) + + /// Declares an immutable map type \p Name and registers the factory + /// for such maps in the program state, but does not add the map itself + /// to the program state. Useful for managing lifetime of maps that are used + /// as elements of other program state data structures. + #define REGISTER_MAP_FACTORY_WITH_PROGRAMSTATE(Name, Key, Value) \ + using Name = llvm::ImmutableMap<Key, Value>; \ + REGISTER_FACTORY_WITH_PROGRAMSTATE(Name) + + + /// Declares an immutable set of type \p NameTy, suitable for placement into + /// the ProgramState. This is implementing using llvm::ImmutableSet. + /// + /// \code + /// State = State->add<Name>(E); + /// State = State->remove<Name>(E); + /// bool Present = State->contains<Name>(E); + /// NameTy Set = State->get<Name>(); + /// \endcode + /// + /// The macro should not be used inside namespaces, or for traits that must + /// be accessible from more than one translation unit. + #define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem) \ + REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, llvm::ImmutableSet<Elem>) + + /// Declares an immutable set type \p Name and registers the factory + /// for such sets in the program state, but does not add the set itself + /// to the program state. Useful for managing lifetime of sets that are used + /// as elements of other program state data structures. + #define REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(Name, Elem) \ + using Name = llvm::ImmutableSet<Elem>; \ + REGISTER_FACTORY_WITH_PROGRAMSTATE(Name) + + + /// Declares an immutable list type \p NameTy, suitable for placement into + /// the ProgramState. This is implementing using llvm::ImmutableList. + /// + /// \code + /// State = State->add<Name>(E); // Adds to the /end/ of the list. + /// bool Present = State->contains<Name>(E); + /// NameTy List = State->get<Name>(); + /// \endcode + /// + /// The macro should not be used inside namespaces, or for traits that must + /// be accessible from more than one translation unit. + #define REGISTER_LIST_WITH_PROGRAMSTATE(Name, Elem) \ + REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, llvm::ImmutableList<Elem>) + + /// Declares an immutable list of type \p Name and registers the factory + /// for such lists in the program state, but does not add the list itself + /// to the program state. Useful for managing lifetime of lists that are used + /// as elements of other program state data structures. + #define REGISTER_LIST_FACTORY_WITH_PROGRAMSTATE(Name, Elem) \ + using Name = llvm::ImmutableList<Elem>; \ + REGISTER_FACTORY_WITH_PROGRAMSTATE(Name) + + + // Partial-specialization for ImmutableMap. + template <typename Key, typename Data, typename Info> + struct ProgramStatePartialTrait<llvm::ImmutableMap<Key, Data, Info>> { + using data_type = llvm::ImmutableMap<Key, Data, Info>; + using context_type = typename data_type::Factory &; + using key_type = Key; + using value_type = Data; + using lookup_type = const value_type *; + + static data_type MakeData(void *const *p) { + return p ? data_type((typename data_type::TreeTy *) *p) + : data_type(nullptr); + } + + static void *MakeVoidPtr(data_type B) { + return B.getRoot(); + } + + static lookup_type Lookup(data_type B, key_type K) { + return B.lookup(K); + } + + static data_type Set(data_type B, key_type K, value_type E, + context_type F) { + return F.add(B, K, E); + } + + static data_type Remove(data_type B, key_type K, context_type F) { + return F.remove(B, K); + } + + static bool Contains(data_type B, key_type K) { + return B.contains(K); + } + + static context_type MakeContext(void *p) { + return *((typename data_type::Factory *) p); + } + + static void *CreateContext(llvm::BumpPtrAllocator& Alloc) { + return new typename data_type::Factory(Alloc); + } + + static void DeleteContext(void *Ctx) { + delete (typename data_type::Factory *) Ctx; + } + }; + + // Partial-specialization for ImmutableSet. + template <typename Key, typename Info> + struct ProgramStatePartialTrait<llvm::ImmutableSet<Key, Info>> { + using data_type = llvm::ImmutableSet<Key, Info>; + using context_type = typename data_type::Factory &; + using key_type = Key; + + static data_type MakeData(void *const *p) { + return p ? data_type((typename data_type::TreeTy *) *p) + : data_type(nullptr); + } + + static void *MakeVoidPtr(data_type B) { + return B.getRoot(); + } + + static data_type Add(data_type B, key_type K, context_type F) { + return F.add(B, K); + } + + static data_type Remove(data_type B, key_type K, context_type F) { + return F.remove(B, K); + } + + static bool Contains(data_type B, key_type K) { + return B.contains(K); + } + + static context_type MakeContext(void *p) { + return *((typename data_type::Factory *) p); + } + + static void *CreateContext(llvm::BumpPtrAllocator &Alloc) { + return new typename data_type::Factory(Alloc); + } + + static void DeleteContext(void *Ctx) { + delete (typename data_type::Factory *) Ctx; + } + }; + + // Partial-specialization for ImmutableList. + template <typename T> + struct ProgramStatePartialTrait<llvm::ImmutableList<T>> { + using data_type = llvm::ImmutableList<T>; + using key_type = T; + using context_type = typename data_type::Factory &; + + static data_type Add(data_type L, key_type K, context_type F) { + return F.add(K, L); + } + + static bool Contains(data_type L, key_type K) { + return L.contains(K); + } + + static data_type MakeData(void *const *p) { + return p ? data_type((const llvm::ImmutableListImpl<T> *) *p) + : data_type(nullptr); + } + + static void *MakeVoidPtr(data_type D) { + return const_cast<llvm::ImmutableListImpl<T> *>(D.getInternalPointer()); + } + + static context_type MakeContext(void *p) { + return *((typename data_type::Factory *) p); + } + + static void *CreateContext(llvm::BumpPtrAllocator &Alloc) { + return new typename data_type::Factory(Alloc); + } + + static void DeleteContext(void *Ctx) { + delete (typename data_type::Factory *) Ctx; + } + }; + + // Partial specialization for bool. + template <> struct ProgramStatePartialTrait<bool> { + using data_type = bool; + + static data_type MakeData(void *const *p) { + return p ? (data_type) (uintptr_t) *p + : data_type(); + } + + static void *MakeVoidPtr(data_type d) { + return (void *) (uintptr_t) d; + } + }; + + // Partial specialization for unsigned. + template <> struct ProgramStatePartialTrait<unsigned> { + using data_type = unsigned; + + static data_type MakeData(void *const *p) { + return p ? (data_type) (uintptr_t) *p + : data_type(); + } + + static void *MakeVoidPtr(data_type d) { + return (void *) (uintptr_t) d; + } + }; + + // Partial specialization for void*. + template <> struct ProgramStatePartialTrait<void *> { + using data_type = void *; + + static data_type MakeData(void *const *p) { + return p ? *p + : data_type(); + } + + static void *MakeVoidPtr(data_type d) { + return d; + } + }; + + // Partial specialization for const void *. + template <> struct ProgramStatePartialTrait<const void *> { + using data_type = const void *; + + static data_type MakeData(void *const *p) { + return p ? *p : data_type(); + } + + static void *MakeVoidPtr(data_type d) { + return const_cast<void *>(d); + } + }; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATETRAIT_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h new file mode 100644 index 00000000..0ea26bf2 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h @@ -0,0 +1,42 @@ +//== ProgramState_Fwd.h - Incomplete declarations of ProgramState -*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATE_FWD_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATE_FWD_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" + +namespace clang { +namespace ento { + class ProgramState; + class ProgramStateManager; + void ProgramStateRetain(const ProgramState *state); + void ProgramStateRelease(const ProgramState *state); +} +} + +namespace llvm { + template <> struct IntrusiveRefCntPtrInfo<const clang::ento::ProgramState> { + static void retain(const clang::ento::ProgramState *state) { + clang::ento::ProgramStateRetain(state); + } + static void release(const clang::ento::ProgramState *state) { + clang::ento::ProgramStateRelease(state); + } + }; +} + +namespace clang { +namespace ento { + typedef IntrusiveRefCntPtr<const ProgramState> ProgramStateRef; +} +} + +#endif + diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h new file mode 100644 index 00000000..16c30ec1 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -0,0 +1,215 @@ +//== RangedConstraintManager.h ----------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Ranged constraint manager, built on SimpleConstraintManager. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h" + +namespace clang { + +namespace ento { + +/// A Range represents the closed range [from, to]. The caller must +/// guarantee that from <= to. Note that Range is immutable, so as not +/// to subvert RangeSet's immutability. +class Range : public std::pair<const llvm::APSInt *, const llvm::APSInt *> { +public: + Range(const llvm::APSInt &from, const llvm::APSInt &to) + : std::pair<const llvm::APSInt *, const llvm::APSInt *>(&from, &to) { + assert(from <= to); + } + bool Includes(const llvm::APSInt &v) const { + return *first <= v && v <= *second; + } + const llvm::APSInt &From() const { return *first; } + const llvm::APSInt &To() const { return *second; } + const llvm::APSInt *getConcreteValue() const { + return &From() == &To() ? &From() : nullptr; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(&From()); + ID.AddPointer(&To()); + } +}; + +class RangeTrait : public llvm::ImutContainerInfo<Range> { +public: + // When comparing if one Range is less than another, we should compare + // the actual APSInt values instead of their pointers. This keeps the order + // consistent (instead of comparing by pointer values) and can potentially + // be used to speed up some of the operations in RangeSet. + static inline bool isLess(key_type_ref lhs, key_type_ref rhs) { + return *lhs.first < *rhs.first || + (!(*rhs.first < *lhs.first) && *lhs.second < *rhs.second); + } +}; + +/// RangeSet contains a set of ranges. If the set is empty, then +/// there the value of a symbol is overly constrained and there are no +/// possible values for that symbol. +class RangeSet { + typedef llvm::ImmutableSet<Range, RangeTrait> PrimRangeSet; + PrimRangeSet ranges; // no need to make const, since it is an + // ImmutableSet - this allows default operator= + // to work. +public: + typedef PrimRangeSet::Factory Factory; + typedef PrimRangeSet::iterator iterator; + + RangeSet(PrimRangeSet RS) : ranges(RS) {} + + /// Create a new set with all ranges of this set and RS. + /// Possible intersections are not checked here. + RangeSet addRange(Factory &F, const RangeSet &RS) { + PrimRangeSet Ranges(RS.ranges); + for (const auto &range : ranges) + Ranges = F.add(Ranges, range); + return RangeSet(Ranges); + } + + iterator begin() const { return ranges.begin(); } + iterator end() const { return ranges.end(); } + + bool isEmpty() const { return ranges.isEmpty(); } + + /// Construct a new RangeSet representing '{ [from, to] }'. + RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to) + : ranges(F.add(F.getEmptySet(), Range(from, to))) {} + + /// Profile - Generates a hash profile of this RangeSet for use + /// by FoldingSet. + void Profile(llvm::FoldingSetNodeID &ID) const { ranges.Profile(ID); } + + /// getConcreteValue - If a symbol is contrained to equal a specific integer + /// constant then this method returns that value. Otherwise, it returns + /// NULL. + const llvm::APSInt *getConcreteValue() const { + return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : nullptr; + } + +private: + void IntersectInRange(BasicValueFactory &BV, Factory &F, + const llvm::APSInt &Lower, const llvm::APSInt &Upper, + PrimRangeSet &newRanges, PrimRangeSet::iterator &i, + PrimRangeSet::iterator &e) const; + + const llvm::APSInt &getMinValue() const; + + bool pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const; + +public: + RangeSet Intersect(BasicValueFactory &BV, Factory &F, llvm::APSInt Lower, + llvm::APSInt Upper) const; + + RangeSet Negate(BasicValueFactory &BV, Factory &F) const; + + void print(raw_ostream &os) const; + + bool operator==(const RangeSet &other) const { + return ranges == other.ranges; + } +}; + + +class ConstraintRange {}; +using ConstraintRangeTy = llvm::ImmutableMap<SymbolRef, RangeSet>; + +template <> +struct ProgramStateTrait<ConstraintRange> + : public ProgramStatePartialTrait<ConstraintRangeTy> { + static void *GDMIndex(); +}; + + +class RangedConstraintManager : public SimpleConstraintManager { +public: + RangedConstraintManager(SubEngine *SE, SValBuilder &SB) + : SimpleConstraintManager(SE, SB) {} + + ~RangedConstraintManager() override; + + //===------------------------------------------------------------------===// + // Implementation for interface from SimpleConstraintManager. + //===------------------------------------------------------------------===// + + ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym, + bool Assumption) override; + + ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange) override; + + ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym, + bool Assumption) override; + +protected: + /// Assume a constraint between a symbolic expression and a concrete integer. + virtual ProgramStateRef assumeSymRel(ProgramStateRef State, SymbolRef Sym, + BinaryOperator::Opcode op, + const llvm::APSInt &Int); + + //===------------------------------------------------------------------===// + // Interface that subclasses must implement. + //===------------------------------------------------------------------===// + + // Each of these is of the form "$Sym+Adj <> V", where "<>" is the comparison + // operation for the method being invoked. + + virtual ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymWithinInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymOutsideInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; + + //===------------------------------------------------------------------===// + // Internal implementation. + //===------------------------------------------------------------------===// +private: + static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment); +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def new file mode 100644 index 00000000..3c52c2bc --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def @@ -0,0 +1,89 @@ +//===-- Regions.def - Metadata about MemRegion kinds ------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// The list of regions (MemRegion sub-classes) used in the Static Analyzer. +// In order to use this information, users of this file must define one or more +// of the three macros: +// +// REGION(Id, Parent) - for specific MemRegion sub-classes, reserving +// enum value IdKind for their kind. +// +// ABSTRACT_REGION(Id, Parent) - for abstract region classes, +// +// REGION_RANGE(Id, First, Last) - for ranges of kind-enums, +// allowing to determine abstract class of a region +// based on the kind-enum value. +// +//===----------------------------------------------------------------------===// + +#ifndef REGION +#define REGION(Id, Parent) +#endif + +#ifndef ABSTRACT_REGION +#define ABSTRACT_REGION(Id, Parent) +#endif + +#ifndef REGION_RANGE +#define REGION_RANGE(Id, First, Last) +#endif + +ABSTRACT_REGION(MemSpaceRegion, MemRegion) + REGION(CodeSpaceRegion, MemSpaceRegion) + ABSTRACT_REGION(GlobalsSpaceRegion, MemSpaceRegion) + ABSTRACT_REGION(NonStaticGlobalSpaceRegion, GlobalsSpaceRegion) + REGION(GlobalImmutableSpaceRegion, NonStaticGlobalSpaceRegion) + REGION(GlobalInternalSpaceRegion, NonStaticGlobalSpaceRegion) + REGION(GlobalSystemSpaceRegion, NonStaticGlobalSpaceRegion) + REGION_RANGE(NON_STATIC_GLOBAL_MEMSPACES, GlobalImmutableSpaceRegionKind, + GlobalSystemSpaceRegionKind) + REGION(StaticGlobalSpaceRegion, MemSpaceRegion) + REGION_RANGE(GLOBAL_MEMSPACES, GlobalImmutableSpaceRegionKind, + StaticGlobalSpaceRegionKind) + REGION(HeapSpaceRegion, MemSpaceRegion) + ABSTRACT_REGION(StackSpaceRegion, MemSpaceRegion) + REGION(StackArgumentsSpaceRegion, StackSpaceRegion) + REGION(StackLocalsSpaceRegion, StackSpaceRegion) + REGION_RANGE(STACK_MEMSPACES, StackArgumentsSpaceRegionKind, + StackLocalsSpaceRegionKind) + REGION(UnknownSpaceRegion, MemSpaceRegion) + REGION_RANGE(MEMSPACES, CodeSpaceRegionKind, + UnknownSpaceRegionKind) +ABSTRACT_REGION(SubRegion, MemRegion) + REGION(AllocaRegion, SubRegion) + REGION(SymbolicRegion, SubRegion) + ABSTRACT_REGION(TypedRegion, SubRegion) + REGION(BlockDataRegion, TypedRegion) + ABSTRACT_REGION(CodeTextRegion, TypedRegion) + REGION(BlockCodeRegion, CodeTextRegion) + REGION(FunctionCodeRegion, CodeTextRegion) + REGION_RANGE(CODE_TEXT_REGIONS, BlockCodeRegionKind, + FunctionCodeRegionKind) + ABSTRACT_REGION(TypedValueRegion, TypedRegion) + REGION(CompoundLiteralRegion, TypedValueRegion) + REGION(CXXBaseObjectRegion, TypedValueRegion) + REGION(CXXDerivedObjectRegion, TypedValueRegion) + REGION(CXXTempObjectRegion, TypedValueRegion) + REGION(CXXThisRegion, TypedValueRegion) + ABSTRACT_REGION(DeclRegion, TypedValueRegion) + REGION(FieldRegion, DeclRegion) + REGION(ObjCIvarRegion, DeclRegion) + REGION(VarRegion, DeclRegion) + REGION_RANGE(DECL_REGIONS, FieldRegionKind, + VarRegionKind) + REGION(ElementRegion, TypedValueRegion) + REGION(ObjCStringRegion, TypedValueRegion) + REGION(StringRegion, TypedValueRegion) + REGION_RANGE(TYPED_VALUE_REGIONS, CompoundLiteralRegionKind, + StringRegionKind) + REGION_RANGE(TYPED_REGIONS, BlockDataRegionKind, + StringRegionKind) + +#undef REGION_RANGE +#undef ABSTRACT_REGION +#undef REGION diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SMTAPI.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SMTAPI.h new file mode 100644 index 00000000..51648921 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SMTAPI.h @@ -0,0 +1,405 @@ +//===- SMTAPI.h -------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines a SMT generic Solver API, which will be the base class +// for every SMT solver specific class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTSOLVER_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTSOLVER_H + +#include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/FoldingSet.h" + +namespace clang { +namespace ento { + +/// Generic base class for SMT sorts +class SMTSort { +public: + SMTSort() = default; + virtual ~SMTSort() = default; + + /// Returns true if the sort is a bitvector, calls isBitvectorSortImpl(). + virtual bool isBitvectorSort() const { return isBitvectorSortImpl(); } + + /// Returns true if the sort is a floating-point, calls isFloatSortImpl(). + virtual bool isFloatSort() const { return isFloatSortImpl(); } + + /// Returns true if the sort is a boolean, calls isBooleanSortImpl(). + virtual bool isBooleanSort() const { return isBooleanSortImpl(); } + + /// Returns the bitvector size, fails if the sort is not a bitvector + /// Calls getBitvectorSortSizeImpl(). + virtual unsigned getBitvectorSortSize() const { + assert(isBitvectorSort() && "Not a bitvector sort!"); + unsigned Size = getBitvectorSortSizeImpl(); + assert(Size && "Size is zero!"); + return Size; + }; + + /// Returns the floating-point size, fails if the sort is not a floating-point + /// Calls getFloatSortSizeImpl(). + virtual unsigned getFloatSortSize() const { + assert(isFloatSort() && "Not a floating-point sort!"); + unsigned Size = getFloatSortSizeImpl(); + assert(Size && "Size is zero!"); + return Size; + }; + + virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0; + + bool operator<(const SMTSort &Other) const { + llvm::FoldingSetNodeID ID1, ID2; + Profile(ID1); + Other.Profile(ID2); + return ID1 < ID2; + } + + friend bool operator==(SMTSort const &LHS, SMTSort const &RHS) { + return LHS.equal_to(RHS); + } + + virtual void print(raw_ostream &OS) const = 0; + + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } + +protected: + /// Query the SMT solver and returns true if two sorts are equal (same kind + /// and bit width). This does not check if the two sorts are the same objects. + virtual bool equal_to(SMTSort const &other) const = 0; + + /// Query the SMT solver and checks if a sort is bitvector. + virtual bool isBitvectorSortImpl() const = 0; + + /// Query the SMT solver and checks if a sort is floating-point. + virtual bool isFloatSortImpl() const = 0; + + /// Query the SMT solver and checks if a sort is boolean. + virtual bool isBooleanSortImpl() const = 0; + + /// Query the SMT solver and returns the sort bit width. + virtual unsigned getBitvectorSortSizeImpl() const = 0; + + /// Query the SMT solver and returns the sort bit width. + virtual unsigned getFloatSortSizeImpl() const = 0; +}; + +/// Shared pointer for SMTSorts, used by SMTSolver API. +using SMTSortRef = const SMTSort *; + +/// Generic base class for SMT exprs +class SMTExpr { +public: + SMTExpr() = default; + virtual ~SMTExpr() = default; + + bool operator<(const SMTExpr &Other) const { + llvm::FoldingSetNodeID ID1, ID2; + Profile(ID1); + Other.Profile(ID2); + return ID1 < ID2; + } + + virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0; + + friend bool operator==(SMTExpr const &LHS, SMTExpr const &RHS) { + return LHS.equal_to(RHS); + } + + virtual void print(raw_ostream &OS) const = 0; + + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } + +protected: + /// Query the SMT solver and returns true if two sorts are equal (same kind + /// and bit width). This does not check if the two sorts are the same objects. + virtual bool equal_to(SMTExpr const &other) const = 0; +}; + +/// Shared pointer for SMTExprs, used by SMTSolver API. +using SMTExprRef = const SMTExpr *; + +/// Generic base class for SMT Solvers +/// +/// This class is responsible for wrapping all sorts and expression generation, +/// through the mk* methods. It also provides methods to create SMT expressions +/// straight from clang's AST, through the from* methods. +class SMTSolver { +public: + SMTSolver() = default; + virtual ~SMTSolver() = default; + + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } + + // Returns an appropriate floating-point sort for the given bitwidth. + SMTSortRef getFloatSort(unsigned BitWidth) { + switch (BitWidth) { + case 16: + return getFloat16Sort(); + case 32: + return getFloat32Sort(); + case 64: + return getFloat64Sort(); + case 128: + return getFloat128Sort(); + default:; + } + llvm_unreachable("Unsupported floating-point bitwidth!"); + } + + // Returns a boolean sort. + virtual SMTSortRef getBoolSort() = 0; + + // Returns an appropriate bitvector sort for the given bitwidth. + virtual SMTSortRef getBitvectorSort(const unsigned BitWidth) = 0; + + // Returns a floating-point sort of width 16 + virtual SMTSortRef getFloat16Sort() = 0; + + // Returns a floating-point sort of width 32 + virtual SMTSortRef getFloat32Sort() = 0; + + // Returns a floating-point sort of width 64 + virtual SMTSortRef getFloat64Sort() = 0; + + // Returns a floating-point sort of width 128 + virtual SMTSortRef getFloat128Sort() = 0; + + // Returns an appropriate sort for the given AST. + virtual SMTSortRef getSort(const SMTExprRef &AST) = 0; + + /// Given a constraint, adds it to the solver + virtual void addConstraint(const SMTExprRef &Exp) const = 0; + + /// Creates a bitvector addition operation + virtual SMTExprRef mkBVAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector subtraction operation + virtual SMTExprRef mkBVSub(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector multiplication operation + virtual SMTExprRef mkBVMul(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed modulus operation + virtual SMTExprRef mkBVSRem(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned modulus operation + virtual SMTExprRef mkBVURem(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed division operation + virtual SMTExprRef mkBVSDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned division operation + virtual SMTExprRef mkBVUDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector logical shift left operation + virtual SMTExprRef mkBVShl(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector arithmetic shift right operation + virtual SMTExprRef mkBVAshr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector logical shift right operation + virtual SMTExprRef mkBVLshr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector negation operation + virtual SMTExprRef mkBVNeg(const SMTExprRef &Exp) = 0; + + /// Creates a bitvector not operation + virtual SMTExprRef mkBVNot(const SMTExprRef &Exp) = 0; + + /// Creates a bitvector xor operation + virtual SMTExprRef mkBVXor(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector or operation + virtual SMTExprRef mkBVOr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector and operation + virtual SMTExprRef mkBVAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned less-than operation + virtual SMTExprRef mkBVUlt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed less-than operation + virtual SMTExprRef mkBVSlt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned greater-than operation + virtual SMTExprRef mkBVUgt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed greater-than operation + virtual SMTExprRef mkBVSgt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned less-equal-than operation + virtual SMTExprRef mkBVUle(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed less-equal-than operation + virtual SMTExprRef mkBVSle(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned greater-equal-than operation + virtual SMTExprRef mkBVUge(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed greater-equal-than operation + virtual SMTExprRef mkBVSge(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean not operation + virtual SMTExprRef mkNot(const SMTExprRef &Exp) = 0; + + /// Creates a boolean equality operation + virtual SMTExprRef mkEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean and operation + virtual SMTExprRef mkAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean or operation + virtual SMTExprRef mkOr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean ite operation + virtual SMTExprRef mkIte(const SMTExprRef &Cond, const SMTExprRef &T, + const SMTExprRef &F) = 0; + + /// Creates a bitvector sign extension operation + virtual SMTExprRef mkBVSignExt(unsigned i, const SMTExprRef &Exp) = 0; + + /// Creates a bitvector zero extension operation + virtual SMTExprRef mkBVZeroExt(unsigned i, const SMTExprRef &Exp) = 0; + + /// Creates a bitvector extract operation + virtual SMTExprRef mkBVExtract(unsigned High, unsigned Low, + const SMTExprRef &Exp) = 0; + + /// Creates a bitvector concat operation + virtual SMTExprRef mkBVConcat(const SMTExprRef &LHS, + const SMTExprRef &RHS) = 0; + + /// Creates a floating-point negation operation + virtual SMTExprRef mkFPNeg(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isInfinite operation + virtual SMTExprRef mkFPIsInfinite(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isNaN operation + virtual SMTExprRef mkFPIsNaN(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isNormal operation + virtual SMTExprRef mkFPIsNormal(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isZero operation + virtual SMTExprRef mkFPIsZero(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point multiplication operation + virtual SMTExprRef mkFPMul(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point division operation + virtual SMTExprRef mkFPDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point remainder operation + virtual SMTExprRef mkFPRem(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point addition operation + virtual SMTExprRef mkFPAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point subtraction operation + virtual SMTExprRef mkFPSub(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point less-than operation + virtual SMTExprRef mkFPLt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point greater-than operation + virtual SMTExprRef mkFPGt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point less-than-or-equal operation + virtual SMTExprRef mkFPLe(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point greater-than-or-equal operation + virtual SMTExprRef mkFPGe(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point equality operation + virtual SMTExprRef mkFPEqual(const SMTExprRef &LHS, + const SMTExprRef &RHS) = 0; + + /// Creates a floating-point conversion from floatint-point to floating-point + /// operation + virtual SMTExprRef mkFPtoFP(const SMTExprRef &From, const SMTSortRef &To) = 0; + + /// Creates a floating-point conversion from signed bitvector to + /// floatint-point operation + virtual SMTExprRef mkSBVtoFP(const SMTExprRef &From, + const SMTSortRef &To) = 0; + + /// Creates a floating-point conversion from unsigned bitvector to + /// floatint-point operation + virtual SMTExprRef mkUBVtoFP(const SMTExprRef &From, + const SMTSortRef &To) = 0; + + /// Creates a floating-point conversion from floatint-point to signed + /// bitvector operation + virtual SMTExprRef mkFPtoSBV(const SMTExprRef &From, unsigned ToWidth) = 0; + + /// Creates a floating-point conversion from floatint-point to unsigned + /// bitvector operation + virtual SMTExprRef mkFPtoUBV(const SMTExprRef &From, unsigned ToWidth) = 0; + + /// Creates a new symbol, given a name and a sort + virtual SMTExprRef mkSymbol(const char *Name, SMTSortRef Sort) = 0; + + // Returns an appropriate floating-point rounding mode. + virtual SMTExprRef getFloatRoundingMode() = 0; + + // If the a model is available, returns the value of a given bitvector symbol + virtual llvm::APSInt getBitvector(const SMTExprRef &Exp, unsigned BitWidth, + bool isUnsigned) = 0; + + // If the a model is available, returns the value of a given boolean symbol + virtual bool getBoolean(const SMTExprRef &Exp) = 0; + + /// Constructs an SMTExprRef from a boolean. + virtual SMTExprRef mkBoolean(const bool b) = 0; + + /// Constructs an SMTExprRef from a finite APFloat. + virtual SMTExprRef mkFloat(const llvm::APFloat Float) = 0; + + /// Constructs an SMTExprRef from an APSInt and its bit width + virtual SMTExprRef mkBitvector(const llvm::APSInt Int, unsigned BitWidth) = 0; + + /// Given an expression, extract the value of this operand in the model. + virtual bool getInterpretation(const SMTExprRef &Exp, llvm::APSInt &Int) = 0; + + /// Given an expression extract the value of this operand in the model. + virtual bool getInterpretation(const SMTExprRef &Exp, + llvm::APFloat &Float) = 0; + + /// Check if the constraints are satisfiable + virtual Optional<bool> check() const = 0; + + /// Push the current solver state + virtual void push() = 0; + + /// Pop the previous solver state + virtual void pop(unsigned NumStates = 1) = 0; + + /// Reset the solver and remove all constraints. + virtual void reset() = 0; + + /// Checks if the solver supports floating-points. + virtual bool isFPSupported() = 0; + + virtual void print(raw_ostream &OS) const = 0; +}; + +/// Shared pointer for SMTSolvers. +using SMTSolverRef = std::shared_ptr<SMTSolver>; + +/// Convenience method to create and Z3Solver object +SMTSolverRef CreateZ3Solver(); + +} // namespace ento +} // namespace clang + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h new file mode 100644 index 00000000..72f36014 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h @@ -0,0 +1,337 @@ +//== SMTConstraintManager.h -------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines a SMT generic API, which will be the base class for +// every SMT solver specific class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h" + +typedef llvm::ImmutableSet< + std::pair<clang::ento::SymbolRef, const clang::ento::SMTExpr *>> + ConstraintSMTType; +REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintSMT, ConstraintSMTType) + +namespace clang { +namespace ento { + +class SMTConstraintManager : public clang::ento::SimpleConstraintManager { + mutable SMTSolverRef Solver = CreateZ3Solver(); + +public: + SMTConstraintManager(clang::ento::SubEngine *SE, clang::ento::SValBuilder &SB) + : SimpleConstraintManager(SE, SB) {} + virtual ~SMTConstraintManager() = default; + + //===------------------------------------------------------------------===// + // Implementation for interface from SimpleConstraintManager. + //===------------------------------------------------------------------===// + + ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym, + bool Assumption) override { + ASTContext &Ctx = getBasicVals().getContext(); + + QualType RetTy; + bool hasComparison; + + SMTExprRef Exp = SMTConv::getExpr(Solver, Ctx, Sym, &RetTy, &hasComparison); + + // Create zero comparison for implicit boolean cast, with reversed + // assumption + if (!hasComparison && !RetTy->isBooleanType()) + return assumeExpr( + State, Sym, + SMTConv::getZeroExpr(Solver, Ctx, Exp, RetTy, !Assumption)); + + return assumeExpr(State, Sym, Assumption ? Exp : Solver->mkNot(Exp)); + } + + ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange) override { + ASTContext &Ctx = getBasicVals().getContext(); + return assumeExpr( + State, Sym, SMTConv::getRangeExpr(Solver, Ctx, Sym, From, To, InRange)); + } + + ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym, + bool Assumption) override { + // Skip anything that is unsupported + return State; + } + + //===------------------------------------------------------------------===// + // Implementation for interface from ConstraintManager. + //===------------------------------------------------------------------===// + + ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override { + ASTContext &Ctx = getBasicVals().getContext(); + + QualType RetTy; + // The expression may be casted, so we cannot call getZ3DataExpr() directly + SMTExprRef VarExp = SMTConv::getExpr(Solver, Ctx, Sym, &RetTy); + SMTExprRef Exp = + SMTConv::getZeroExpr(Solver, Ctx, VarExp, RetTy, /*Assumption=*/true); + + // Negate the constraint + SMTExprRef NotExp = + SMTConv::getZeroExpr(Solver, Ctx, VarExp, RetTy, /*Assumption=*/false); + + ConditionTruthVal isSat = checkModel(State, Sym, Exp); + ConditionTruthVal isNotSat = checkModel(State, Sym, NotExp); + + // Zero is the only possible solution + if (isSat.isConstrainedTrue() && isNotSat.isConstrainedFalse()) + return true; + + // Zero is not a solution + if (isSat.isConstrainedFalse() && isNotSat.isConstrainedTrue()) + return false; + + // Zero may be a solution + return ConditionTruthVal(); + } + + const llvm::APSInt *getSymVal(ProgramStateRef State, + SymbolRef Sym) const override { + BasicValueFactory &BVF = getBasicVals(); + ASTContext &Ctx = BVF.getContext(); + + if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) { + QualType Ty = Sym->getType(); + assert(!Ty->isRealFloatingType()); + llvm::APSInt Value(Ctx.getTypeSize(Ty), + !Ty->isSignedIntegerOrEnumerationType()); + + // TODO: this should call checkModel so we can use the cache, however, + // this method tries to get the interpretation (the actual value) from + // the solver, which is currently not cached. + + SMTExprRef Exp = + SMTConv::fromData(Solver, SD->getSymbolID(), Ty, Ctx.getTypeSize(Ty)); + + Solver->reset(); + addStateConstraints(State); + + // Constraints are unsatisfiable + Optional<bool> isSat = Solver->check(); + if (!isSat.hasValue() || !isSat.getValue()) + return nullptr; + + // Model does not assign interpretation + if (!Solver->getInterpretation(Exp, Value)) + return nullptr; + + // A value has been obtained, check if it is the only value + SMTExprRef NotExp = SMTConv::fromBinOp( + Solver, Exp, BO_NE, + Ty->isBooleanType() ? Solver->mkBoolean(Value.getBoolValue()) + : Solver->mkBitvector(Value, Value.getBitWidth()), + /*isSigned=*/false); + + Solver->addConstraint(NotExp); + + Optional<bool> isNotSat = Solver->check(); + if (!isSat.hasValue() || isNotSat.getValue()) + return nullptr; + + // This is the only solution, store it + return &BVF.getValue(Value); + } + + if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) { + SymbolRef CastSym = SC->getOperand(); + QualType CastTy = SC->getType(); + // Skip the void type + if (CastTy->isVoidType()) + return nullptr; + + const llvm::APSInt *Value; + if (!(Value = getSymVal(State, CastSym))) + return nullptr; + return &BVF.Convert(SC->getType(), *Value); + } + + if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { + const llvm::APSInt *LHS, *RHS; + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) { + LHS = getSymVal(State, SIE->getLHS()); + RHS = &SIE->getRHS(); + } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) { + LHS = &ISE->getLHS(); + RHS = getSymVal(State, ISE->getRHS()); + } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) { + // Early termination to avoid expensive call + LHS = getSymVal(State, SSM->getLHS()); + RHS = LHS ? getSymVal(State, SSM->getRHS()) : nullptr; + } else { + llvm_unreachable("Unsupported binary expression to get symbol value!"); + } + + if (!LHS || !RHS) + return nullptr; + + llvm::APSInt ConvertedLHS, ConvertedRHS; + QualType LTy, RTy; + std::tie(ConvertedLHS, LTy) = SMTConv::fixAPSInt(Ctx, *LHS); + std::tie(ConvertedRHS, RTy) = SMTConv::fixAPSInt(Ctx, *RHS); + SMTConv::doIntTypeConversion<llvm::APSInt, &SMTConv::castAPSInt>( + Solver, Ctx, ConvertedLHS, LTy, ConvertedRHS, RTy); + return BVF.evalAPSInt(BSE->getOpcode(), ConvertedLHS, ConvertedRHS); + } + + llvm_unreachable("Unsupported expression to get symbol value!"); + } + + ProgramStateRef removeDeadBindings(ProgramStateRef State, + SymbolReaper &SymReaper) override { + auto CZ = State->get<ConstraintSMT>(); + auto &CZFactory = State->get_context<ConstraintSMT>(); + + for (auto I = CZ.begin(), E = CZ.end(); I != E; ++I) { + if (SymReaper.isDead(I->first)) + CZ = CZFactory.remove(CZ, *I); + } + + return State->set<ConstraintSMT>(CZ); + } + + void print(ProgramStateRef St, raw_ostream &OS, const char *nl, + const char *sep) override { + + auto CZ = St->get<ConstraintSMT>(); + + OS << nl << sep << "Constraints:"; + for (auto I = CZ.begin(), E = CZ.end(); I != E; ++I) { + OS << nl << ' ' << I->first << " : "; + I->second->print(OS); + } + OS << nl; + } + + bool canReasonAbout(SVal X) const override { + const TargetInfo &TI = getBasicVals().getContext().getTargetInfo(); + + Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); + if (!SymVal) + return true; + + const SymExpr *Sym = SymVal->getSymbol(); + QualType Ty = Sym->getType(); + + // Complex types are not modeled + if (Ty->isComplexType() || Ty->isComplexIntegerType()) + return false; + + // Non-IEEE 754 floating-point types are not modeled + if ((Ty->isSpecificBuiltinType(BuiltinType::LongDouble) && + (&TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended() || + &TI.getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble()))) + return false; + + if (Ty->isRealFloatingType()) + return Solver->isFPSupported(); + + if (isa<SymbolData>(Sym)) + return true; + + SValBuilder &SVB = getSValBuilder(); + + if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) + return canReasonAbout(SVB.makeSymbolVal(SC->getOperand())); + + if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) + return canReasonAbout(SVB.makeSymbolVal(SIE->getLHS())); + + if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) + return canReasonAbout(SVB.makeSymbolVal(ISE->getRHS())); + + if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(BSE)) + return canReasonAbout(SVB.makeSymbolVal(SSE->getLHS())) && + canReasonAbout(SVB.makeSymbolVal(SSE->getRHS())); + } + + llvm_unreachable("Unsupported expression to reason about!"); + } + + /// Dumps SMT formula + LLVM_DUMP_METHOD void dump() const { Solver->dump(); } + +protected: + // Check whether a new model is satisfiable, and update the program state. + virtual ProgramStateRef assumeExpr(ProgramStateRef State, SymbolRef Sym, + const SMTExprRef &Exp) { + // Check the model, avoid simplifying AST to save time + if (checkModel(State, Sym, Exp).isConstrainedTrue()) + return State->add<ConstraintSMT>(std::make_pair(Sym, Exp)); + + return nullptr; + } + + /// Given a program state, construct the logical conjunction and add it to + /// the solver + virtual void addStateConstraints(ProgramStateRef State) const { + // TODO: Don't add all the constraints, only the relevant ones + auto CZ = State->get<ConstraintSMT>(); + auto I = CZ.begin(), IE = CZ.end(); + + // Construct the logical AND of all the constraints + if (I != IE) { + std::vector<SMTExprRef> ASTs; + + SMTExprRef Constraint = I++->second; + while (I != IE) { + Constraint = Solver->mkAnd(Constraint, I++->second); + } + + Solver->addConstraint(Constraint); + } + } + + // Generate and check a Z3 model, using the given constraint. + ConditionTruthVal checkModel(ProgramStateRef State, SymbolRef Sym, + const SMTExprRef &Exp) const { + ProgramStateRef NewState = + State->add<ConstraintSMT>(std::make_pair(Sym, Exp)); + + llvm::FoldingSetNodeID ID; + NewState->get<ConstraintSMT>().Profile(ID); + + unsigned hash = ID.ComputeHash(); + auto I = Cached.find(hash); + if (I != Cached.end()) + return I->second; + + Solver->reset(); + addStateConstraints(NewState); + + Optional<bool> res = Solver->check(); + if (!res.hasValue()) + Cached[hash] = ConditionTruthVal(); + else + Cached[hash] = ConditionTruthVal(res.getValue()); + + return Cached[hash]; + } + + // Cache the result of an SMT query (true, false, unknown). The key is the + // hash of the constraints in a state + mutable llvm::DenseMap<unsigned, ConditionTruthVal> Cached; +}; // end class SMTConstraintManager + +} // namespace ento +} // namespace clang + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h new file mode 100644 index 00000000..f5145699 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h @@ -0,0 +1,752 @@ +//== SMTConv.h --------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines a set of functions to create SMT expressions +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONV_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONV_H + +#include "clang/AST/Expr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTAPI.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" + +namespace clang { +namespace ento { + +class SMTConv { +public: + // Returns an appropriate sort, given a QualType and it's bit width. + static inline SMTSortRef mkSort(SMTSolverRef &Solver, const QualType &Ty, + unsigned BitWidth) { + if (Ty->isBooleanType()) + return Solver->getBoolSort(); + + if (Ty->isRealFloatingType()) + return Solver->getFloatSort(BitWidth); + + return Solver->getBitvectorSort(BitWidth); + } + + /// Constructs an SMTExprRef from an unary operator. + static inline SMTExprRef fromUnOp(SMTSolverRef &Solver, + const UnaryOperator::Opcode Op, + const SMTExprRef &Exp) { + switch (Op) { + case UO_Minus: + return Solver->mkBVNeg(Exp); + + case UO_Not: + return Solver->mkBVNot(Exp); + + case UO_LNot: + return Solver->mkNot(Exp); + + default:; + } + llvm_unreachable("Unimplemented opcode"); + } + + /// Constructs an SMTExprRef from a floating-point unary operator. + static inline SMTExprRef fromFloatUnOp(SMTSolverRef &Solver, + const UnaryOperator::Opcode Op, + const SMTExprRef &Exp) { + switch (Op) { + case UO_Minus: + return Solver->mkFPNeg(Exp); + + case UO_LNot: + return fromUnOp(Solver, Op, Exp); + + default:; + } + llvm_unreachable("Unimplemented opcode"); + } + + /// Construct an SMTExprRef from a n-ary binary operator. + static inline SMTExprRef fromNBinOp(SMTSolverRef &Solver, + const BinaryOperator::Opcode Op, + const std::vector<SMTExprRef> &ASTs) { + assert(!ASTs.empty()); + + if (Op != BO_LAnd && Op != BO_LOr) + llvm_unreachable("Unimplemented opcode"); + + SMTExprRef res = ASTs.front(); + for (std::size_t i = 1; i < ASTs.size(); ++i) + res = (Op == BO_LAnd) ? Solver->mkAnd(res, ASTs[i]) + : Solver->mkOr(res, ASTs[i]); + return res; + } + + /// Construct an SMTExprRef from a binary operator. + static inline SMTExprRef fromBinOp(SMTSolverRef &Solver, + const SMTExprRef &LHS, + const BinaryOperator::Opcode Op, + const SMTExprRef &RHS, bool isSigned) { + assert(*Solver->getSort(LHS) == *Solver->getSort(RHS) && + "AST's must have the same sort!"); + + switch (Op) { + // Multiplicative operators + case BO_Mul: + return Solver->mkBVMul(LHS, RHS); + + case BO_Div: + return isSigned ? Solver->mkBVSDiv(LHS, RHS) : Solver->mkBVUDiv(LHS, RHS); + + case BO_Rem: + return isSigned ? Solver->mkBVSRem(LHS, RHS) : Solver->mkBVURem(LHS, RHS); + + // Additive operators + case BO_Add: + return Solver->mkBVAdd(LHS, RHS); + + case BO_Sub: + return Solver->mkBVSub(LHS, RHS); + + // Bitwise shift operators + case BO_Shl: + return Solver->mkBVShl(LHS, RHS); + + case BO_Shr: + return isSigned ? Solver->mkBVAshr(LHS, RHS) : Solver->mkBVLshr(LHS, RHS); + + // Relational operators + case BO_LT: + return isSigned ? Solver->mkBVSlt(LHS, RHS) : Solver->mkBVUlt(LHS, RHS); + + case BO_GT: + return isSigned ? Solver->mkBVSgt(LHS, RHS) : Solver->mkBVUgt(LHS, RHS); + + case BO_LE: + return isSigned ? Solver->mkBVSle(LHS, RHS) : Solver->mkBVUle(LHS, RHS); + + case BO_GE: + return isSigned ? Solver->mkBVSge(LHS, RHS) : Solver->mkBVUge(LHS, RHS); + + // Equality operators + case BO_EQ: + return Solver->mkEqual(LHS, RHS); + + case BO_NE: + return fromUnOp(Solver, UO_LNot, + fromBinOp(Solver, LHS, BO_EQ, RHS, isSigned)); + + // Bitwise operators + case BO_And: + return Solver->mkBVAnd(LHS, RHS); + + case BO_Xor: + return Solver->mkBVXor(LHS, RHS); + + case BO_Or: + return Solver->mkBVOr(LHS, RHS); + + // Logical operators + case BO_LAnd: + return Solver->mkAnd(LHS, RHS); + + case BO_LOr: + return Solver->mkOr(LHS, RHS); + + default:; + } + llvm_unreachable("Unimplemented opcode"); + } + + /// Construct an SMTExprRef from a special floating-point binary operator. + static inline SMTExprRef + fromFloatSpecialBinOp(SMTSolverRef &Solver, const SMTExprRef &LHS, + const BinaryOperator::Opcode Op, + const llvm::APFloat::fltCategory &RHS) { + switch (Op) { + // Equality operators + case BO_EQ: + switch (RHS) { + case llvm::APFloat::fcInfinity: + return Solver->mkFPIsInfinite(LHS); + + case llvm::APFloat::fcNaN: + return Solver->mkFPIsNaN(LHS); + + case llvm::APFloat::fcNormal: + return Solver->mkFPIsNormal(LHS); + + case llvm::APFloat::fcZero: + return Solver->mkFPIsZero(LHS); + } + break; + + case BO_NE: + return fromFloatUnOp(Solver, UO_LNot, + fromFloatSpecialBinOp(Solver, LHS, BO_EQ, RHS)); + + default:; + } + + llvm_unreachable("Unimplemented opcode"); + } + + /// Construct an SMTExprRef from a floating-point binary operator. + static inline SMTExprRef fromFloatBinOp(SMTSolverRef &Solver, + const SMTExprRef &LHS, + const BinaryOperator::Opcode Op, + const SMTExprRef &RHS) { + assert(*Solver->getSort(LHS) == *Solver->getSort(RHS) && + "AST's must have the same sort!"); + + switch (Op) { + // Multiplicative operators + case BO_Mul: + return Solver->mkFPMul(LHS, RHS); + + case BO_Div: + return Solver->mkFPDiv(LHS, RHS); + + case BO_Rem: + return Solver->mkFPRem(LHS, RHS); + + // Additive operators + case BO_Add: + return Solver->mkFPAdd(LHS, RHS); + + case BO_Sub: + return Solver->mkFPSub(LHS, RHS); + + // Relational operators + case BO_LT: + return Solver->mkFPLt(LHS, RHS); + + case BO_GT: + return Solver->mkFPGt(LHS, RHS); + + case BO_LE: + return Solver->mkFPLe(LHS, RHS); + + case BO_GE: + return Solver->mkFPGe(LHS, RHS); + + // Equality operators + case BO_EQ: + return Solver->mkFPEqual(LHS, RHS); + + case BO_NE: + return fromFloatUnOp(Solver, UO_LNot, + fromFloatBinOp(Solver, LHS, BO_EQ, RHS)); + + // Logical operators + case BO_LAnd: + case BO_LOr: + return fromBinOp(Solver, LHS, Op, RHS, /*isSigned=*/false); + + default:; + } + + llvm_unreachable("Unimplemented opcode"); + } + + /// Construct an SMTExprRef from a QualType FromTy to a QualType ToTy, and + /// their bit widths. + static inline SMTExprRef fromCast(SMTSolverRef &Solver, const SMTExprRef &Exp, + QualType ToTy, uint64_t ToBitWidth, + QualType FromTy, uint64_t FromBitWidth) { + if ((FromTy->isIntegralOrEnumerationType() && + ToTy->isIntegralOrEnumerationType()) || + (FromTy->isAnyPointerType() ^ ToTy->isAnyPointerType()) || + (FromTy->isBlockPointerType() ^ ToTy->isBlockPointerType()) || + (FromTy->isReferenceType() ^ ToTy->isReferenceType())) { + + if (FromTy->isBooleanType()) { + assert(ToBitWidth > 0 && "BitWidth must be positive!"); + return Solver->mkIte( + Exp, Solver->mkBitvector(llvm::APSInt("1"), ToBitWidth), + Solver->mkBitvector(llvm::APSInt("0"), ToBitWidth)); + } + + if (ToBitWidth > FromBitWidth) + return FromTy->isSignedIntegerOrEnumerationType() + ? Solver->mkBVSignExt(ToBitWidth - FromBitWidth, Exp) + : Solver->mkBVZeroExt(ToBitWidth - FromBitWidth, Exp); + + if (ToBitWidth < FromBitWidth) + return Solver->mkBVExtract(ToBitWidth - 1, 0, Exp); + + // Both are bitvectors with the same width, ignore the type cast + return Exp; + } + + if (FromTy->isRealFloatingType() && ToTy->isRealFloatingType()) { + if (ToBitWidth != FromBitWidth) + return Solver->mkFPtoFP(Exp, Solver->getFloatSort(ToBitWidth)); + + return Exp; + } + + if (FromTy->isIntegralOrEnumerationType() && ToTy->isRealFloatingType()) { + SMTSortRef Sort = Solver->getFloatSort(ToBitWidth); + return FromTy->isSignedIntegerOrEnumerationType() + ? Solver->mkSBVtoFP(Exp, Sort) + : Solver->mkUBVtoFP(Exp, Sort); + } + + if (FromTy->isRealFloatingType() && ToTy->isIntegralOrEnumerationType()) + return ToTy->isSignedIntegerOrEnumerationType() + ? Solver->mkFPtoSBV(Exp, ToBitWidth) + : Solver->mkFPtoUBV(Exp, ToBitWidth); + + llvm_unreachable("Unsupported explicit type cast!"); + } + + // Callback function for doCast parameter on APSInt type. + static inline llvm::APSInt castAPSInt(SMTSolverRef &Solver, + const llvm::APSInt &V, QualType ToTy, + uint64_t ToWidth, QualType FromTy, + uint64_t FromWidth) { + APSIntType TargetType(ToWidth, !ToTy->isSignedIntegerOrEnumerationType()); + return TargetType.convert(V); + } + + /// Construct an SMTExprRef from a SymbolData. + static inline SMTExprRef fromData(SMTSolverRef &Solver, const SymbolID ID, + const QualType &Ty, uint64_t BitWidth) { + llvm::Twine Name = "$" + llvm::Twine(ID); + return Solver->mkSymbol(Name.str().c_str(), mkSort(Solver, Ty, BitWidth)); + } + + // Wrapper to generate SMTExprRef from SymbolCast data. + static inline SMTExprRef getCastExpr(SMTSolverRef &Solver, ASTContext &Ctx, + const SMTExprRef &Exp, QualType FromTy, + QualType ToTy) { + return fromCast(Solver, Exp, ToTy, Ctx.getTypeSize(ToTy), FromTy, + Ctx.getTypeSize(FromTy)); + } + + // Wrapper to generate SMTExprRef from unpacked binary symbolic expression. + // Sets the RetTy parameter. See getSMTExprRef(). + static inline SMTExprRef getBinExpr(SMTSolverRef &Solver, ASTContext &Ctx, + const SMTExprRef &LHS, QualType LTy, + BinaryOperator::Opcode Op, + const SMTExprRef &RHS, QualType RTy, + QualType *RetTy) { + SMTExprRef NewLHS = LHS; + SMTExprRef NewRHS = RHS; + doTypeConversion(Solver, Ctx, NewLHS, NewRHS, LTy, RTy); + + // Update the return type parameter if the output type has changed. + if (RetTy) { + // A boolean result can be represented as an integer type in C/C++, but at + // this point we only care about the SMT sorts. Set it as a boolean type + // to avoid subsequent SMT errors. + if (BinaryOperator::isComparisonOp(Op) || + BinaryOperator::isLogicalOp(Op)) { + *RetTy = Ctx.BoolTy; + } else { + *RetTy = LTy; + } + + // If the two operands are pointers and the operation is a subtraction, + // the result is of type ptrdiff_t, which is signed + if (LTy->isAnyPointerType() && RTy->isAnyPointerType() && Op == BO_Sub) { + *RetTy = Ctx.getPointerDiffType(); + } + } + + return LTy->isRealFloatingType() + ? fromFloatBinOp(Solver, NewLHS, Op, NewRHS) + : fromBinOp(Solver, NewLHS, Op, NewRHS, + LTy->isSignedIntegerOrEnumerationType()); + } + + // Wrapper to generate SMTExprRef from BinarySymExpr. + // Sets the hasComparison and RetTy parameters. See getSMTExprRef(). + static inline SMTExprRef getSymBinExpr(SMTSolverRef &Solver, ASTContext &Ctx, + const BinarySymExpr *BSE, + bool *hasComparison, QualType *RetTy) { + QualType LTy, RTy; + BinaryOperator::Opcode Op = BSE->getOpcode(); + + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) { + SMTExprRef LHS = + getSymExpr(Solver, Ctx, SIE->getLHS(), <y, hasComparison); + llvm::APSInt NewRInt; + std::tie(NewRInt, RTy) = fixAPSInt(Ctx, SIE->getRHS()); + SMTExprRef RHS = Solver->mkBitvector(NewRInt, NewRInt.getBitWidth()); + return getBinExpr(Solver, Ctx, LHS, LTy, Op, RHS, RTy, RetTy); + } + + if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) { + llvm::APSInt NewLInt; + std::tie(NewLInt, LTy) = fixAPSInt(Ctx, ISE->getLHS()); + SMTExprRef LHS = Solver->mkBitvector(NewLInt, NewLInt.getBitWidth()); + SMTExprRef RHS = + getSymExpr(Solver, Ctx, ISE->getRHS(), &RTy, hasComparison); + return getBinExpr(Solver, Ctx, LHS, LTy, Op, RHS, RTy, RetTy); + } + + if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) { + SMTExprRef LHS = + getSymExpr(Solver, Ctx, SSM->getLHS(), <y, hasComparison); + SMTExprRef RHS = + getSymExpr(Solver, Ctx, SSM->getRHS(), &RTy, hasComparison); + return getBinExpr(Solver, Ctx, LHS, LTy, Op, RHS, RTy, RetTy); + } + + llvm_unreachable("Unsupported BinarySymExpr type!"); + } + + // Recursive implementation to unpack and generate symbolic expression. + // Sets the hasComparison and RetTy parameters. See getExpr(). + static inline SMTExprRef getSymExpr(SMTSolverRef &Solver, ASTContext &Ctx, + SymbolRef Sym, QualType *RetTy, + bool *hasComparison) { + if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) { + if (RetTy) + *RetTy = Sym->getType(); + + return fromData(Solver, SD->getSymbolID(), Sym->getType(), + Ctx.getTypeSize(Sym->getType())); + } + + if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) { + if (RetTy) + *RetTy = Sym->getType(); + + QualType FromTy; + SMTExprRef Exp = + getSymExpr(Solver, Ctx, SC->getOperand(), &FromTy, hasComparison); + + // Casting an expression with a comparison invalidates it. Note that this + // must occur after the recursive call above. + // e.g. (signed char) (x > 0) + if (hasComparison) + *hasComparison = false; + return getCastExpr(Solver, Ctx, Exp, FromTy, Sym->getType()); + } + + if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { + SMTExprRef Exp = getSymBinExpr(Solver, Ctx, BSE, hasComparison, RetTy); + // Set the hasComparison parameter, in post-order traversal order. + if (hasComparison) + *hasComparison = BinaryOperator::isComparisonOp(BSE->getOpcode()); + return Exp; + } + + llvm_unreachable("Unsupported SymbolRef type!"); + } + + // Generate an SMTExprRef that represents the given symbolic expression. + // Sets the hasComparison parameter if the expression has a comparison + // operator. Sets the RetTy parameter to the final return type after + // promotions and casts. + static inline SMTExprRef getExpr(SMTSolverRef &Solver, ASTContext &Ctx, + SymbolRef Sym, QualType *RetTy = nullptr, + bool *hasComparison = nullptr) { + if (hasComparison) { + *hasComparison = false; + } + + return getSymExpr(Solver, Ctx, Sym, RetTy, hasComparison); + } + + // Generate an SMTExprRef that compares the expression to zero. + static inline SMTExprRef getZeroExpr(SMTSolverRef &Solver, ASTContext &Ctx, + const SMTExprRef &Exp, QualType Ty, + bool Assumption) { + + if (Ty->isRealFloatingType()) { + llvm::APFloat Zero = + llvm::APFloat::getZero(Ctx.getFloatTypeSemantics(Ty)); + return fromFloatBinOp(Solver, Exp, Assumption ? BO_EQ : BO_NE, + Solver->mkFloat(Zero)); + } + + if (Ty->isIntegralOrEnumerationType() || Ty->isAnyPointerType() || + Ty->isBlockPointerType() || Ty->isReferenceType()) { + + // Skip explicit comparison for boolean types + bool isSigned = Ty->isSignedIntegerOrEnumerationType(); + if (Ty->isBooleanType()) + return Assumption ? fromUnOp(Solver, UO_LNot, Exp) : Exp; + + return fromBinOp( + Solver, Exp, Assumption ? BO_EQ : BO_NE, + Solver->mkBitvector(llvm::APSInt("0"), Ctx.getTypeSize(Ty)), + isSigned); + } + + llvm_unreachable("Unsupported type for zero value!"); + } + + // Wrapper to generate SMTExprRef from a range. If From == To, an equality + // will be created instead. + static inline SMTExprRef getRangeExpr(SMTSolverRef &Solver, ASTContext &Ctx, + SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, bool InRange) { + // Convert lower bound + QualType FromTy; + llvm::APSInt NewFromInt; + std::tie(NewFromInt, FromTy) = fixAPSInt(Ctx, From); + SMTExprRef FromExp = + Solver->mkBitvector(NewFromInt, NewFromInt.getBitWidth()); + + // Convert symbol + QualType SymTy; + SMTExprRef Exp = getExpr(Solver, Ctx, Sym, &SymTy); + + // Construct single (in)equality + if (From == To) + return getBinExpr(Solver, Ctx, Exp, SymTy, InRange ? BO_EQ : BO_NE, + FromExp, FromTy, /*RetTy=*/nullptr); + + QualType ToTy; + llvm::APSInt NewToInt; + std::tie(NewToInt, ToTy) = fixAPSInt(Ctx, To); + SMTExprRef ToExp = Solver->mkBitvector(NewToInt, NewToInt.getBitWidth()); + assert(FromTy == ToTy && "Range values have different types!"); + + // Construct two (in)equalities, and a logical and/or + SMTExprRef LHS = + getBinExpr(Solver, Ctx, Exp, SymTy, InRange ? BO_GE : BO_LT, FromExp, + FromTy, /*RetTy=*/nullptr); + SMTExprRef RHS = getBinExpr(Solver, Ctx, Exp, SymTy, + InRange ? BO_LE : BO_GT, ToExp, ToTy, + /*RetTy=*/nullptr); + + return fromBinOp(Solver, LHS, InRange ? BO_LAnd : BO_LOr, RHS, + SymTy->isSignedIntegerOrEnumerationType()); + } + + // Recover the QualType of an APSInt. + // TODO: Refactor to put elsewhere + static inline QualType getAPSIntType(ASTContext &Ctx, + const llvm::APSInt &Int) { + return Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned()); + } + + // Get the QualTy for the input APSInt, and fix it if it has a bitwidth of 1. + static inline std::pair<llvm::APSInt, QualType> + fixAPSInt(ASTContext &Ctx, const llvm::APSInt &Int) { + llvm::APSInt NewInt; + + // FIXME: This should be a cast from a 1-bit integer type to a boolean type, + // but the former is not available in Clang. Instead, extend the APSInt + // directly. + if (Int.getBitWidth() == 1 && getAPSIntType(Ctx, Int).isNull()) { + NewInt = Int.extend(Ctx.getTypeSize(Ctx.BoolTy)); + } else + NewInt = Int; + + return std::make_pair(NewInt, getAPSIntType(Ctx, NewInt)); + } + + // Perform implicit type conversion on binary symbolic expressions. + // May modify all input parameters. + // TODO: Refactor to use built-in conversion functions + static inline void doTypeConversion(SMTSolverRef &Solver, ASTContext &Ctx, + SMTExprRef &LHS, SMTExprRef &RHS, + QualType <y, QualType &RTy) { + assert(!LTy.isNull() && !RTy.isNull() && "Input type is null!"); + + // Perform type conversion + if ((LTy->isIntegralOrEnumerationType() && + RTy->isIntegralOrEnumerationType()) && + (LTy->isArithmeticType() && RTy->isArithmeticType())) { + SMTConv::doIntTypeConversion<SMTExprRef, &fromCast>(Solver, Ctx, LHS, LTy, + RHS, RTy); + return; + } + + if (LTy->isRealFloatingType() || RTy->isRealFloatingType()) { + SMTConv::doFloatTypeConversion<SMTExprRef, &fromCast>(Solver, Ctx, LHS, + LTy, RHS, RTy); + return; + } + + if ((LTy->isAnyPointerType() || RTy->isAnyPointerType()) || + (LTy->isBlockPointerType() || RTy->isBlockPointerType()) || + (LTy->isReferenceType() || RTy->isReferenceType())) { + // TODO: Refactor to Sema::FindCompositePointerType(), and + // Sema::CheckCompareOperands(). + + uint64_t LBitWidth = Ctx.getTypeSize(LTy); + uint64_t RBitWidth = Ctx.getTypeSize(RTy); + + // Cast the non-pointer type to the pointer type. + // TODO: Be more strict about this. + if ((LTy->isAnyPointerType() ^ RTy->isAnyPointerType()) || + (LTy->isBlockPointerType() ^ RTy->isBlockPointerType()) || + (LTy->isReferenceType() ^ RTy->isReferenceType())) { + if (LTy->isNullPtrType() || LTy->isBlockPointerType() || + LTy->isReferenceType()) { + LHS = fromCast(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } else { + RHS = fromCast(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } + } + + // Cast the void pointer type to the non-void pointer type. + // For void types, this assumes that the casted value is equal to the + // value of the original pointer, and does not account for alignment + // requirements. + if (LTy->isVoidPointerType() ^ RTy->isVoidPointerType()) { + assert((Ctx.getTypeSize(LTy) == Ctx.getTypeSize(RTy)) && + "Pointer types have different bitwidths!"); + if (RTy->isVoidPointerType()) + RTy = LTy; + else + LTy = RTy; + } + + if (LTy == RTy) + return; + } + + // Fallback: for the solver, assume that these types don't really matter + if ((LTy.getCanonicalType() == RTy.getCanonicalType()) || + (LTy->isObjCObjectPointerType() && RTy->isObjCObjectPointerType())) { + LTy = RTy; + return; + } + + // TODO: Refine behavior for invalid type casts + } + + // Perform implicit integer type conversion. + // May modify all input parameters. + // TODO: Refactor to use Sema::handleIntegerConversion() + template <typename T, T (*doCast)(SMTSolverRef &Solver, const T &, QualType, + uint64_t, QualType, uint64_t)> + static inline void doIntTypeConversion(SMTSolverRef &Solver, ASTContext &Ctx, + T &LHS, QualType <y, T &RHS, + QualType &RTy) { + + uint64_t LBitWidth = Ctx.getTypeSize(LTy); + uint64_t RBitWidth = Ctx.getTypeSize(RTy); + + assert(!LTy.isNull() && !RTy.isNull() && "Input type is null!"); + // Always perform integer promotion before checking type equality. + // Otherwise, e.g. (bool) a + (bool) b could trigger a backend assertion + if (LTy->isPromotableIntegerType()) { + QualType NewTy = Ctx.getPromotedIntegerType(LTy); + uint64_t NewBitWidth = Ctx.getTypeSize(NewTy); + LHS = (*doCast)(Solver, LHS, NewTy, NewBitWidth, LTy, LBitWidth); + LTy = NewTy; + LBitWidth = NewBitWidth; + } + if (RTy->isPromotableIntegerType()) { + QualType NewTy = Ctx.getPromotedIntegerType(RTy); + uint64_t NewBitWidth = Ctx.getTypeSize(NewTy); + RHS = (*doCast)(Solver, RHS, NewTy, NewBitWidth, RTy, RBitWidth); + RTy = NewTy; + RBitWidth = NewBitWidth; + } + + if (LTy == RTy) + return; + + // Perform integer type conversion + // Note: Safe to skip updating bitwidth because this must terminate + bool isLSignedTy = LTy->isSignedIntegerOrEnumerationType(); + bool isRSignedTy = RTy->isSignedIntegerOrEnumerationType(); + + int order = Ctx.getIntegerTypeOrder(LTy, RTy); + if (isLSignedTy == isRSignedTy) { + // Same signedness; use the higher-ranked type + if (order == 1) { + RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else { + LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } + } else if (order != (isLSignedTy ? 1 : -1)) { + // The unsigned type has greater than or equal rank to the + // signed type, so use the unsigned type + if (isRSignedTy) { + RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else { + LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } + } else if (LBitWidth != RBitWidth) { + // The two types are different widths; if we are here, that + // means the signed type is larger than the unsigned type, so + // use the signed type. + if (isLSignedTy) { + RHS = (doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else { + LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } + } else { + // The signed type is higher-ranked than the unsigned type, + // but isn't actually any bigger (like unsigned int and long + // on most 32-bit systems). Use the unsigned type corresponding + // to the signed type. + QualType NewTy = + Ctx.getCorrespondingUnsignedType(isLSignedTy ? LTy : RTy); + RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = NewTy; + LHS = (doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = NewTy; + } + } + + // Perform implicit floating-point type conversion. + // May modify all input parameters. + // TODO: Refactor to use Sema::handleFloatConversion() + template <typename T, T (*doCast)(SMTSolverRef &Solver, const T &, QualType, + uint64_t, QualType, uint64_t)> + static inline void + doFloatTypeConversion(SMTSolverRef &Solver, ASTContext &Ctx, T &LHS, + QualType <y, T &RHS, QualType &RTy) { + + uint64_t LBitWidth = Ctx.getTypeSize(LTy); + uint64_t RBitWidth = Ctx.getTypeSize(RTy); + + // Perform float-point type promotion + if (!LTy->isRealFloatingType()) { + LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + LBitWidth = RBitWidth; + } + if (!RTy->isRealFloatingType()) { + RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + RBitWidth = LBitWidth; + } + + if (LTy == RTy) + return; + + // If we have two real floating types, convert the smaller operand to the + // bigger result + // Note: Safe to skip updating bitwidth because this must terminate + int order = Ctx.getFloatingTypeOrder(LTy, RTy); + if (order > 0) { + RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else if (order == 0) { + LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } else { + llvm_unreachable("Unsupported floating-point type cast!"); + } + } +}; +} // namespace ento +} // namespace clang + +#endif
\ No newline at end of file diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h new file mode 100644 index 00000000..35ebefdc --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -0,0 +1,395 @@ +// SValBuilder.h - Construction of SVals from evaluating expressions -*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines SValBuilder, a class that defines the interface for +// "symbolical evaluators" which construct an SVal from an expression. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALBUILDER_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALBUILDER_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/Optional.h" +#include <cstdint> + +namespace clang { + +class BlockDecl; +class CXXBoolLiteralExpr; +class CXXMethodDecl; +class CXXRecordDecl; +class DeclaratorDecl; +class FunctionDecl; +class LocationContext; +class StackFrameContext; +class Stmt; + +namespace ento { + +class ConditionTruthVal; +class ProgramStateManager; +class StoreRef; + +class SValBuilder { + virtual void anchor(); + +protected: + ASTContext &Context; + + /// Manager of APSInt values. + BasicValueFactory BasicVals; + + /// Manages the creation of symbols. + SymbolManager SymMgr; + + /// Manages the creation of memory regions. + MemRegionManager MemMgr; + + ProgramStateManager &StateMgr; + + /// The scalar type to use for array indices. + const QualType ArrayIndexTy; + + /// The width of the scalar type used for array indices. + const unsigned ArrayIndexWidth; + + virtual SVal evalCastFromNonLoc(NonLoc val, QualType castTy) = 0; + virtual SVal evalCastFromLoc(Loc val, QualType castTy) = 0; + +public: + // FIXME: Make these protected again once RegionStoreManager correctly + // handles loads from different bound value types. + virtual SVal dispatchCast(SVal val, QualType castTy) = 0; + +public: + SValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, + ProgramStateManager &stateMgr) + : Context(context), BasicVals(context, alloc), + SymMgr(context, BasicVals, alloc), MemMgr(context, alloc), + StateMgr(stateMgr), ArrayIndexTy(context.LongLongTy), + ArrayIndexWidth(context.getTypeSize(ArrayIndexTy)) {} + + virtual ~SValBuilder() = default; + + bool haveSameType(const SymExpr *Sym1, const SymExpr *Sym2) { + return haveSameType(Sym1->getType(), Sym2->getType()); + } + + bool haveSameType(QualType Ty1, QualType Ty2) { + // FIXME: Remove the second disjunct when we support symbolic + // truncation/extension. + return (Context.getCanonicalType(Ty1) == Context.getCanonicalType(Ty2) || + (Ty1->isIntegralOrEnumerationType() && + Ty2->isIntegralOrEnumerationType())); + } + + SVal evalCast(SVal val, QualType castTy, QualType originalType); + + // Handles casts of type CK_IntegralCast. + SVal evalIntegralCast(ProgramStateRef state, SVal val, QualType castTy, + QualType originalType); + + virtual SVal evalMinus(NonLoc val) = 0; + + virtual SVal evalComplement(NonLoc val) = 0; + + /// Create a new value which represents a binary expression with two non- + /// location operands. + virtual SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, + NonLoc lhs, NonLoc rhs, QualType resultTy) = 0; + + /// Create a new value which represents a binary expression with two memory + /// location operands. + virtual SVal evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op, + Loc lhs, Loc rhs, QualType resultTy) = 0; + + /// Create a new value which represents a binary expression with a memory + /// location and non-location operands. For example, this would be used to + /// evaluate a pointer arithmetic operation. + virtual SVal evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, + Loc lhs, NonLoc rhs, QualType resultTy) = 0; + + /// Evaluates a given SVal. If the SVal has only one possible (integer) value, + /// that value is returned. Otherwise, returns NULL. + virtual const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal val) = 0; + + /// Simplify symbolic expressions within a given SVal. Return an SVal + /// that represents the same value, but is hopefully easier to work with + /// than the original SVal. + virtual SVal simplifySVal(ProgramStateRef State, SVal Val) = 0; + + /// Constructs a symbolic expression for two non-location values. + SVal makeSymExprValNN(BinaryOperator::Opcode op, + NonLoc lhs, NonLoc rhs, QualType resultTy); + + SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, + SVal lhs, SVal rhs, QualType type); + + /// \return Whether values in \p lhs and \p rhs are equal at \p state. + ConditionTruthVal areEqual(ProgramStateRef state, SVal lhs, SVal rhs); + + SVal evalEQ(ProgramStateRef state, SVal lhs, SVal rhs); + + DefinedOrUnknownSVal evalEQ(ProgramStateRef state, DefinedOrUnknownSVal lhs, + DefinedOrUnknownSVal rhs); + + ASTContext &getContext() { return Context; } + const ASTContext &getContext() const { return Context; } + + ProgramStateManager &getStateManager() { return StateMgr; } + + QualType getConditionType() const { + return Context.getLangOpts().CPlusPlus ? Context.BoolTy : Context.IntTy; + } + + QualType getArrayIndexType() const { + return ArrayIndexTy; + } + + BasicValueFactory &getBasicValueFactory() { return BasicVals; } + const BasicValueFactory &getBasicValueFactory() const { return BasicVals; } + + SymbolManager &getSymbolManager() { return SymMgr; } + const SymbolManager &getSymbolManager() const { return SymMgr; } + + MemRegionManager &getRegionManager() { return MemMgr; } + const MemRegionManager &getRegionManager() const { return MemMgr; } + + // Forwarding methods to SymbolManager. + + const SymbolConjured* conjureSymbol(const Stmt *stmt, + const LocationContext *LCtx, + QualType type, + unsigned visitCount, + const void *symbolTag = nullptr) { + return SymMgr.conjureSymbol(stmt, LCtx, type, visitCount, symbolTag); + } + + const SymbolConjured* conjureSymbol(const Expr *expr, + const LocationContext *LCtx, + unsigned visitCount, + const void *symbolTag = nullptr) { + return SymMgr.conjureSymbol(expr, LCtx, visitCount, symbolTag); + } + + /// Construct an SVal representing '0' for the specified type. + DefinedOrUnknownSVal makeZeroVal(QualType type); + + /// Make a unique symbol for value of region. + DefinedOrUnknownSVal getRegionValueSymbolVal(const TypedValueRegion *region); + + /// Create a new symbol with a unique 'name'. + /// + /// We resort to conjured symbols when we cannot construct a derived symbol. + /// The advantage of symbols derived/built from other symbols is that we + /// preserve the relation between related(or even equivalent) expressions, so + /// conjured symbols should be used sparingly. + DefinedOrUnknownSVal conjureSymbolVal(const void *symbolTag, + const Expr *expr, + const LocationContext *LCtx, + unsigned count); + DefinedOrUnknownSVal conjureSymbolVal(const void *symbolTag, + const Expr *expr, + const LocationContext *LCtx, + QualType type, + unsigned count); + DefinedOrUnknownSVal conjureSymbolVal(const Stmt *stmt, + const LocationContext *LCtx, + QualType type, + unsigned visitCount); + + /// Conjure a symbol representing heap allocated memory region. + /// + /// Note, the expression should represent a location. + DefinedOrUnknownSVal getConjuredHeapSymbolVal(const Expr *E, + const LocationContext *LCtx, + unsigned Count); + + DefinedOrUnknownSVal getDerivedRegionValueSymbolVal( + SymbolRef parentSymbol, const TypedValueRegion *region); + + DefinedSVal getMetadataSymbolVal(const void *symbolTag, + const MemRegion *region, + const Expr *expr, QualType type, + const LocationContext *LCtx, + unsigned count); + + DefinedSVal getMemberPointer(const DeclaratorDecl *DD); + + DefinedSVal getFunctionPointer(const FunctionDecl *func); + + DefinedSVal getBlockPointer(const BlockDecl *block, CanQualType locTy, + const LocationContext *locContext, + unsigned blockCount); + + /// Returns the value of \p E, if it can be determined in a non-path-sensitive + /// manner. + /// + /// If \p E is not a constant or cannot be modeled, returns \c None. + Optional<SVal> getConstantVal(const Expr *E); + + NonLoc makeCompoundVal(QualType type, llvm::ImmutableList<SVal> vals) { + return nonloc::CompoundVal(BasicVals.getCompoundValData(type, vals)); + } + + NonLoc makeLazyCompoundVal(const StoreRef &store, + const TypedValueRegion *region) { + return nonloc::LazyCompoundVal( + BasicVals.getLazyCompoundValData(store, region)); + } + + NonLoc makePointerToMember(const DeclaratorDecl *DD) { + return nonloc::PointerToMember(DD); + } + + NonLoc makePointerToMember(const PointerToMemberData *PTMD) { + return nonloc::PointerToMember(PTMD); + } + + NonLoc makeZeroArrayIndex() { + return nonloc::ConcreteInt(BasicVals.getValue(0, ArrayIndexTy)); + } + + NonLoc makeArrayIndex(uint64_t idx) { + return nonloc::ConcreteInt(BasicVals.getValue(idx, ArrayIndexTy)); + } + + SVal convertToArrayIndex(SVal val); + + nonloc::ConcreteInt makeIntVal(const IntegerLiteral* integer) { + return nonloc::ConcreteInt( + BasicVals.getValue(integer->getValue(), + integer->getType()->isUnsignedIntegerOrEnumerationType())); + } + + nonloc::ConcreteInt makeBoolVal(const ObjCBoolLiteralExpr *boolean) { + return makeTruthVal(boolean->getValue(), boolean->getType()); + } + + nonloc::ConcreteInt makeBoolVal(const CXXBoolLiteralExpr *boolean); + + nonloc::ConcreteInt makeIntVal(const llvm::APSInt& integer) { + return nonloc::ConcreteInt(BasicVals.getValue(integer)); + } + + loc::ConcreteInt makeIntLocVal(const llvm::APSInt &integer) { + return loc::ConcreteInt(BasicVals.getValue(integer)); + } + + NonLoc makeIntVal(const llvm::APInt& integer, bool isUnsigned) { + return nonloc::ConcreteInt(BasicVals.getValue(integer, isUnsigned)); + } + + DefinedSVal makeIntVal(uint64_t integer, QualType type) { + if (Loc::isLocType(type)) + return loc::ConcreteInt(BasicVals.getValue(integer, type)); + + return nonloc::ConcreteInt(BasicVals.getValue(integer, type)); + } + + NonLoc makeIntVal(uint64_t integer, bool isUnsigned) { + return nonloc::ConcreteInt(BasicVals.getIntValue(integer, isUnsigned)); + } + + NonLoc makeIntValWithPtrWidth(uint64_t integer, bool isUnsigned) { + return nonloc::ConcreteInt( + BasicVals.getIntWithPtrWidth(integer, isUnsigned)); + } + + NonLoc makeLocAsInteger(Loc loc, unsigned bits) { + return nonloc::LocAsInteger(BasicVals.getPersistentSValWithData(loc, bits)); + } + + NonLoc makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APSInt& rhs, QualType type); + + NonLoc makeNonLoc(const llvm::APSInt& rhs, BinaryOperator::Opcode op, + const SymExpr *lhs, QualType type); + + NonLoc makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const SymExpr *rhs, QualType type); + + /// Create a NonLoc value for cast. + NonLoc makeNonLoc(const SymExpr *operand, QualType fromTy, QualType toTy); + + nonloc::ConcreteInt makeTruthVal(bool b, QualType type) { + return nonloc::ConcreteInt(BasicVals.getTruthValue(b, type)); + } + + nonloc::ConcreteInt makeTruthVal(bool b) { + return nonloc::ConcreteInt(BasicVals.getTruthValue(b)); + } + + /// Create NULL pointer, with proper pointer bit-width for given address + /// space. + /// \param type pointer type. + Loc makeNullWithType(QualType type) { + return loc::ConcreteInt(BasicVals.getZeroWithTypeSize(type)); + } + + Loc makeNull() { + return loc::ConcreteInt(BasicVals.getZeroWithPtrWidth()); + } + + Loc makeLoc(SymbolRef sym) { + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + } + + Loc makeLoc(const MemRegion* region) { + return loc::MemRegionVal(region); + } + + Loc makeLoc(const AddrLabelExpr *expr) { + return loc::GotoLabel(expr->getLabel()); + } + + Loc makeLoc(const llvm::APSInt& integer) { + return loc::ConcreteInt(BasicVals.getValue(integer)); + } + + /// Make an SVal that represents the given symbol. This follows the convention + /// of representing Loc-type symbols (symbolic pointers and references) + /// as Loc values wrapping the symbol rather than as plain symbol values. + SVal makeSymbolVal(SymbolRef Sym) { + if (Loc::isLocType(Sym->getType())) + return makeLoc(Sym); + return nonloc::SymbolVal(Sym); + } + + /// Return a memory region for the 'this' object reference. + loc::MemRegionVal getCXXThis(const CXXMethodDecl *D, + const StackFrameContext *SFC); + + /// Return a memory region for the 'this' object reference. + loc::MemRegionVal getCXXThis(const CXXRecordDecl *D, + const StackFrameContext *SFC); +}; + +SValBuilder* createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, + ASTContext &context, + ProgramStateManager &stateMgr); + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALBUILDER_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h new file mode 100644 index 00000000..fc83e261 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h @@ -0,0 +1,150 @@ +//===--- SValVisitor.h - Visitor for SVal subclasses ------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the SValVisitor, SymExprVisitor, and MemRegionVisitor +// interfaces, and also FullSValVisitor, which visits all three hierarchies. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALVISITOR_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALVISITOR_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" + +namespace clang { + +namespace ento { + +/// SValVisitor - this class implements a simple visitor for SVal +/// subclasses. +template <typename ImplClass, typename RetTy = void> class SValVisitor { +public: + +#define DISPATCH(NAME, CLASS) \ + return static_cast<ImplClass *>(this)->Visit ## NAME(V.castAs<CLASS>()) + + RetTy Visit(SVal V) { + // Dispatch to VisitFooVal for each FooVal. + // Take namespaces (loc:: and nonloc::) into account. + switch (V.getBaseKind()) { +#define BASIC_SVAL(Id, Parent) case SVal::Id ## Kind: DISPATCH(Id, Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + case SVal::LocKind: + switch (V.getSubKind()) { +#define LOC_SVAL(Id, Parent) \ + case loc::Id ## Kind: DISPATCH(Loc ## Id, loc :: Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + } + llvm_unreachable("Unknown Loc sub-kind!"); + case SVal::NonLocKind: + switch (V.getSubKind()) { +#define NONLOC_SVAL(Id, Parent) \ + case nonloc::Id ## Kind: DISPATCH(NonLoc ## Id, nonloc :: Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + } + llvm_unreachable("Unknown NonLoc sub-kind!"); + } + llvm_unreachable("Unknown SVal kind!"); + } + +#define BASIC_SVAL(Id, Parent) \ + RetTy Visit ## Id(Id V) { DISPATCH(Parent, Id); } +#define ABSTRACT_SVAL(Id, Parent) \ + BASIC_SVAL(Id, Parent) +#define LOC_SVAL(Id, Parent) \ + RetTy VisitLoc ## Id(loc::Id V) { DISPATCH(Parent, Parent); } +#define NONLOC_SVAL(Id, Parent) \ + RetTy VisitNonLoc ## Id(nonloc::Id V) { DISPATCH(Parent, Parent); } +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + + // Base case, ignore it. :) + RetTy VisitSVal(SVal V) { return RetTy(); } + +#undef DISPATCH +}; + +/// SymExprVisitor - this class implements a simple visitor for SymExpr +/// subclasses. +template <typename ImplClass, typename RetTy = void> class SymExprVisitor { +public: + +#define DISPATCH(CLASS) \ + return static_cast<ImplClass *>(this)->Visit ## CLASS(cast<CLASS>(S)) + + RetTy Visit(SymbolRef S) { + // Dispatch to VisitSymbolFoo for each SymbolFoo. + switch (S->getKind()) { +#define SYMBOL(Id, Parent) \ + case SymExpr::Id ## Kind: DISPATCH(Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + } + llvm_unreachable("Unknown SymExpr kind!"); + } + + // If the implementation chooses not to implement a certain visit method, fall + // back on visiting the superclass. +#define SYMBOL(Id, Parent) RetTy Visit ## Id(const Id *S) { DISPATCH(Parent); } +#define ABSTRACT_SYMBOL(Id, Parent) SYMBOL(Id, Parent) +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + + // Base case, ignore it. :) + RetTy VisitSymExpr(SymbolRef S) { return RetTy(); } + +#undef DISPATCH +}; + +/// MemRegionVisitor - this class implements a simple visitor for MemRegion +/// subclasses. +template <typename ImplClass, typename RetTy = void> class MemRegionVisitor { +public: + +#define DISPATCH(CLASS) \ + return static_cast<ImplClass *>(this)->Visit ## CLASS(cast<CLASS>(R)) + + RetTy Visit(const MemRegion *R) { + // Dispatch to VisitFooRegion for each FooRegion. + switch (R->getKind()) { +#define REGION(Id, Parent) case MemRegion::Id ## Kind: DISPATCH(Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + } + llvm_unreachable("Unknown MemRegion kind!"); + } + + // If the implementation chooses not to implement a certain visit method, fall + // back on visiting the superclass. +#define REGION(Id, Parent) \ + RetTy Visit ## Id(const Id *R) { DISPATCH(Parent); } +#define ABSTRACT_REGION(Id, Parent) \ + REGION(Id, Parent) +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + + // Base case, ignore it. :) + RetTy VisitMemRegion(const MemRegion *R) { return RetTy(); } + +#undef DISPATCH +}; + +/// FullSValVisitor - a convenient mixed visitor for all three: +/// SVal, SymExpr and MemRegion subclasses. +template <typename ImplClass, typename RetTy = void> +class FullSValVisitor : public SValVisitor<ImplClass, RetTy>, + public SymExprVisitor<ImplClass, RetTy>, + public MemRegionVisitor<ImplClass, RetTy> { +public: + using SValVisitor<ImplClass, RetTy>::Visit; + using SymExprVisitor<ImplClass, RetTy>::Visit; + using MemRegionVisitor<ImplClass, RetTy>::Visit; +}; + +} // end namespace ento + +} // end namespace clang + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def new file mode 100644 index 00000000..eb05de6d --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def @@ -0,0 +1,74 @@ +//===-- SVals.def - Metadata about SVal kinds -------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// The list of symbolic values (SVal kinds and sub-kinds) used in the Static +// Analyzer. The distinction between loc:: and nonloc:: SVal namespaces is +// currently hardcoded, because it is too peculiar and explicit to be handled +// uniformly. In order to use this information, users of this file must define +// one or more of the following macros: +// +// BASIC_SVAL(Id, Parent) - for specific SVal sub-kinds, which are +// neither in loc:: nor in nonloc:: namespace; these classes occupy +// their own base kind IdKind. +// +// ABSTRACT_SVAL(Id, Parent) - for abstract SVal classes which are +// neither in loc:: nor in nonloc:: namespace, +// +// ABSTRACT_SVAL_WITH_KIND(Id, Parent) - for SVal classes which are also +// neither in loc:: nor in nonloc:: namespace, but occupy a whole base kind +// identifier IdKind, much like BASIC_SVALs. +// +// LOC_SVAL(Id, Parent) - for values in loc:: namespace, which occupy a sub-kind +// loc::IdKind. +// +// NONLOC_SVAL(Id, Parent) - for values in nonloc:: namespace, which occupy a +// sub-kind nonloc::IdKind. +// +//===----------------------------------------------------------------------===// + +#ifndef BASIC_SVAL +#define BASIC_SVAL(Id, Parent) +#endif + +#ifndef ABSTRACT_SVAL +#define ABSTRACT_SVAL(Id, Parent) +#endif + +#ifndef ABSTRACT_SVAL_WITH_KIND +#define ABSTRACT_SVAL_WITH_KIND(Id, Parent) ABSTRACT_SVAL(Id, Parent) +#endif + +#ifndef LOC_SVAL +#define LOC_SVAL(Id, Parent) +#endif + +#ifndef NONLOC_SVAL +#define NONLOC_SVAL(Id, Parent) +#endif + +BASIC_SVAL(UndefinedVal, SVal) +ABSTRACT_SVAL(DefinedOrUnknownSVal, SVal) + BASIC_SVAL(UnknownVal, DefinedOrUnknownSVal) + ABSTRACT_SVAL(DefinedSVal, DefinedOrUnknownSVal) + ABSTRACT_SVAL_WITH_KIND(Loc, DefinedSVal) + LOC_SVAL(ConcreteInt, Loc) + LOC_SVAL(GotoLabel, Loc) + LOC_SVAL(MemRegionVal, Loc) + ABSTRACT_SVAL_WITH_KIND(NonLoc, DefinedSVal) + NONLOC_SVAL(CompoundVal, NonLoc) + NONLOC_SVAL(ConcreteInt, NonLoc) + NONLOC_SVAL(LazyCompoundVal, NonLoc) + NONLOC_SVAL(LocAsInteger, NonLoc) + NONLOC_SVAL(SymbolVal, NonLoc) + NONLOC_SVAL(PointerToMember, NonLoc) + +#undef NONLOC_SVAL +#undef LOC_SVAL +#undef ABSTRACT_SVAL_WITH_KIND +#undef ABSTRACT_SVAL +#undef BASIC_SVAL diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h new file mode 100644 index 00000000..e8599366 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -0,0 +1,670 @@ +//===- SVals.h - Abstract Values for Static Analysis ------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines SVal, Loc, and NonLoc, classes that represent +// abstract r-values for use with path-sensitive value tracking. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H + +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/Support/Casting.h" +#include <cassert> +#include <cstdint> +#include <utility> + +//==------------------------------------------------------------------------==// +// Base SVal types. +//==------------------------------------------------------------------------==// + +namespace clang { + +class CXXBaseSpecifier; +class DeclaratorDecl; +class FunctionDecl; +class LabelDecl; + +namespace ento { + +class BasicValueFactory; +class CompoundValData; +class LazyCompoundValData; +class MemRegion; +class PointerToMemberData; +class SValBuilder; +class TypedValueRegion; + +namespace nonloc { + +/// Sub-kinds for NonLoc values. +enum Kind { +#define NONLOC_SVAL(Id, Parent) Id ## Kind, +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" +}; + +} // namespace nonloc + +namespace loc { + +/// Sub-kinds for Loc values. +enum Kind { +#define LOC_SVAL(Id, Parent) Id ## Kind, +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" +}; + +} // namespace loc + +/// SVal - This represents a symbolic expression, which can be either +/// an L-value or an R-value. +/// +class SVal { +public: + enum BaseKind { + // The enumerators must be representable using 2 bits. +#define BASIC_SVAL(Id, Parent) Id ## Kind, +#define ABSTRACT_SVAL_WITH_KIND(Id, Parent) Id ## Kind, +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + }; + enum { BaseBits = 2, BaseMask = 0x3 }; + +protected: + const void *Data = nullptr; + + /// The lowest 2 bits are a BaseKind (0 -- 3). + /// The higher bits are an unsigned "kind" value. + unsigned Kind = 0; + + explicit SVal(const void *d, bool isLoc, unsigned ValKind) + : Data(d), Kind((isLoc ? LocKind : NonLocKind) | (ValKind << BaseBits)) {} + + explicit SVal(BaseKind k, const void *D = nullptr) : Data(D), Kind(k) {} + +public: + explicit SVal() = default; + + /// Convert to the specified SVal type, asserting that this SVal is of + /// the desired type. + template<typename T> + T castAs() const { + assert(T::isKind(*this)); + return *static_cast<const T *>(this); + } + + /// Convert to the specified SVal type, returning None if this SVal is + /// not of the desired type. + template<typename T> + Optional<T> getAs() const { + if (!T::isKind(*this)) + return None; + return *static_cast<const T *>(this); + } + + unsigned getRawKind() const { return Kind; } + BaseKind getBaseKind() const { return (BaseKind) (Kind & BaseMask); } + unsigned getSubKind() const { return (Kind & ~BaseMask) >> BaseBits; } + + // This method is required for using SVal in a FoldingSetNode. It + // extracts a unique signature for this SVal object. + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger((unsigned) getRawKind()); + ID.AddPointer(Data); + } + + bool operator==(const SVal &R) const { + return getRawKind() == R.getRawKind() && Data == R.Data; + } + + bool operator!=(const SVal &R) const { + return !(*this == R); + } + + bool isUnknown() const { + return getRawKind() == UnknownValKind; + } + + bool isUndef() const { + return getRawKind() == UndefinedValKind; + } + + bool isUnknownOrUndef() const { + return getRawKind() <= UnknownValKind; + } + + bool isValid() const { + return getRawKind() > UnknownValKind; + } + + bool isConstant() const; + + bool isConstant(int I) const; + + bool isZeroConstant() const; + + /// hasConjuredSymbol - If this SVal wraps a conjured symbol, return true; + bool hasConjuredSymbol() const; + + /// getAsFunctionDecl - If this SVal is a MemRegionVal and wraps a + /// CodeTextRegion wrapping a FunctionDecl, return that FunctionDecl. + /// Otherwise return 0. + const FunctionDecl *getAsFunctionDecl() const; + + /// If this SVal is a location and wraps a symbol, return that + /// SymbolRef. Otherwise return 0. + /// + /// Casts are ignored during lookup. + /// \param IncludeBaseRegions The boolean that controls whether the search + /// should continue to the base regions if the region is not symbolic. + SymbolRef getAsLocSymbol(bool IncludeBaseRegions = false) const; + + /// Get the symbol in the SVal or its base region. + SymbolRef getLocSymbolInBase() const; + + /// If this SVal wraps a symbol return that SymbolRef. + /// Otherwise, return 0. + /// + /// Casts are ignored during lookup. + /// \param IncludeBaseRegions The boolean that controls whether the search + /// should continue to the base regions if the region is not symbolic. + SymbolRef getAsSymbol(bool IncludeBaseRegions = false) const; + + /// getAsSymbolicExpression - If this Sval wraps a symbolic expression then + /// return that expression. Otherwise return NULL. + const SymExpr *getAsSymbolicExpression() const; + + const SymExpr *getAsSymExpr() const; + + const MemRegion *getAsRegion() const; + + void dumpToStream(raw_ostream &OS) const; + void dump() const; + + SymExpr::symbol_iterator symbol_begin() const { + const SymExpr *SE = getAsSymbol(/*IncludeBaseRegions=*/true); + if (SE) + return SE->symbol_begin(); + else + return SymExpr::symbol_iterator(); + } + + SymExpr::symbol_iterator symbol_end() const { + return SymExpr::symbol_end(); + } +}; + +inline raw_ostream &operator<<(raw_ostream &os, clang::ento::SVal V) { + V.dumpToStream(os); + return os; +} + +class UndefinedVal : public SVal { +public: + UndefinedVal() : SVal(UndefinedValKind) {} + +private: + friend class SVal; + + static bool isKind(const SVal& V) { + return V.getBaseKind() == UndefinedValKind; + } +}; + +class DefinedOrUnknownSVal : public SVal { +public: + // We want calling these methods to be a compiler error since they are + // tautologically false. + bool isUndef() const = delete; + bool isValid() const = delete; + +protected: + DefinedOrUnknownSVal() = default; + explicit DefinedOrUnknownSVal(const void *d, bool isLoc, unsigned ValKind) + : SVal(d, isLoc, ValKind) {} + explicit DefinedOrUnknownSVal(BaseKind k, void *D = nullptr) : SVal(k, D) {} + +private: + friend class SVal; + + static bool isKind(const SVal& V) { + return !V.isUndef(); + } +}; + +class UnknownVal : public DefinedOrUnknownSVal { +public: + explicit UnknownVal() : DefinedOrUnknownSVal(UnknownValKind) {} + +private: + friend class SVal; + + static bool isKind(const SVal &V) { + return V.getBaseKind() == UnknownValKind; + } +}; + +class DefinedSVal : public DefinedOrUnknownSVal { +public: + // We want calling these methods to be a compiler error since they are + // tautologically true/false. + bool isUnknown() const = delete; + bool isUnknownOrUndef() const = delete; + bool isValid() const = delete; + +protected: + DefinedSVal() = default; + explicit DefinedSVal(const void *d, bool isLoc, unsigned ValKind) + : DefinedOrUnknownSVal(d, isLoc, ValKind) {} + +private: + friend class SVal; + + static bool isKind(const SVal& V) { + return !V.isUnknownOrUndef(); + } +}; + +/// Represents an SVal that is guaranteed to not be UnknownVal. +class KnownSVal : public SVal { + friend class SVal; + + KnownSVal() = default; + + static bool isKind(const SVal &V) { + return !V.isUnknown(); + } + +public: + KnownSVal(const DefinedSVal &V) : SVal(V) {} + KnownSVal(const UndefinedVal &V) : SVal(V) {} +}; + +class NonLoc : public DefinedSVal { +protected: + NonLoc() = default; + explicit NonLoc(unsigned SubKind, const void *d) + : DefinedSVal(d, false, SubKind) {} + +public: + void dumpToStream(raw_ostream &Out) const; + + static bool isCompoundType(QualType T) { + return T->isArrayType() || T->isRecordType() || + T->isComplexType() || T->isVectorType(); + } + +private: + friend class SVal; + + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind; + } +}; + +class Loc : public DefinedSVal { +protected: + Loc() = default; + explicit Loc(unsigned SubKind, const void *D) + : DefinedSVal(const_cast<void *>(D), true, SubKind) {} + +public: + void dumpToStream(raw_ostream &Out) const; + + static bool isLocType(QualType T) { + return T->isAnyPointerType() || T->isBlockPointerType() || + T->isReferenceType() || T->isNullPtrType(); + } + +private: + friend class SVal; + + static bool isKind(const SVal& V) { + return V.getBaseKind() == LocKind; + } +}; + +//==------------------------------------------------------------------------==// +// Subclasses of NonLoc. +//==------------------------------------------------------------------------==// + +namespace nonloc { + +/// Represents symbolic expression that isn't a location. +class SymbolVal : public NonLoc { +public: + SymbolVal() = delete; + SymbolVal(SymbolRef sym) : NonLoc(SymbolValKind, sym) { + assert(sym); + assert(!Loc::isLocType(sym->getType())); + } + + SymbolRef getSymbol() const { + return (const SymExpr *) Data; + } + + bool isExpression() const { + return !isa<SymbolData>(getSymbol()); + } + +private: + friend class SVal; + + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && + V.getSubKind() == SymbolValKind; + } + + static bool isKind(const NonLoc& V) { + return V.getSubKind() == SymbolValKind; + } +}; + +/// Value representing integer constant. +class ConcreteInt : public NonLoc { +public: + explicit ConcreteInt(const llvm::APSInt& V) : NonLoc(ConcreteIntKind, &V) {} + + const llvm::APSInt& getValue() const { + return *static_cast<const llvm::APSInt *>(Data); + } + + // Transfer functions for binary/unary operations on ConcreteInts. + SVal evalBinOp(SValBuilder &svalBuilder, BinaryOperator::Opcode Op, + const ConcreteInt& R) const; + + ConcreteInt evalComplement(SValBuilder &svalBuilder) const; + + ConcreteInt evalMinus(SValBuilder &svalBuilder) const; + +private: + friend class SVal; + + ConcreteInt() = default; + + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && + V.getSubKind() == ConcreteIntKind; + } + + static bool isKind(const NonLoc& V) { + return V.getSubKind() == ConcreteIntKind; + } +}; + +class LocAsInteger : public NonLoc { + friend class ento::SValBuilder; + + explicit LocAsInteger(const std::pair<SVal, uintptr_t> &data) + : NonLoc(LocAsIntegerKind, &data) { + // We do not need to represent loc::ConcreteInt as LocAsInteger, + // as it'd collapse into a nonloc::ConcreteInt instead. + assert(data.first.getBaseKind() == LocKind && + (data.first.getSubKind() == loc::MemRegionValKind || + data.first.getSubKind() == loc::GotoLabelKind)); + } + +public: + Loc getLoc() const { + const std::pair<SVal, uintptr_t> *D = + static_cast<const std::pair<SVal, uintptr_t> *>(Data); + return D->first.castAs<Loc>(); + } + + Loc getPersistentLoc() const { + const std::pair<SVal, uintptr_t> *D = + static_cast<const std::pair<SVal, uintptr_t> *>(Data); + const SVal& V = D->first; + return V.castAs<Loc>(); + } + + unsigned getNumBits() const { + const std::pair<SVal, uintptr_t> *D = + static_cast<const std::pair<SVal, uintptr_t> *>(Data); + return D->second; + } + +private: + friend class SVal; + + LocAsInteger() = default; + + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && + V.getSubKind() == LocAsIntegerKind; + } + + static bool isKind(const NonLoc& V) { + return V.getSubKind() == LocAsIntegerKind; + } +}; + +class CompoundVal : public NonLoc { + friend class ento::SValBuilder; + + explicit CompoundVal(const CompoundValData* D) : NonLoc(CompoundValKind, D) {} + +public: + const CompoundValData* getValue() const { + return static_cast<const CompoundValData *>(Data); + } + + using iterator = llvm::ImmutableList<SVal>::iterator; + + iterator begin() const; + iterator end() const; + +private: + friend class SVal; + + CompoundVal() = default; + + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && V.getSubKind() == CompoundValKind; + } + + static bool isKind(const NonLoc& V) { + return V.getSubKind() == CompoundValKind; + } +}; + +class LazyCompoundVal : public NonLoc { + friend class ento::SValBuilder; + + explicit LazyCompoundVal(const LazyCompoundValData *D) + : NonLoc(LazyCompoundValKind, D) {} + +public: + const LazyCompoundValData *getCVData() const { + return static_cast<const LazyCompoundValData *>(Data); + } + + const void *getStore() const; + const TypedValueRegion *getRegion() const; + +private: + friend class SVal; + + LazyCompoundVal() = default; + + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && + V.getSubKind() == LazyCompoundValKind; + } + + static bool isKind(const NonLoc& V) { + return V.getSubKind() == LazyCompoundValKind; + } +}; + +/// Value representing pointer-to-member. +/// +/// This value is qualified as NonLoc because neither loading nor storing +/// operations are applied to it. Instead, the analyzer uses the L-value coming +/// from pointer-to-member applied to an object. +/// This SVal is represented by a DeclaratorDecl which can be a member function +/// pointer or a member data pointer and a list of CXXBaseSpecifiers. This list +/// is required to accumulate the pointer-to-member cast history to figure out +/// the correct subobject field. +class PointerToMember : public NonLoc { + friend class ento::SValBuilder; + +public: + using PTMDataType = + llvm::PointerUnion<const DeclaratorDecl *, const PointerToMemberData *>; + + const PTMDataType getPTMData() const { + return PTMDataType::getFromOpaqueValue(const_cast<void *>(Data)); + } + + bool isNullMemberPointer() const; + + const DeclaratorDecl *getDecl() const; + + template<typename AdjustedDecl> + const AdjustedDecl *getDeclAs() const { + return dyn_cast_or_null<AdjustedDecl>(getDecl()); + } + + using iterator = llvm::ImmutableList<const CXXBaseSpecifier *>::iterator; + + iterator begin() const; + iterator end() const; + +private: + friend class SVal; + + PointerToMember() = default; + explicit PointerToMember(const PTMDataType D) + : NonLoc(PointerToMemberKind, D.getOpaqueValue()) {} + + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && + V.getSubKind() == PointerToMemberKind; + } + + static bool isKind(const NonLoc& V) { + return V.getSubKind() == PointerToMemberKind; + } +}; + +} // namespace nonloc + +//==------------------------------------------------------------------------==// +// Subclasses of Loc. +//==------------------------------------------------------------------------==// + +namespace loc { + +class GotoLabel : public Loc { +public: + explicit GotoLabel(const LabelDecl *Label) : Loc(GotoLabelKind, Label) { + assert(Label); + } + + const LabelDecl *getLabel() const { + return static_cast<const LabelDecl *>(Data); + } + +private: + friend class SVal; + + GotoLabel() = default; + + static bool isKind(const SVal& V) { + return V.getBaseKind() == LocKind && V.getSubKind() == GotoLabelKind; + } + + static bool isKind(const Loc& V) { + return V.getSubKind() == GotoLabelKind; + } +}; + +class MemRegionVal : public Loc { +public: + explicit MemRegionVal(const MemRegion* r) : Loc(MemRegionValKind, r) { + assert(r); + } + + /// Get the underlining region. + const MemRegion *getRegion() const { + return static_cast<const MemRegion *>(Data); + } + + /// Get the underlining region and strip casts. + const MemRegion* stripCasts(bool StripBaseCasts = true) const; + + template <typename REGION> + const REGION* getRegionAs() const { + return dyn_cast<REGION>(getRegion()); + } + + bool operator==(const MemRegionVal &R) const { + return getRegion() == R.getRegion(); + } + + bool operator!=(const MemRegionVal &R) const { + return getRegion() != R.getRegion(); + } + +private: + friend class SVal; + + MemRegionVal() = default; + + static bool isKind(const SVal& V) { + return V.getBaseKind() == LocKind && + V.getSubKind() == MemRegionValKind; + } + + static bool isKind(const Loc& V) { + return V.getSubKind() == MemRegionValKind; + } +}; + +class ConcreteInt : public Loc { +public: + explicit ConcreteInt(const llvm::APSInt& V) : Loc(ConcreteIntKind, &V) {} + + const llvm::APSInt &getValue() const { + return *static_cast<const llvm::APSInt *>(Data); + } + + // Transfer functions for binary/unary operations on ConcreteInts. + SVal evalBinOp(BasicValueFactory& BasicVals, BinaryOperator::Opcode Op, + const ConcreteInt& R) const; + +private: + friend class SVal; + + ConcreteInt() = default; + + static bool isKind(const SVal& V) { + return V.getBaseKind() == LocKind && + V.getSubKind() == ConcreteIntKind; + } + + static bool isKind(const Loc& V) { + return V.getSubKind() == ConcreteIntKind; + } +}; + +} // namespace loc + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h new file mode 100644 index 00000000..6bf5e94a --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h @@ -0,0 +1,92 @@ +//== SimpleConstraintManager.h ----------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Simplified constraint manager backend. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SIMPLECONSTRAINTMANAGER_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SIMPLECONSTRAINTMANAGER_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" + +namespace clang { + +namespace ento { + +class SimpleConstraintManager : public ConstraintManager { + SubEngine *SU; + SValBuilder &SVB; + +public: + SimpleConstraintManager(SubEngine *subengine, SValBuilder &SB) + : SU(subengine), SVB(SB) {} + + ~SimpleConstraintManager() override; + + //===------------------------------------------------------------------===// + // Implementation for interface from ConstraintManager. + //===------------------------------------------------------------------===// + + /// Ensures that the DefinedSVal conditional is expressed as a NonLoc by + /// creating boolean casts to handle Loc's. + ProgramStateRef assume(ProgramStateRef State, DefinedSVal Cond, + bool Assumption) override; + + ProgramStateRef assumeInclusiveRange(ProgramStateRef State, NonLoc Value, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange) override; + +protected: + //===------------------------------------------------------------------===// + // Interface that subclasses must implement. + //===------------------------------------------------------------------===// + + /// Given a symbolic expression that can be reasoned about, assume that it is + /// true/false and generate the new program state. + virtual ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym, + bool Assumption) = 0; + + /// Given a symbolic expression within the range [From, To], assume that it is + /// true/false and generate the new program state. + /// This function is used to handle case ranges produced by a language + /// extension for switch case statements. + virtual ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, + SymbolRef Sym, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange) = 0; + + /// Given a symbolic expression that cannot be reasoned about, assume that + /// it is zero/nonzero and add it directly to the solver state. + virtual ProgramStateRef assumeSymUnsupported(ProgramStateRef State, + SymbolRef Sym, + bool Assumption) = 0; + + //===------------------------------------------------------------------===// + // Internal implementation. + //===------------------------------------------------------------------===// + + SValBuilder &getSValBuilder() const { return SVB; } + BasicValueFactory &getBasicVals() const { return SVB.getBasicValueFactory(); } + SymbolManager &getSymbolManager() const { return SVB.getSymbolManager(); } + +private: + ProgramStateRef assume(ProgramStateRef State, NonLoc Cond, bool Assumption); + + ProgramStateRef assumeAux(ProgramStateRef State, NonLoc Cond, + bool Assumption); +}; + +} // end namespace ento + +} // end namespace clang + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h new file mode 100644 index 00000000..17736833 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h @@ -0,0 +1,338 @@ +//===- Store.h - Interface for maps from Locations to Values ----*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defined the types Store and StoreManager. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_STORE_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_STORE_H + +#include "clang/AST/Type.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include <cassert> +#include <cstdint> +#include <memory> + +namespace clang { + +class ASTContext; +class CastExpr; +class CompoundLiteralExpr; +class CXXBasePath; +class Decl; +class Expr; +class LocationContext; +class ObjCIvarDecl; +class StackFrameContext; + +namespace ento { + +class CallEvent; +class ProgramStateManager; +class ScanReachableSymbols; +class SymbolReaper; + +using InvalidatedSymbols = llvm::DenseSet<SymbolRef>; + +class StoreManager { +protected: + SValBuilder &svalBuilder; + ProgramStateManager &StateMgr; + + /// MRMgr - Manages region objects associated with this StoreManager. + MemRegionManager &MRMgr; + ASTContext &Ctx; + + StoreManager(ProgramStateManager &stateMgr); + +public: + virtual ~StoreManager() = default; + + /// Return the value bound to specified location in a given state. + /// \param[in] store The store in which to make the lookup. + /// \param[in] loc The symbolic memory location. + /// \param[in] T An optional type that provides a hint indicating the + /// expected type of the returned value. This is used if the value is + /// lazily computed. + /// \return The value bound to the location \c loc. + virtual SVal getBinding(Store store, Loc loc, QualType T = QualType()) = 0; + + /// Return the default value bound to a region in a given store. The default + /// binding is the value of sub-regions that were not initialized separately + /// from their base region. For example, if the structure is zero-initialized + /// upon construction, this method retrieves the concrete zero value, even if + /// some or all fields were later overwritten manually. Default binding may be + /// an unknown, undefined, concrete, or symbolic value. + /// \param[in] store The store in which to make the lookup. + /// \param[in] R The region to find the default binding for. + /// \return The default value bound to the region in the store, if a default + /// binding exists. + virtual Optional<SVal> getDefaultBinding(Store store, const MemRegion *R) = 0; + + /// Return the default value bound to a LazyCompoundVal. The default binding + /// is used to represent the value of any fields or elements within the + /// structure represented by the LazyCompoundVal which were not initialized + /// explicitly separately from the whole structure. Default binding may be an + /// unknown, undefined, concrete, or symbolic value. + /// \param[in] lcv The lazy compound value. + /// \return The default value bound to the LazyCompoundVal \c lcv, if a + /// default binding exists. + Optional<SVal> getDefaultBinding(nonloc::LazyCompoundVal lcv) { + return getDefaultBinding(lcv.getStore(), lcv.getRegion()); + } + + /// Return a store with the specified value bound to the given location. + /// \param[in] store The store in which to make the binding. + /// \param[in] loc The symbolic memory location. + /// \param[in] val The value to bind to location \c loc. + /// \return A StoreRef object that contains the same + /// bindings as \c store with the addition of having the value specified + /// by \c val bound to the location given for \c loc. + virtual StoreRef Bind(Store store, Loc loc, SVal val) = 0; + + /// Return a store with the specified value bound to all sub-regions of the + /// region. The region must not have previous bindings. If you need to + /// invalidate existing bindings, consider invalidateRegions(). + virtual StoreRef BindDefaultInitial(Store store, const MemRegion *R, + SVal V) = 0; + + /// Return a store with in which all values within the given region are + /// reset to zero. This method is allowed to overwrite previous bindings. + virtual StoreRef BindDefaultZero(Store store, const MemRegion *R) = 0; + + /// Create a new store with the specified binding removed. + /// \param ST the original store, that is the basis for the new store. + /// \param L the location whose binding should be removed. + virtual StoreRef killBinding(Store ST, Loc L) = 0; + + /// getInitialStore - Returns the initial "empty" store representing the + /// value bindings upon entry to an analyzed function. + virtual StoreRef getInitialStore(const LocationContext *InitLoc) = 0; + + /// getRegionManager - Returns the internal RegionManager object that is + /// used to query and manipulate MemRegion objects. + MemRegionManager& getRegionManager() { return MRMgr; } + + SValBuilder& getSValBuilder() { return svalBuilder; } + + virtual Loc getLValueVar(const VarDecl *VD, const LocationContext *LC) { + return svalBuilder.makeLoc(MRMgr.getVarRegion(VD, LC)); + } + + Loc getLValueCompoundLiteral(const CompoundLiteralExpr *CL, + const LocationContext *LC) { + return loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)); + } + + virtual SVal getLValueIvar(const ObjCIvarDecl *decl, SVal base); + + virtual SVal getLValueField(const FieldDecl *D, SVal Base) { + return getLValueFieldOrIvar(D, Base); + } + + virtual SVal getLValueElement(QualType elementType, NonLoc offset, SVal Base); + + // FIXME: This should soon be eliminated altogether; clients should deal with + // region extents directly. + virtual DefinedOrUnknownSVal getSizeInElements(ProgramStateRef state, + const MemRegion *region, + QualType EleTy) { + return UnknownVal(); + } + + /// ArrayToPointer - Used by ExprEngine::VistCast to handle implicit + /// conversions between arrays and pointers. + virtual SVal ArrayToPointer(Loc Array, QualType ElementTy) = 0; + + /// Evaluates a chain of derived-to-base casts through the path specified in + /// \p Cast. + SVal evalDerivedToBase(SVal Derived, const CastExpr *Cast); + + /// Evaluates a chain of derived-to-base casts through the specified path. + SVal evalDerivedToBase(SVal Derived, const CXXBasePath &CastPath); + + /// Evaluates a derived-to-base cast through a single level of derivation. + SVal evalDerivedToBase(SVal Derived, QualType DerivedPtrType, + bool IsVirtual); + + /// Attempts to do a down cast. Used to model BaseToDerived and C++ + /// dynamic_cast. + /// The callback may result in the following 3 scenarios: + /// - Successful cast (ex: derived is subclass of base). + /// - Failed cast (ex: derived is definitely not a subclass of base). + /// The distinction of this case from the next one is necessary to model + /// dynamic_cast. + /// - We don't know (base is a symbolic region and we don't have + /// enough info to determine if the cast will succeed at run time). + /// The function returns an SVal representing the derived class; it's + /// valid only if Failed flag is set to false. + SVal attemptDownCast(SVal Base, QualType DerivedPtrType, bool &Failed); + + const ElementRegion *GetElementZeroRegion(const SubRegion *R, QualType T); + + /// castRegion - Used by ExprEngine::VisitCast to handle casts from + /// a MemRegion* to a specific location type. 'R' is the region being + /// casted and 'CastToTy' the result type of the cast. + const MemRegion *castRegion(const MemRegion *region, QualType CastToTy); + + virtual StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, + SymbolReaper &SymReaper) = 0; + + virtual bool includedInBindings(Store store, + const MemRegion *region) const = 0; + + /// If the StoreManager supports it, increment the reference count of + /// the specified Store object. + virtual void incrementReferenceCount(Store store) {} + + /// If the StoreManager supports it, decrement the reference count of + /// the specified Store object. If the reference count hits 0, the memory + /// associated with the object is recycled. + virtual void decrementReferenceCount(Store store) {} + + using InvalidatedRegions = SmallVector<const MemRegion *, 8>; + + /// invalidateRegions - Clears out the specified regions from the store, + /// marking their values as unknown. Depending on the store, this may also + /// invalidate additional regions that may have changed based on accessing + /// the given regions. Optionally, invalidates non-static globals as well. + /// \param[in] store The initial store + /// \param[in] Values The values to invalidate. + /// \param[in] E The current statement being evaluated. Used to conjure + /// symbols to mark the values of invalidated regions. + /// \param[in] Count The current block count. Used to conjure + /// symbols to mark the values of invalidated regions. + /// \param[in] Call The call expression which will be used to determine which + /// globals should get invalidated. + /// \param[in,out] IS A set to fill with any symbols that are no longer + /// accessible. Pass \c NULL if this information will not be used. + /// \param[in] ITraits Information about invalidation for a particular + /// region/symbol. + /// \param[in,out] InvalidatedTopLevel A vector to fill with regions + //// explicitly being invalidated. Pass \c NULL if this + /// information will not be used. + /// \param[in,out] Invalidated A vector to fill with any regions being + /// invalidated. This should include any regions explicitly invalidated + /// even if they do not currently have bindings. Pass \c NULL if this + /// information will not be used. + virtual StoreRef invalidateRegions(Store store, + ArrayRef<SVal> Values, + const Expr *E, unsigned Count, + const LocationContext *LCtx, + const CallEvent *Call, + InvalidatedSymbols &IS, + RegionAndSymbolInvalidationTraits &ITraits, + InvalidatedRegions *InvalidatedTopLevel, + InvalidatedRegions *Invalidated) = 0; + + /// enterStackFrame - Let the StoreManager to do something when execution + /// engine is about to execute into a callee. + StoreRef enterStackFrame(Store store, + const CallEvent &Call, + const StackFrameContext *CalleeCtx); + + /// Finds the transitive closure of symbols within the given region. + /// + /// Returns false if the visitor aborted the scan. + virtual bool scanReachableSymbols(Store S, const MemRegion *R, + ScanReachableSymbols &Visitor) = 0; + + virtual void print(Store store, raw_ostream &Out, const char* nl) = 0; + + class BindingsHandler { + public: + virtual ~BindingsHandler(); + + /// \return whether the iteration should continue. + virtual bool HandleBinding(StoreManager& SMgr, Store store, + const MemRegion *region, SVal val) = 0; + }; + + class FindUniqueBinding : public BindingsHandler { + SymbolRef Sym; + const MemRegion* Binding = nullptr; + bool First = true; + + public: + FindUniqueBinding(SymbolRef sym) : Sym(sym) {} + + explicit operator bool() { return First && Binding; } + + bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, + SVal val) override; + const MemRegion *getRegion() { return Binding; } + }; + + /// iterBindings - Iterate over the bindings in the Store. + virtual void iterBindings(Store store, BindingsHandler& f) = 0; + +protected: + const ElementRegion *MakeElementRegion(const SubRegion *baseRegion, + QualType pointeeTy, + uint64_t index = 0); + + /// CastRetrievedVal - Used by subclasses of StoreManager to implement + /// implicit casts that arise from loads from regions that are reinterpreted + /// as another region. + SVal CastRetrievedVal(SVal val, const TypedValueRegion *region, + QualType castTy); + +private: + SVal getLValueFieldOrIvar(const Decl *decl, SVal base); +}; + +inline StoreRef::StoreRef(Store store, StoreManager & smgr) + : store(store), mgr(smgr) { + if (store) + mgr.incrementReferenceCount(store); +} + +inline StoreRef::StoreRef(const StoreRef &sr) + : store(sr.store), mgr(sr.mgr) +{ + if (store) + mgr.incrementReferenceCount(store); +} + +inline StoreRef::~StoreRef() { + if (store) + mgr.decrementReferenceCount(store); +} + +inline StoreRef &StoreRef::operator=(StoreRef const &newStore) { + assert(&newStore.mgr == &mgr); + if (store != newStore.store) { + mgr.incrementReferenceCount(newStore.store); + mgr.decrementReferenceCount(store); + store = newStore.getStore(); + } + return *this; +} + +// FIXME: Do we need to pass ProgramStateManager anymore? +std::unique_ptr<StoreManager> +CreateRegionStoreManager(ProgramStateManager &StMgr); +std::unique_ptr<StoreManager> +CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr); + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_STORE_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h new file mode 100644 index 00000000..a2dd05cf --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h @@ -0,0 +1,53 @@ +//===- StoreRef.h - Smart pointer for store objects -------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defined the type StoreRef. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_STOREREF_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_STOREREF_H + +#include <cassert> + +namespace clang { +namespace ento { + +class StoreManager; + +/// Store - This opaque type encapsulates an immutable mapping from +/// locations to values. At a high-level, it represents the symbolic +/// memory model. Different subclasses of StoreManager may choose +/// different types to represent the locations and values. +using Store = const void *; + +class StoreRef { + Store store; + StoreManager &mgr; + +public: + StoreRef(Store store, StoreManager &smgr); + StoreRef(const StoreRef &sr); + StoreRef &operator=(StoreRef const &newStore); + ~StoreRef(); + + bool operator==(const StoreRef &x) const { + assert(&mgr == &x.mgr); + return x.store == store; + } + + bool operator!=(const StoreRef &x) const { return !operator==(x); } + + Store getStore() const { return store; } + const StoreManager &getStoreManager() const { return mgr; } +}; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_STOREREF_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h new file mode 100644 index 00000000..9296e17c --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h @@ -0,0 +1,175 @@ +//== SubEngine.h - Interface of the subengine of CoreEngine --------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the interface of a subengine of the CoreEngine. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SUBENGINE_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SUBENGINE_H + +#include "clang/Analysis/ProgramPoint.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" + +namespace clang { + +class CFGBlock; +class CFGElement; +class LocationContext; +class Stmt; + +namespace cross_tu { +class CrossTranslationUnitContext; +} + +namespace ento { + +struct NodeBuilderContext; +class AnalysisManager; +class ExplodedNodeSet; +class ExplodedNode; +class ProgramState; +class ProgramStateManager; +class BlockCounter; +class BranchNodeBuilder; +class IndirectGotoNodeBuilder; +class SwitchNodeBuilder; +class EndOfFunctionNodeBuilder; +class NodeBuilderWithSinks; +class MemRegion; + +class SubEngine { + virtual void anchor(); +public: + virtual ~SubEngine() {} + + virtual ProgramStateRef getInitialState(const LocationContext *InitLoc) = 0; + + virtual AnalysisManager &getAnalysisManager() = 0; + + virtual cross_tu::CrossTranslationUnitContext * + getCrossTranslationUnitContext() = 0; + + virtual ProgramStateManager &getStateManager() = 0; + + /// Called by CoreEngine. Used to generate new successor + /// nodes by processing the 'effects' of a block-level statement. + virtual void processCFGElement(const CFGElement E, ExplodedNode* Pred, + unsigned StmtIdx, NodeBuilderContext *Ctx)=0; + + /// Called by CoreEngine when it starts processing a CFGBlock. The + /// SubEngine is expected to populate dstNodes with new nodes representing + /// updated analysis state, or generate no nodes at all if it doesn't. + virtual void processCFGBlockEntrance(const BlockEdge &L, + NodeBuilderWithSinks &nodeBuilder, + ExplodedNode *Pred) = 0; + + /// Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a branch condition. + virtual void processBranch(const Stmt *Condition, + NodeBuilderContext& BuilderCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) = 0; + + /// Called by CoreEngine. + /// Used to generate successor nodes for temporary destructors depending + /// on whether the corresponding constructor was visited. + virtual void processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, + NodeBuilderContext &BldCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) = 0; + + /// Called by CoreEngine. Used to processing branching behavior + /// at static initializers. + virtual void processStaticInitializer(const DeclStmt *DS, + NodeBuilderContext& BuilderCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) = 0; + + /// Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a computed goto jump. + virtual void processIndirectGoto(IndirectGotoNodeBuilder& builder) = 0; + + /// Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a switch statement. + virtual void processSwitch(SwitchNodeBuilder& builder) = 0; + + /// Called by CoreEngine. Used to notify checkers that processing a + /// function has begun. Called for both inlined and and top-level functions. + virtual void processBeginOfFunction(NodeBuilderContext &BC, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const BlockEdge &L) = 0; + + /// Called by CoreEngine. Used to notify checkers that processing a + /// function has ended. Called for both inlined and and top-level functions. + virtual void processEndOfFunction(NodeBuilderContext& BC, + ExplodedNode *Pred, + const ReturnStmt *RS = nullptr) = 0; + + // Generate the entry node of the callee. + virtual void processCallEnter(NodeBuilderContext& BC, CallEnter CE, + ExplodedNode *Pred) = 0; + + // Generate the first post callsite node. + virtual void processCallExit(ExplodedNode *Pred) = 0; + + /// Called by ConstraintManager. Used to call checker-specific + /// logic for handling assumptions on symbolic values. + virtual ProgramStateRef processAssume(ProgramStateRef state, + SVal cond, bool assumption) = 0; + + /// processRegionChanges - Called by ProgramStateManager whenever a change is + /// made to the store. Used to update checkers that track region values. + virtual ProgramStateRef + processRegionChanges(ProgramStateRef state, + const InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call) = 0; + + + inline ProgramStateRef + processRegionChange(ProgramStateRef state, + const MemRegion* MR, + const LocationContext *LCtx) { + return processRegionChanges(state, nullptr, MR, MR, LCtx, nullptr); + } + + virtual ProgramStateRef + processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, SVal Val, const LocationContext *LCtx) = 0; + + virtual ProgramStateRef + notifyCheckersOfPointerEscape(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + const CallEvent *Call, + RegionAndSymbolInvalidationTraits &HTraits) = 0; + + /// printState - Called by ProgramStateManager to print checker-specific data. + virtual void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep, + const LocationContext *LCtx = nullptr) = 0; + + /// Called by CoreEngine when the analysis worklist is either empty or the + // maximum number of analysis steps have been reached. + virtual void processEndWorklist() = 0; +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SummaryManager.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SummaryManager.h new file mode 100644 index 00000000..1a56153d --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SummaryManager.h @@ -0,0 +1,57 @@ +//== SummaryManager.h - Generic handling of function summaries --*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines SummaryManager and related classes, which provides +// a generic mechanism for managing function summaries. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_SUMMARY +#define LLVM_CLANG_GR_SUMMARY + +namespace clang { + +namespace ento { + +namespace summMgr { + + +/* Key kinds: + + - C functions + - C++ functions (name + parameter types) + - ObjC methods: + - Class, selector (class method) + - Class, selector (instance method) + - Category, selector (instance method) + - Protocol, selector (instance method) + - C++ methods + - Class, function name + parameter types + const + */ + +class SummaryKey { + +}; + +} // end namespace clang::summMgr + +class SummaryManagerImpl { + +}; + + +template <typename T> +class SummaryManager : SummaryManagerImpl { + +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h new file mode 100644 index 00000000..abfcd1d8 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h @@ -0,0 +1,145 @@ +//===- SymExpr.h - Management of Symbolic Values ----------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines SymExpr and SymbolData. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMEXPR_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMEXPR_H + +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/SmallVector.h" +#include <cassert> + +namespace clang { +namespace ento { + +class MemRegion; + +/// Symbolic value. These values used to capture symbolic execution of +/// the program. +class SymExpr : public llvm::FoldingSetNode { + virtual void anchor(); + +public: + enum Kind { +#define SYMBOL(Id, Parent) Id##Kind, +#define SYMBOL_RANGE(Id, First, Last) BEGIN_##Id = First, END_##Id = Last, +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + }; + +private: + Kind K; + +protected: + SymExpr(Kind k) : K(k) {} + + static bool isValidTypeForSymbol(QualType T) { + // FIXME: Depending on whether we choose to deprecate structural symbols, + // this may become much stricter. + return !T.isNull() && !T->isVoidType(); + } + + mutable unsigned Complexity = 0; + +public: + virtual ~SymExpr() = default; + + Kind getKind() const { return K; } + + virtual void dump() const; + + virtual void dumpToStream(raw_ostream &os) const {} + + virtual QualType getType() const = 0; + virtual void Profile(llvm::FoldingSetNodeID &profile) = 0; + + /// Iterator over symbols that the current symbol depends on. + /// + /// For SymbolData, it's the symbol itself; for expressions, it's the + /// expression symbol and all the operands in it. Note, SymbolDerived is + /// treated as SymbolData - the iterator will NOT visit the parent region. + class symbol_iterator { + SmallVector<const SymExpr *, 5> itr; + + void expand(); + + public: + symbol_iterator() = default; + symbol_iterator(const SymExpr *SE); + + symbol_iterator &operator++(); + const SymExpr *operator*(); + + bool operator==(const symbol_iterator &X) const; + bool operator!=(const symbol_iterator &X) const; + }; + + symbol_iterator symbol_begin() const { return symbol_iterator(this); } + static symbol_iterator symbol_end() { return symbol_iterator(); } + + virtual unsigned computeComplexity() const = 0; + + /// Find the region from which this symbol originates. + /// + /// Whenever the symbol was constructed to denote an unknown value of + /// a certain memory region, return this region. This method + /// allows checkers to make decisions depending on the origin of the symbol. + /// Symbol classes for which the origin region is known include + /// SymbolRegionValue which denotes the value of the region before + /// the beginning of the analysis, and SymbolDerived which denotes the value + /// of a certain memory region after its super region (a memory space or + /// a larger record region) is default-bound with a certain symbol. + virtual const MemRegion *getOriginRegion() const { return nullptr; } +}; + +inline raw_ostream &operator<<(raw_ostream &os, + const clang::ento::SymExpr *SE) { + SE->dumpToStream(os); + return os; +} + +using SymbolRef = const SymExpr *; +using SymbolRefSmallVectorTy = SmallVector<SymbolRef, 2>; +using SymbolID = unsigned; + +/// A symbol representing data which can be stored in a memory location +/// (region). +class SymbolData : public SymExpr { + const SymbolID Sym; + + void anchor() override; + +protected: + SymbolData(Kind k, SymbolID sym) : SymExpr(k), Sym(sym) { + assert(classof(this)); + } + +public: + ~SymbolData() override = default; + + SymbolID getSymbolID() const { return Sym; } + + unsigned computeComplexity() const override { + return 1; + }; + + // Implement isa<T> support. + static inline bool classof(const SymExpr *SE) { + Kind k = SE->getKind(); + return k >= BEGIN_SYMBOLS && k <= END_SYMBOLS; + } +}; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMEXPR_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h new file mode 100644 index 00000000..d212e23d --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -0,0 +1,650 @@ +//===- SymbolManager.h - Management of Symbolic Values ----------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines SymbolManager, a class that manages symbolic values +// created for use by ExprEngine and related classes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMBOLMANAGER_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMBOLMANAGER_H + +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/Allocator.h" +#include <cassert> + +namespace clang { + +class ASTContext; +class Stmt; + +namespace ento { + +class BasicValueFactory; +class StoreManager; + +///A symbol representing the value stored at a MemRegion. +class SymbolRegionValue : public SymbolData { + const TypedValueRegion *R; + +public: + SymbolRegionValue(SymbolID sym, const TypedValueRegion *r) + : SymbolData(SymbolRegionValueKind, sym), R(r) { + assert(r); + assert(isValidTypeForSymbol(r->getValueType())); + } + + const TypedValueRegion* getRegion() const { return R; } + + static void Profile(llvm::FoldingSetNodeID& profile, const TypedValueRegion* R) { + profile.AddInteger((unsigned) SymbolRegionValueKind); + profile.AddPointer(R); + } + + void Profile(llvm::FoldingSetNodeID& profile) override { + Profile(profile, R); + } + + void dumpToStream(raw_ostream &os) const override; + const MemRegion *getOriginRegion() const override { return getRegion(); } + + QualType getType() const override; + + // Implement isa<T> support. + static bool classof(const SymExpr *SE) { + return SE->getKind() == SymbolRegionValueKind; + } +}; + +/// A symbol representing the result of an expression in the case when we do +/// not know anything about what the expression is. +class SymbolConjured : public SymbolData { + const Stmt *S; + QualType T; + unsigned Count; + const LocationContext *LCtx; + const void *SymbolTag; + +public: + SymbolConjured(SymbolID sym, const Stmt *s, const LocationContext *lctx, + QualType t, unsigned count, const void *symbolTag) + : SymbolData(SymbolConjuredKind, sym), S(s), T(t), Count(count), + LCtx(lctx), SymbolTag(symbolTag) { + // FIXME: 's' might be a nullptr if we're conducting invalidation + // that was caused by a destructor call on a temporary object, + // which has no statement associated with it. + // Due to this, we might be creating the same invalidation symbol for + // two different invalidation passes (for two different temporaries). + assert(lctx); + assert(isValidTypeForSymbol(t)); + } + + const Stmt *getStmt() const { return S; } + unsigned getCount() const { return Count; } + const void *getTag() const { return SymbolTag; } + + QualType getType() const override; + + void dumpToStream(raw_ostream &os) const override; + + static void Profile(llvm::FoldingSetNodeID& profile, const Stmt *S, + QualType T, unsigned Count, const LocationContext *LCtx, + const void *SymbolTag) { + profile.AddInteger((unsigned) SymbolConjuredKind); + profile.AddPointer(S); + profile.AddPointer(LCtx); + profile.Add(T); + profile.AddInteger(Count); + profile.AddPointer(SymbolTag); + } + + void Profile(llvm::FoldingSetNodeID& profile) override { + Profile(profile, S, T, Count, LCtx, SymbolTag); + } + + // Implement isa<T> support. + static bool classof(const SymExpr *SE) { + return SE->getKind() == SymbolConjuredKind; + } +}; + +/// A symbol representing the value of a MemRegion whose parent region has +/// symbolic value. +class SymbolDerived : public SymbolData { + SymbolRef parentSymbol; + const TypedValueRegion *R; + +public: + SymbolDerived(SymbolID sym, SymbolRef parent, const TypedValueRegion *r) + : SymbolData(SymbolDerivedKind, sym), parentSymbol(parent), R(r) { + assert(parent); + assert(r); + assert(isValidTypeForSymbol(r->getValueType())); + } + + SymbolRef getParentSymbol() const { return parentSymbol; } + const TypedValueRegion *getRegion() const { return R; } + + QualType getType() const override; + + void dumpToStream(raw_ostream &os) const override; + const MemRegion *getOriginRegion() const override { return getRegion(); } + + static void Profile(llvm::FoldingSetNodeID& profile, SymbolRef parent, + const TypedValueRegion *r) { + profile.AddInteger((unsigned) SymbolDerivedKind); + profile.AddPointer(r); + profile.AddPointer(parent); + } + + void Profile(llvm::FoldingSetNodeID& profile) override { + Profile(profile, parentSymbol, R); + } + + // Implement isa<T> support. + static bool classof(const SymExpr *SE) { + return SE->getKind() == SymbolDerivedKind; + } +}; + +/// SymbolExtent - Represents the extent (size in bytes) of a bounded region. +/// Clients should not ask the SymbolManager for a region's extent. Always use +/// SubRegion::getExtent instead -- the value returned may not be a symbol. +class SymbolExtent : public SymbolData { + const SubRegion *R; + +public: + SymbolExtent(SymbolID sym, const SubRegion *r) + : SymbolData(SymbolExtentKind, sym), R(r) { + assert(r); + } + + const SubRegion *getRegion() const { return R; } + + QualType getType() const override; + + void dumpToStream(raw_ostream &os) const override; + + static void Profile(llvm::FoldingSetNodeID& profile, const SubRegion *R) { + profile.AddInteger((unsigned) SymbolExtentKind); + profile.AddPointer(R); + } + + void Profile(llvm::FoldingSetNodeID& profile) override { + Profile(profile, R); + } + + // Implement isa<T> support. + static bool classof(const SymExpr *SE) { + return SE->getKind() == SymbolExtentKind; + } +}; + +/// SymbolMetadata - Represents path-dependent metadata about a specific region. +/// Metadata symbols remain live as long as they are marked as in use before +/// dead-symbol sweeping AND their associated regions are still alive. +/// Intended for use by checkers. +class SymbolMetadata : public SymbolData { + const MemRegion* R; + const Stmt *S; + QualType T; + const LocationContext *LCtx; + unsigned Count; + const void *Tag; + +public: + SymbolMetadata(SymbolID sym, const MemRegion* r, const Stmt *s, QualType t, + const LocationContext *LCtx, unsigned count, const void *tag) + : SymbolData(SymbolMetadataKind, sym), R(r), S(s), T(t), LCtx(LCtx), + Count(count), Tag(tag) { + assert(r); + assert(s); + assert(isValidTypeForSymbol(t)); + assert(LCtx); + assert(tag); + } + + const MemRegion *getRegion() const { return R; } + const Stmt *getStmt() const { return S; } + const LocationContext *getLocationContext() const { return LCtx; } + unsigned getCount() const { return Count; } + const void *getTag() const { return Tag; } + + QualType getType() const override; + + void dumpToStream(raw_ostream &os) const override; + + static void Profile(llvm::FoldingSetNodeID& profile, const MemRegion *R, + const Stmt *S, QualType T, const LocationContext *LCtx, + unsigned Count, const void *Tag) { + profile.AddInteger((unsigned) SymbolMetadataKind); + profile.AddPointer(R); + profile.AddPointer(S); + profile.Add(T); + profile.AddPointer(LCtx); + profile.AddInteger(Count); + profile.AddPointer(Tag); + } + + void Profile(llvm::FoldingSetNodeID& profile) override { + Profile(profile, R, S, T, LCtx, Count, Tag); + } + + // Implement isa<T> support. + static bool classof(const SymExpr *SE) { + return SE->getKind() == SymbolMetadataKind; + } +}; + +/// Represents a cast expression. +class SymbolCast : public SymExpr { + const SymExpr *Operand; + + /// Type of the operand. + QualType FromTy; + + /// The type of the result. + QualType ToTy; + +public: + SymbolCast(const SymExpr *In, QualType From, QualType To) + : SymExpr(SymbolCastKind), Operand(In), FromTy(From), ToTy(To) { + assert(In); + assert(isValidTypeForSymbol(From)); + // FIXME: GenericTaintChecker creates symbols of void type. + // Otherwise, 'To' should also be a valid type. + } + + unsigned computeComplexity() const override { + if (Complexity == 0) + Complexity = 1 + Operand->computeComplexity(); + return Complexity; + } + + QualType getType() const override { return ToTy; } + + const SymExpr *getOperand() const { return Operand; } + + void dumpToStream(raw_ostream &os) const override; + + static void Profile(llvm::FoldingSetNodeID& ID, + const SymExpr *In, QualType From, QualType To) { + ID.AddInteger((unsigned) SymbolCastKind); + ID.AddPointer(In); + ID.Add(From); + ID.Add(To); + } + + void Profile(llvm::FoldingSetNodeID& ID) override { + Profile(ID, Operand, FromTy, ToTy); + } + + // Implement isa<T> support. + static bool classof(const SymExpr *SE) { + return SE->getKind() == SymbolCastKind; + } +}; + +/// Represents a symbolic expression involving a binary operator +class BinarySymExpr : public SymExpr { + BinaryOperator::Opcode Op; + QualType T; + +protected: + BinarySymExpr(Kind k, BinaryOperator::Opcode op, QualType t) + : SymExpr(k), Op(op), T(t) { + assert(classof(this)); + // Binary expressions are results of arithmetic. Pointer arithmetic is not + // handled by binary expressions, but it is instead handled by applying + // sub-regions to regions. + assert(isValidTypeForSymbol(t) && !Loc::isLocType(t)); + } + +public: + // FIXME: We probably need to make this out-of-line to avoid redundant + // generation of virtual functions. + QualType getType() const override { return T; } + + BinaryOperator::Opcode getOpcode() const { return Op; } + + // Implement isa<T> support. + static bool classof(const SymExpr *SE) { + Kind k = SE->getKind(); + return k >= BEGIN_BINARYSYMEXPRS && k <= END_BINARYSYMEXPRS; + } +}; + +/// Represents a symbolic expression like 'x' + 3. +class SymIntExpr : public BinarySymExpr { + const SymExpr *LHS; + const llvm::APSInt& RHS; + +public: + SymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APSInt &rhs, QualType t) + : BinarySymExpr(SymIntExprKind, op, t), LHS(lhs), RHS(rhs) { + assert(lhs); + } + + void dumpToStream(raw_ostream &os) const override; + + const SymExpr *getLHS() const { return LHS; } + const llvm::APSInt &getRHS() const { return RHS; } + + unsigned computeComplexity() const override { + if (Complexity == 0) + Complexity = 1 + LHS->computeComplexity(); + return Complexity; + } + + static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, + BinaryOperator::Opcode op, const llvm::APSInt& rhs, + QualType t) { + ID.AddInteger((unsigned) SymIntExprKind); + ID.AddPointer(lhs); + ID.AddInteger(op); + ID.AddPointer(&rhs); + ID.Add(t); + } + + void Profile(llvm::FoldingSetNodeID& ID) override { + Profile(ID, LHS, getOpcode(), RHS, getType()); + } + + // Implement isa<T> support. + static bool classof(const SymExpr *SE) { + return SE->getKind() == SymIntExprKind; + } +}; + +/// Represents a symbolic expression like 3 - 'x'. +class IntSymExpr : public BinarySymExpr { + const llvm::APSInt& LHS; + const SymExpr *RHS; + +public: + IntSymExpr(const llvm::APSInt &lhs, BinaryOperator::Opcode op, + const SymExpr *rhs, QualType t) + : BinarySymExpr(IntSymExprKind, op, t), LHS(lhs), RHS(rhs) { + assert(rhs); + } + + void dumpToStream(raw_ostream &os) const override; + + const SymExpr *getRHS() const { return RHS; } + const llvm::APSInt &getLHS() const { return LHS; } + + unsigned computeComplexity() const override { + if (Complexity == 0) + Complexity = 1 + RHS->computeComplexity(); + return Complexity; + } + + static void Profile(llvm::FoldingSetNodeID& ID, const llvm::APSInt& lhs, + BinaryOperator::Opcode op, const SymExpr *rhs, + QualType t) { + ID.AddInteger((unsigned) IntSymExprKind); + ID.AddPointer(&lhs); + ID.AddInteger(op); + ID.AddPointer(rhs); + ID.Add(t); + } + + void Profile(llvm::FoldingSetNodeID& ID) override { + Profile(ID, LHS, getOpcode(), RHS, getType()); + } + + // Implement isa<T> support. + static bool classof(const SymExpr *SE) { + return SE->getKind() == IntSymExprKind; + } +}; + +/// Represents a symbolic expression like 'x' + 'y'. +class SymSymExpr : public BinarySymExpr { + const SymExpr *LHS; + const SymExpr *RHS; + +public: + SymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, + QualType t) + : BinarySymExpr(SymSymExprKind, op, t), LHS(lhs), RHS(rhs) { + assert(lhs); + assert(rhs); + } + + const SymExpr *getLHS() const { return LHS; } + const SymExpr *getRHS() const { return RHS; } + + void dumpToStream(raw_ostream &os) const override; + + unsigned computeComplexity() const override { + if (Complexity == 0) + Complexity = RHS->computeComplexity() + LHS->computeComplexity(); + return Complexity; + } + + static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, + BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) { + ID.AddInteger((unsigned) SymSymExprKind); + ID.AddPointer(lhs); + ID.AddInteger(op); + ID.AddPointer(rhs); + ID.Add(t); + } + + void Profile(llvm::FoldingSetNodeID& ID) override { + Profile(ID, LHS, getOpcode(), RHS, getType()); + } + + // Implement isa<T> support. + static bool classof(const SymExpr *SE) { + return SE->getKind() == SymSymExprKind; + } +}; + +class SymbolManager { + using DataSetTy = llvm::FoldingSet<SymExpr>; + using SymbolDependTy = llvm::DenseMap<SymbolRef, SymbolRefSmallVectorTy *>; + + DataSetTy DataSet; + + /// Stores the extra dependencies between symbols: the data should be kept + /// alive as long as the key is live. + SymbolDependTy SymbolDependencies; + + unsigned SymbolCounter = 0; + llvm::BumpPtrAllocator& BPAlloc; + BasicValueFactory &BV; + ASTContext &Ctx; + +public: + SymbolManager(ASTContext &ctx, BasicValueFactory &bv, + llvm::BumpPtrAllocator& bpalloc) + : SymbolDependencies(16), BPAlloc(bpalloc), BV(bv), Ctx(ctx) {} + ~SymbolManager(); + + static bool canSymbolicate(QualType T); + + /// Make a unique symbol for MemRegion R according to its kind. + const SymbolRegionValue* getRegionValueSymbol(const TypedValueRegion* R); + + const SymbolConjured* conjureSymbol(const Stmt *E, + const LocationContext *LCtx, + QualType T, + unsigned VisitCount, + const void *SymbolTag = nullptr); + + const SymbolConjured* conjureSymbol(const Expr *E, + const LocationContext *LCtx, + unsigned VisitCount, + const void *SymbolTag = nullptr) { + return conjureSymbol(E, LCtx, E->getType(), VisitCount, SymbolTag); + } + + const SymbolDerived *getDerivedSymbol(SymbolRef parentSymbol, + const TypedValueRegion *R); + + const SymbolExtent *getExtentSymbol(const SubRegion *R); + + /// Creates a metadata symbol associated with a specific region. + /// + /// VisitCount can be used to differentiate regions corresponding to + /// different loop iterations, thus, making the symbol path-dependent. + const SymbolMetadata *getMetadataSymbol(const MemRegion *R, const Stmt *S, + QualType T, + const LocationContext *LCtx, + unsigned VisitCount, + const void *SymbolTag = nullptr); + + const SymbolCast* getCastSymbol(const SymExpr *Operand, + QualType From, QualType To); + + const SymIntExpr *getSymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APSInt& rhs, QualType t); + + const SymIntExpr *getSymIntExpr(const SymExpr &lhs, BinaryOperator::Opcode op, + const llvm::APSInt& rhs, QualType t) { + return getSymIntExpr(&lhs, op, rhs, t); + } + + const IntSymExpr *getIntSymExpr(const llvm::APSInt& lhs, + BinaryOperator::Opcode op, + const SymExpr *rhs, QualType t); + + const SymSymExpr *getSymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op, + const SymExpr *rhs, QualType t); + + QualType getType(const SymExpr *SE) const { + return SE->getType(); + } + + /// Add artificial symbol dependency. + /// + /// The dependent symbol should stay alive as long as the primary is alive. + void addSymbolDependency(const SymbolRef Primary, const SymbolRef Dependent); + + const SymbolRefSmallVectorTy *getDependentSymbols(const SymbolRef Primary); + + ASTContext &getContext() { return Ctx; } + BasicValueFactory &getBasicVals() { return BV; } +}; + +/// A class responsible for cleaning up unused symbols. +class SymbolReaper { + enum SymbolStatus { + NotProcessed, + HaveMarkedDependents + }; + + using SymbolSetTy = llvm::DenseSet<SymbolRef>; + using SymbolMapTy = llvm::DenseMap<SymbolRef, SymbolStatus>; + using RegionSetTy = llvm::DenseSet<const MemRegion *>; + + SymbolMapTy TheLiving; + SymbolSetTy MetadataInUse; + + RegionSetTy RegionRoots; + + const StackFrameContext *LCtx; + const Stmt *Loc; + SymbolManager& SymMgr; + StoreRef reapedStore; + llvm::DenseMap<const MemRegion *, unsigned> includedRegionCache; + +public: + /// Construct a reaper object, which removes everything which is not + /// live before we execute statement s in the given location context. + /// + /// If the statement is NULL, everything is this and parent contexts is + /// considered live. + /// If the stack frame context is NULL, everything on stack is considered + /// dead. + SymbolReaper(const StackFrameContext *Ctx, const Stmt *s, + SymbolManager &symmgr, StoreManager &storeMgr) + : LCtx(Ctx), Loc(s), SymMgr(symmgr), reapedStore(nullptr, storeMgr) {} + + const LocationContext *getLocationContext() const { return LCtx; } + + bool isLive(SymbolRef sym); + bool isLiveRegion(const MemRegion *region); + bool isLive(const Stmt *ExprVal, const LocationContext *LCtx) const; + bool isLive(const VarRegion *VR, bool includeStoreBindings = false) const; + + /// Unconditionally marks a symbol as live. + /// + /// This should never be + /// used by checkers, only by the state infrastructure such as the store and + /// environment. Checkers should instead use metadata symbols and markInUse. + void markLive(SymbolRef sym); + + /// Marks a symbol as important to a checker. + /// + /// For metadata symbols, + /// this will keep the symbol alive as long as its associated region is also + /// live. For other symbols, this has no effect; checkers are not permitted + /// to influence the life of other symbols. This should be used before any + /// symbol marking has occurred, i.e. in the MarkLiveSymbols callback. + void markInUse(SymbolRef sym); + + using region_iterator = RegionSetTy::const_iterator; + + region_iterator region_begin() const { return RegionRoots.begin(); } + region_iterator region_end() const { return RegionRoots.end(); } + + /// Returns whether or not a symbol has been confirmed dead. + /// + /// This should only be called once all marking of dead symbols has completed. + /// (For checkers, this means only in the checkDeadSymbols callback.) + bool isDead(SymbolRef sym) { + return !isLive(sym); + } + + void markLive(const MemRegion *region); + void markElementIndicesLive(const MemRegion *region); + + /// Set to the value of the symbolic store after + /// StoreManager::removeDeadBindings has been called. + void setReapedStore(StoreRef st) { reapedStore = st; } + +private: + /// Mark the symbols dependent on the input symbol as live. + void markDependentsLive(SymbolRef sym); +}; + +class SymbolVisitor { +protected: + ~SymbolVisitor() = default; + +public: + SymbolVisitor() = default; + SymbolVisitor(const SymbolVisitor &) = default; + SymbolVisitor(SymbolVisitor &&) {} + + /// A visitor method invoked by ProgramStateManager::scanReachableSymbols. + /// + /// The method returns \c true if symbols should continue be scanned and \c + /// false otherwise. + virtual bool VisitSymbol(SymbolRef sym) = 0; + virtual bool VisitMemRegion(const MemRegion *) { return true; } +}; + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMBOLMANAGER_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def new file mode 100644 index 00000000..7163a162 --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def @@ -0,0 +1,54 @@ +//===-- Symbols.def - Metadata about SymExpr kinds --------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// The list of symbols (SymExpr sub-classes) used in the Static Analyzer. +// In order to use this information, users of this file must define +// one or more of the three macros: +// +// SYMBOL(Id, Parent) - for specific SymExpr sub-classes, reserving the +// IdKind identifier for its kind enumeration value. +// +// ABSTRACT_SYMBOL(Id, Parent) - for abstract symbol classes, +// +// SYMBOL_RANGE(Id, First, Last) - for ranges of kind-enums, +// allowing to determine abstract class of a symbol +// based on the kind enumeration value. +// +//===----------------------------------------------------------------------===// + +#ifndef SYMBOL +#define SYMBOL(Id, Parent) +#endif + +#ifndef ABSTRACT_SYMBOL +#define ABSTRACT_SYMBOL(Id, Parent) +#endif + +#ifndef SYMBOL_RANGE +#define SYMBOL_RANGE(Id, First, Last) +#endif + +ABSTRACT_SYMBOL(BinarySymExpr, SymExpr) + SYMBOL(IntSymExpr, BinarySymExpr) + SYMBOL(SymIntExpr, BinarySymExpr) + SYMBOL(SymSymExpr, BinarySymExpr) +SYMBOL_RANGE(BINARYSYMEXPRS, IntSymExprKind, SymSymExprKind) + +SYMBOL(SymbolCast, SymExpr) + +ABSTRACT_SYMBOL(SymbolData, SymExpr) + SYMBOL(SymbolConjured, SymbolData) + SYMBOL(SymbolDerived, SymbolData) + SYMBOL(SymbolExtent, SymbolData) + SYMBOL(SymbolMetadata, SymbolData) + SYMBOL(SymbolRegionValue, SymbolData) +SYMBOL_RANGE(SYMBOLS, SymbolConjuredKind, SymbolRegionValueKind) + +#undef SYMBOL +#undef ABSTRACT_SYMBOL +#undef SYMBOL_RANGE diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h new file mode 100644 index 00000000..9424392c --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h @@ -0,0 +1,58 @@ +//===- TaintManager.h - Managing taint --------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file provides APIs for adding, removing, querying symbol taint. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTMANAGER_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTMANAGER_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h" +#include "llvm/ADT/ImmutableMap.h" + +namespace clang { +namespace ento { + +/// The GDM component containing the tainted root symbols. We lazily infer the +/// taint of the dependent symbols. Currently, this is a map from a symbol to +/// tag kind. TODO: Should support multiple tag kinds. +// FIXME: This does not use the nice trait macros because it must be accessible +// from multiple translation units. +struct TaintMap {}; + +using TaintMapImpl = llvm::ImmutableMap<SymbolRef, TaintTagType>; + +template<> struct ProgramStateTrait<TaintMap> + : public ProgramStatePartialTrait<TaintMapImpl> { + static void *GDMIndex(); +}; + +/// The GDM component mapping derived symbols' parent symbols to their +/// underlying regions. This is used to efficiently check whether a symbol is +/// tainted when it represents a sub-region of a tainted symbol. +struct DerivedSymTaint {}; + +using DerivedSymTaintImpl = llvm::ImmutableMap<SymbolRef, TaintedSubRegions>; + +template<> struct ProgramStateTrait<DerivedSymTaint> + : public ProgramStatePartialTrait<DerivedSymTaintImpl> { + static void *GDMIndex(); +}; + +class TaintManager { + TaintManager() = default; +}; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTMANAGER_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h new file mode 100644 index 00000000..d1018f9d --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h @@ -0,0 +1,29 @@ +//===- TaintTag.h - Path-sensitive "State" for tracking values --*- 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 a set of taint tags. Several tags are used to differentiate kinds +// of taint. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTTAG_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTTAG_H + +namespace clang { +namespace ento { + +/// The type of taint, which helps to differentiate between different types of +/// taint. +using TaintTagType = unsigned; + +static const TaintTagType TaintTagGeneric = 0; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_TAINTTAG_H diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/WorkList.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/WorkList.h new file mode 100644 index 00000000..7beb7ddf --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/WorkList.h @@ -0,0 +1,94 @@ +//==- WorkList.h - Worklist class used by CoreEngine ---------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines WorkList, a pure virtual class that represents an opaque +// worklist used by CoreEngine to explore the reachability state space. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_WORKLIST_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_WORKLIST_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include <cassert> + +namespace clang { + +class CFGBlock; + +namespace ento { + +class WorkListUnit { + ExplodedNode *node; + BlockCounter counter; + const CFGBlock *block; + unsigned blockIdx; // This is the index of the next statement. + +public: + WorkListUnit(ExplodedNode *N, BlockCounter C, + const CFGBlock *B, unsigned idx) + : node(N), + counter(C), + block(B), + blockIdx(idx) {} + + explicit WorkListUnit(ExplodedNode *N, BlockCounter C) + : node(N), + counter(C), + block(nullptr), + blockIdx(0) {} + + /// Returns the node associated with the worklist unit. + ExplodedNode *getNode() const { return node; } + + /// Returns the block counter map associated with the worklist unit. + BlockCounter getBlockCounter() const { return counter; } + + /// Returns the CFGblock associated with the worklist unit. + const CFGBlock *getBlock() const { return block; } + + /// Return the index within the CFGBlock for the worklist unit. + unsigned getIndex() const { return blockIdx; } +}; + +class WorkList { + BlockCounter CurrentCounter; +public: + virtual ~WorkList(); + virtual bool hasWork() const = 0; + + virtual void enqueue(const WorkListUnit& U) = 0; + + void enqueue(ExplodedNode *N, const CFGBlock *B, unsigned idx) { + enqueue(WorkListUnit(N, CurrentCounter, B, idx)); + } + + void enqueue(ExplodedNode *N) { + assert(N->getLocation().getKind() != ProgramPoint::PostStmtKind); + enqueue(WorkListUnit(N, CurrentCounter)); + } + + virtual WorkListUnit dequeue() = 0; + + void setBlockCounter(BlockCounter C) { CurrentCounter = C; } + BlockCounter getBlockCounter() const { return CurrentCounter; } + + static std::unique_ptr<WorkList> makeDFS(); + static std::unique_ptr<WorkList> makeBFS(); + static std::unique_ptr<WorkList> makeBFSBlockDFSContents(); + static std::unique_ptr<WorkList> makeUnexploredFirst(); + static std::unique_ptr<WorkList> makeUnexploredFirstPriorityQueue(); + static std::unique_ptr<WorkList> makeUnexploredFirstPriorityLocationQueue(); +}; + +} // end ento namespace + +} // end clang namespace + +#endif |
