//== SVals.h - Abstract Values for Static Analysis ---------*- C++ -*--==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // 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/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" //==------------------------------------------------------------------------==// // Base SVal types. //==------------------------------------------------------------------------==// namespace clang { namespace ento { class CompoundValData; class LazyCompoundValData; class PointerToMemberData; class ProgramState; class BasicValueFactory; class MemRegion; class TypedValueRegion; class MemRegionManager; class ProgramStateManager; class SValBuilder; namespace nonloc { /// Sub-kinds for NonLoc values. enum Kind { #define NONLOC_SVAL(Id, Parent) Id ## Kind, #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" }; } namespace loc { /// Sub-kinds for Loc values. enum Kind { #define LOC_SVAL(Id, Parent) Id ## Kind, #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" }; } /// 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; /// The lowest 2 bits are a BaseKind (0 -- 3). /// The higher bits are an unsigned "kind" value. unsigned Kind; 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() : Data(nullptr), Kind(0) {} /// \brief Convert to the specified SVal type, asserting that this SVal is of /// the desired type. template T castAs() const { assert(T::isKind(*this)); return *static_cast(this); } /// \brief Convert to the specified SVal type, returning None if this SVal is /// not of the desired type. template Optional getAs() const { if (!T::isKind(*this)) return None; return *static_cast(this); } inline unsigned getRawKind() const { return Kind; } inline BaseKind getBaseKind() const { return (BaseKind) (Kind & BaseMask); } inline 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. inline void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger((unsigned) getRawKind()); ID.AddPointer(Data); } inline bool operator==(const SVal& R) const { return getRawKind() == R.getRawKind() && Data == R.Data; } inline bool operator!=(const SVal& R) const { return !(*this == R); } inline bool isUnknown() const { return getRawKind() == UnknownValKind; } inline bool isUndef() const { return getRawKind() == UndefinedValKind; } inline bool isUnknownOrUndef() const { return getRawKind() <= UnknownValKind; } inline 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; /// \brief 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; /// \brief 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 = getAsSymbolicExpression(); 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 { private: // 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() {} 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 { private: // 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() {} 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(); } }; /// \brief Represents an SVal that is guaranteed to not be UnknownVal. class KnownSVal : public SVal { KnownSVal() {} friend class SVal; 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() {} explicit NonLoc(unsigned SubKind, const void *d) : DefinedSVal(d, false, SubKind) {} public: void dumpToStream(raw_ostream &Out) const; static inline 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() {} explicit Loc(unsigned SubKind, const void *D) : DefinedSVal(const_cast(D), true, SubKind) {} public: void dumpToStream(raw_ostream &Out) const; static inline 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 { /// \brief Represents symbolic expression. class SymbolVal : public NonLoc { public: SymbolVal() = delete; SymbolVal(SymbolRef sym) : NonLoc(SymbolValKind, sym) { assert(sym); } SymbolRef getSymbol() const { return (const SymExpr*) Data; } bool isExpression() const { return !isa(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; } }; /// \brief 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(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() {} 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 &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 *D = static_cast *>(Data); return D->first.castAs(); } Loc getPersistentLoc() const { const std::pair *D = static_cast *>(Data); const SVal& V = D->first; return V.castAs(); } unsigned getNumBits() const { const std::pair *D = static_cast *>(Data); return D->second; } private: friend class SVal; LocAsInteger() {} 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(Data); } typedef llvm::ImmutableList::iterator iterator; iterator begin() const; iterator end() const; private: friend class SVal; CompoundVal() {} 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(Data); } const void *getStore() const; const TypedValueRegion *getRegion() const; private: friend class SVal; LazyCompoundVal() {} static bool isKind(const SVal& V) { return V.getBaseKind() == NonLocKind && V.getSubKind() == LazyCompoundValKind; } static bool isKind(const NonLoc& V) { return V.getSubKind() == LazyCompoundValKind; } }; /// \brief Value representing pointer-to-member. /// /// This value is qualified as NonLoc because neither loading nor storing /// operations are aplied 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: typedef llvm::PointerUnion PTMDataType; const PTMDataType getPTMData() const { return PTMDataType::getFromOpaqueValue(const_cast(Data)); } bool isNullMemberPointer() const { return getPTMData().isNull(); } const DeclaratorDecl *getDecl() const; template const AdjustedDecl* getDeclAs() const { return dyn_cast_or_null(getDecl()); } typedef llvm::ImmutableList::iterator iterator; iterator begin() const; iterator end() const; private: explicit PointerToMember(const PTMDataType D) : NonLoc(PointerToMemberKind, D.getOpaqueValue()) {} friend class SVal; PointerToMember() {} static bool isKind(const SVal& V) { return V.getBaseKind() == NonLocKind && V.getSubKind() == PointerToMemberKind; } static bool isKind(const NonLoc& V) { return V.getSubKind() == PointerToMemberKind; } }; } // end namespace ento::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(Data); } private: friend class SVal; GotoLabel() {} 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); } /// \brief Get the underlining region. const MemRegion* getRegion() const { return static_cast(Data); } /// \brief Get the underlining region and strip casts. const MemRegion* stripCasts(bool StripBaseCasts = true) const; template const REGION* getRegionAs() const { return dyn_cast(getRegion()); } inline bool operator==(const MemRegionVal& R) const { return getRegion() == R.getRegion(); } inline bool operator!=(const MemRegionVal& R) const { return getRegion() != R.getRegion(); } private: friend class SVal; MemRegionVal() {} 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(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() {} static bool isKind(const SVal& V) { return V.getBaseKind() == LocKind && V.getSubKind() == ConcreteIntKind; } static bool isKind(const Loc& V) { return V.getSubKind() == ConcreteIntKind; } }; } // end ento::loc namespace } // end ento namespace } // end clang namespace namespace llvm { template struct isPodLike; template <> struct isPodLike { static const bool value = true; }; } // end llvm namespace #endif