From: Ted Kremenek Date: Thu, 7 Feb 2008 05:48:01 +0000 (+0000) Subject: Added recording of "implicit" NULL dereferences of symbolic pointers. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d131c4ff6133c691f91d8a82e7197d97b187425f;p=clang Added recording of "implicit" NULL dereferences of symbolic pointers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@46843 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Analysis/GRConstants.cpp b/Analysis/GRConstants.cpp index 399dacc96f..44c5d51664 100644 --- a/Analysis/GRConstants.cpp +++ b/Analysis/GRConstants.cpp @@ -124,6 +124,12 @@ protected: typedef llvm::SmallPtrSet UninitBranchesTy; UninitBranchesTy UninitBranches; + /// ImplicitNullDeref - Nodes in the ExplodedGraph that result from + /// taking a dereference on a symbolic pointer that may be NULL. + typedef llvm::SmallPtrSet ImplicitNullDerefTy; + ImplicitNullDerefTy ImplicitNullDeref; + + bool StateCleaned; ASTContext& getContext() const { return G.getContext(); } @@ -162,6 +168,10 @@ public: bool isUninitControlFlow(const NodeTy* N) const { return N->isSink() && UninitBranches.count(const_cast(N)) != 0; } + + bool isImplicitNullDeref(const NodeTy* N) const { + return N->isSink() && ImplicitNullDeref.count(const_cast(N)) != 0; + } /// ProcessStmt - Called by GREngine. Used to generate new successor /// nodes by processing the 'effects' of a block-level statement. @@ -203,8 +213,10 @@ public: return GetValue(St, const_cast(S)); } - inline RValue GetValue(const StateTy& St, const LValue& LV) { - return StateMgr.GetValue(St, LV); + inline RValue GetValue(const StateTy& St, const LValue& LV, + QualType* T = NULL) { + + return StateMgr.GetValue(St, LV, T); } inline LValue GetLValue(const StateTy& St, Stmt* S) { @@ -237,7 +249,7 @@ public: StateTy AssumeSymInt(StateTy St, bool Assumption, const SymIntConstraint& C, bool& isFeasible); - void Nodify(NodeSet& Dst, Stmt* S, NodeTy* Pred, StateTy St); + NodeTy* Nodify(NodeSet& Dst, Stmt* S, NodeTy* Pred, StateTy St); /// Nodify - This version of Nodify is used to batch process a set of states. /// The states are not guaranteed to be unique. @@ -557,13 +569,16 @@ GRConstants::StateTy GRConstants::RemoveDeadBindings(Stmt* Loc, StateTy M) { return M; } -void GRConstants::Nodify(NodeSet& Dst, Stmt* S, NodeTy* Pred, StateTy St) { +GRConstants::NodeTy* +GRConstants::Nodify(NodeSet& Dst, Stmt* S, NodeTy* Pred, StateTy St) { // If the state hasn't changed, don't generate a new node. if (St == Pred->getState()) - return; + return NULL; - Dst.Add(Builder->generateNode(S, St, Pred)); + NodeTy* N = Builder->generateNode(S, St, Pred); + Dst.Add(N); + return N; } void GRConstants::Nodify(NodeSet& Dst, Stmt* S, NodeTy* Pred, @@ -756,7 +771,34 @@ void GRConstants::VisitUnaryOperator(UnaryOperator* U, const RValue& V = GetValue(St, U->getSubExpr()); const LValue& L1 = cast(V); - Nodify(Dst, U, N1, SetValue(St, U, GetValue(St, L1))); + // After a dereference, one of two possible situations arise: + // (1) A crash, because the pointer was NULL. + // (2) The pointer is not NULL, and the dereference works. + // + // We add these assumptions. + + bool isFeasible; + + // "Assume" that the pointer is NULL. + StateTy StNull = Assume(St, L1, false, isFeasible); + + if (isFeasible) { + NodeTy* NullNode = Nodify(Dst, U, N1, StNull); + if (NullNode) { + NullNode->markAsSink(); + ImplicitNullDeref.insert(NullNode); + } + } + + // "Assume" that the pointer is Not-NULL. + StateTy StNotNull = Assume(St, L1, true, isFeasible); + + if (isFeasible) { + QualType T = U->getType(); + Nodify(Dst, U, N1, SetValue(StNotNull, U, + GetValue(StNotNull, L1, &T))); + } + break; } @@ -1208,6 +1250,11 @@ struct VISIBILITY_HIDDEN DOTGraphTraits : << (void*) L.getStmt() << ' '; L.getStmt()->printPretty(Out); + + if (GraphPrintCheckerState->isImplicitNullDeref(N)) { + Out << "\\|Implicit-Null Dereference.\\l"; + } + break; } diff --git a/Analysis/RValues.cpp b/Analysis/RValues.cpp index f006ed7501..d0f60fbe63 100644 --- a/Analysis/RValues.cpp +++ b/Analysis/RValues.cpp @@ -24,23 +24,35 @@ using llvm::APSInt; //===----------------------------------------------------------------------===// SymbolID SymbolManager::getSymbol(ParmVarDecl* D) { - SymbolID& X = DataToSymbol[D]; + SymbolID& X = DataToSymbol[getKey(D)]; if (!X.isInitialized()) { X = SymbolToData.size(); - SymbolToData.push_back(D); + SymbolToData.push_back(SymbolDataParmVar(D)); } return X; } +SymbolID SymbolManager::getContentsOfSymbol(SymbolID sym) { + SymbolID& X = DataToSymbol[getKey(sym)]; + + if (!X.isInitialized()) { + X = SymbolToData.size(); + SymbolToData.push_back(SymbolDataContentsOf(sym)); + } + + return X; +} + QualType SymbolData::getType() const { switch (getKind()) { default: assert (false && "getType() not implemented for this symbol."); case ParmKind: - return static_cast(getPtr())->getType(); + return cast(this)->getDecl()->getType(); + } } @@ -554,7 +566,7 @@ void NonLValue::print(std::ostream& Out) const { } } -#if 0 + void LValue::print(std::ostream& Out) const { switch (getSubKind()) { case lval::ConcreteIntKind: @@ -576,4 +588,4 @@ void LValue::print(std::ostream& Out) const { break; } } -#endif + diff --git a/Analysis/RValues.h b/Analysis/RValues.h index 8d5437fe6e..ce9b8a683a 100644 --- a/Analysis/RValues.h +++ b/Analysis/RValues.h @@ -61,21 +61,70 @@ public: } }; + // SymbolData: Used to record meta data about symbols. + class SymbolData { +public: + enum Kind { UninitKind, ParmKind, ContentsOfKind }; + +private: uintptr_t Data; + Kind K; + +protected: + SymbolData(uintptr_t D, Kind k) : Data(D), K(k) {} + SymbolData(void* D, Kind k) : Data(reinterpret_cast(D)), K(k) {} + + void* getPtr() const { + assert (K != UninitKind); + return reinterpret_cast(Data); + } + + uintptr_t getInt() const { + assert (K != UninitKind); + return Data; + } + public: - enum Kind { ParmKind = 0x0, Mask = 0x3 }; + SymbolData() : Data(0), K(UninitKind) {} - SymbolData(ParmVarDecl* D) - : Data(reinterpret_cast(D) | ParmKind) {} + Kind getKind() const { return K; } + + inline bool operator==(const SymbolData& R) const { + return K == R.K && Data == R.Data; + } - inline Kind getKind() const { return (Kind) (Data & Mask); } - inline void* getPtr() const { return reinterpret_cast(Data & ~Mask); } - inline bool operator==(const SymbolData& R) const { return Data == R.Data; } + QualType getType() const; - QualType getType() const; + // Implement isa support. + static inline bool classof(const SymbolData*) { return true; } }; + +class SymbolDataParmVar : public SymbolData { +public: + SymbolDataParmVar(ParmVarDecl* VD) : SymbolData(VD, ParmKind) {} + + ParmVarDecl* getDecl() const { return (ParmVarDecl*) getPtr(); } + + // Implement isa support. + static inline bool classof(const SymbolData* D) { + return D->getKind() == ParmKind; + } +}; + +class SymbolDataContentsOf : public SymbolData { +public: + SymbolDataContentsOf(SymbolID ID) : SymbolData(ID, ContentsOfKind) {} + + SymbolID getSymbol() const { return (SymbolID) getInt(); } + // Implement isa support. + static inline bool classof(const SymbolData* D) { + return D->getKind() == ContentsOfKind; + } +}; + + // Constraints on symbols. Usually wrapped by RValues. class SymIntConstraint : public llvm::FoldingSetNode { SymbolID Symbol; @@ -112,13 +161,22 @@ class SymbolManager { typedef llvm::DenseMap MapTy; MapTy DataToSymbol; + void* getKey(void* P) const { + return reinterpret_cast(reinterpret_cast(P) | 0x1); + } + + void* getKey(SymbolID sym) const { + return reinterpret_cast((uintptr_t) (sym << 1)); + } + public: SymbolManager(); ~SymbolManager(); SymbolID getSymbol(ParmVarDecl* D); + SymbolID getContentsOfSymbol(SymbolID sym); - inline SymbolData getSymbolData(SymbolID ID) const { + inline const SymbolData& getSymbolData(SymbolID ID) const { assert (ID < SymbolToData.size()); return SymbolToData[ID]; } @@ -175,30 +233,6 @@ public: } // end clang namespace -//==------------------------------------------------------------------------==// -// Casting machinery to get cast<> and dyn_cast<> working with SymbolData. -//==------------------------------------------------------------------------==// - -namespace llvm { - - template<> inline bool - isa(const clang::SymbolData& V) { - return V.getKind() == clang::SymbolData::ParmKind; - } - - template<> struct cast_retty_impl { - typedef const clang::ParmVarDecl* ret_type; - }; - - template<> struct simplify_type { - typedef void* SimpleType; - static inline SimpleType getSimplifiedValue(const clang::SymbolData &V) { - return V.getPtr(); - } - }; - -} // end llvm namespace - //==------------------------------------------------------------------------==// // Base RValue types. //==------------------------------------------------------------------------==// @@ -323,7 +357,7 @@ protected: NonLValue NE(ValueManager& ValMgr, const LValue& RHS) const; public: -// void print(std::ostream& Out) const; + void print(std::ostream& Out) const; RValue EvalBinaryOp(ValueManager& ValMgr, BinaryOperator::Opcode Op, const LValue& RHS) const; diff --git a/Analysis/ValueState.cpp b/Analysis/ValueState.cpp index b5a5985c44..20112c8ead 100644 --- a/Analysis/ValueState.cpp +++ b/Analysis/ValueState.cpp @@ -33,7 +33,8 @@ const llvm::APSInt* ValueState::getSymVal(SymbolID sym) const { -RValue ValueStateManager::GetValue(const StateTy& St, const LValue& LV) { +RValue ValueStateManager::GetValue(const StateTy& St, const LValue& LV, + QualType* T) { if (isa(LV)) return InvalidValue(); @@ -44,6 +45,19 @@ RValue ValueStateManager::GetValue(const StateTy& St, const LValue& LV) { return T ? T->getValue().second : InvalidValue(); } + + // FIXME: We should bind how far a "ContentsOf" will go... + + case lval::SymbolValKind: { + const lval::SymbolVal& SV = cast(LV); + assert (T); + + if (T->getTypePtr()->isPointerType()) + return lval::SymbolVal(SymMgr.getContentsOfSymbol(SV.getSymbol())); + else + return nonlval::SymbolVal(SymMgr.getContentsOfSymbol(SV.getSymbol())); + } + default: assert (false && "Invalid LValue."); break; diff --git a/Analysis/ValueState.h b/Analysis/ValueState.h index 1067531f5b..4f7d13850f 100644 --- a/Analysis/ValueState.h +++ b/Analysis/ValueState.h @@ -264,7 +264,7 @@ public: StateTy SetValue(StateTy St, const LValue& LV, const RValue& V); RValue GetValue(const StateTy& St, Stmt* S, bool* hasVal = NULL); - RValue GetValue(const StateTy& St, const LValue& LV); + RValue GetValue(const StateTy& St, const LValue& LV, QualType* T = NULL); LValue GetLValue(const StateTy& St, Stmt* S);