]> granicus.if.org Git - clang/commitdiff
Added recording of "implicit" NULL dereferences of symbolic pointers.
authorTed Kremenek <kremenek@apple.com>
Thu, 7 Feb 2008 05:48:01 +0000 (05:48 +0000)
committerTed Kremenek <kremenek@apple.com>
Thu, 7 Feb 2008 05:48:01 +0000 (05:48 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@46843 91177308-0d34-0410-b5e6-96231b3b80d8

Analysis/GRConstants.cpp
Analysis/RValues.cpp
Analysis/RValues.h
Analysis/ValueState.cpp
Analysis/ValueState.h

index 399dacc96f7d0606938d853e9d598fa55876e6ab..44c5d51664686bef6a7fc1a664a421417281d8e3 100644 (file)
@@ -124,6 +124,12 @@ protected:
   typedef llvm::SmallPtrSet<NodeTy*,5> 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<NodeTy*,5> 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<NodeTy*>(N)) != 0;
   }
+  
+  bool isImplicitNullDeref(const NodeTy* N) const {
+    return N->isSink() && ImplicitNullDeref.count(const_cast<NodeTy*>(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<Stmt*>(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<LValue>(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<GRConstants::NodeTy*> :
             << (void*) L.getStmt() << ' ';
         
         L.getStmt()->printPretty(Out);
+        
+        if (GraphPrintCheckerState->isImplicitNullDeref(N)) {
+          Out << "\\|Implicit-Null Dereference.\\l";
+        }
+        
         break;
       }
     
index f006ed75013f82d4dfc6d9968151b3ee9da0a330..d0f60fbe63879bab27635ff3e325eca1f29e9999 100644 (file)
@@ -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<ParmVarDecl*>(getPtr())->getType();
+      return cast<SymbolDataParmVar>(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
+
index 8d5437fe6ec06a3f4b7c4dba8d9fede397561311..ce9b8a683a08a0e2e392de294e3e3468ff073b8e 100644 (file)
@@ -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<uintptr_t>(D)), K(k) {}
+  
+  void* getPtr() const { 
+    assert (K != UninitKind);
+    return reinterpret_cast<void*>(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<uintptr_t>(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<void*>(Data & ~Mask); }  
-  inline bool operator==(const SymbolData& R) const { return Data == R.Data; }
+  QualType getType() const;
   
-  QualType getType() const;  
+  // Implement isa<T> 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<T> 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<T> 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<void*,SymbolID> MapTy;
   MapTy DataToSymbol;
   
+  void* getKey(void* P) const {
+    return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(P) | 0x1);
+  }
+  
+  void* getKey(SymbolID sym) const {
+    return reinterpret_cast<void*>((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<clang::ParmVarDecl,clang::SymbolData>(const clang::SymbolData& V) {
-    return V.getKind() == clang::SymbolData::ParmKind;
-  }
-  
-  template<> struct cast_retty_impl<clang::ParmVarDecl,clang::SymbolData> {
-    typedef const clang::ParmVarDecl* ret_type;
-  };
-  
-  template<> struct simplify_type<clang::SymbolData> {
-    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;
index b5a5985c445dba1e532221713878b6250a46e0dd..20112c8eada7f0bf8f37b8894ca30c3ecfe2d30b 100644 (file)
@@ -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<InvalidValue>(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<lval::SymbolVal>(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;
index 1067531f5bbe637a9a3a6b191b4bfdea8163cbe4..4f7d13850f4ec8cba109f68408736aeae7375324 100644 (file)
@@ -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);