diff options
| author | Ralf Luther <luther.ralf@gmail.com> | 2019-03-27 20:23:17 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit2@aicp-server-3> | 2019-03-27 20:23:17 +0000 |
| commit | 1ce3a9d272e564b22a1333a1e36a3d3ab7cfab01 (patch) | |
| tree | 391382eadd4fec5bb480f2e8934fa352770221d1 /clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive | |
| parent | d1d48b140bafaa8a50107292f5fce95562575765 (diff) | |
| parent | 4f56932d3416ac03f646bc1a611b3135fec2fe08 (diff) | |
Merge "Update prebuilt Clang to r353983." into p9.0HEADp9.0-backupp9.0
Diffstat (limited to 'clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive')
41 files changed, 12403 insertions, 0 deletions
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 |
