[cfe-commits] r49861 - /cfe/trunk/lib/Analysis/CFRefCount.cpp

Ted Kremenek kremenek at apple.com
Thu Apr 17 11:12:53 PDT 2008


Author: kremenek
Date: Thu Apr 17 13:12:53 2008
New Revision: 49861

URL: http://llvm.org/viewvc/llvm-project?rev=49861&view=rev
Log:
Added transfer function support for ReturnStmt to support detecting leaks
involving objects that are returned but have an excessive reference count.

Modified:
    cfe/trunk/lib/Analysis/CFRefCount.cpp

Modified: cfe/trunk/lib/Analysis/CFRefCount.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFRefCount.cpp?rev=49861&r1=49860&r2=49861&view=diff

==============================================================================
--- cfe/trunk/lib/Analysis/CFRefCount.cpp (original)
+++ cfe/trunk/lib/Analysis/CFRefCount.cpp Thu Apr 17 13:12:53 2008
@@ -27,6 +27,15 @@
 using namespace clang;
 
 //===----------------------------------------------------------------------===//
+// Utility functions.
+//===----------------------------------------------------------------------===//
+
+static inline Selector GetUnarySelector(const char* name, ASTContext& Ctx) {
+  IdentifierInfo* II = &Ctx.Idents.get(name);
+  return Ctx.Selectors.getSelector(0, &II);
+}
+
+//===----------------------------------------------------------------------===//
 // Symbolic Evaluation of Reference Counting Logic
 //===----------------------------------------------------------------------===//
 
@@ -429,7 +438,7 @@
     }
     virtual const char* getDescription() const {
       return "(CoreFoundation) Reference-counted object is used"
-      " after it is released.";
+             " after it is released.";
     }
     
     virtual void EmitWarnings(BugReporter& BR);
@@ -445,8 +454,8 @@
     }
     virtual const char* getDescription() const {
       return "Incorrect decrement of the reference count of a "
-      "CoreFoundation object:\n"
-      "The object is not owned at this point by the caller.";
+             "CoreFoundation object:\n"
+             "The object is not owned at this point by the caller.";
     }
     
     virtual void EmitWarnings(BugReporter& BR);
@@ -461,28 +470,35 @@
 namespace {
   
 class VISIBILITY_HIDDEN RefVal {
-  unsigned Data;
+public:  
   
-  RefVal(unsigned K, unsigned D) : Data((D << 3) | K) {
-    assert ((K & ~0x7) == 0x0);
-  }
+  enum Kind {
+    Owned = 0, // Owning reference.    
+    NotOwned,  // Reference is not owned by still valid (not freed).    
+    Released,  // Object has been released.
+    ReturnedOwned, // Returned object passes ownership to caller.
+    ReturnedNotOwned, // Return object does not pass ownership to caller.
+    ErrorUseAfterRelease, // Object used after released.    
+    ErrorReleaseNotOwned, // Release of an object that was not owned.
+    ErrorLeak  // A memory leak due to excessive reference counts.
+  };
   
-  RefVal(unsigned K) : Data(K) {
-    assert ((K & ~0x7) == 0x0);
-  }
+private:
+  
+  Kind kind;
+  unsigned Cnt;
+    
+  RefVal(Kind k, unsigned cnt) : kind(k), Cnt(cnt) {}
+  
+  RefVal(Kind k) : kind(k), Cnt(0) {}
 
 public:  
   
-  enum Kind { Owned = 0, NotOwned = 1, Released = 2,
-              ErrorUseAfterRelease = 3, ErrorReleaseNotOwned = 4,
-              ErrorLeak = 5 };    
-  
-  Kind getKind() const { return (Kind) (Data & 0x7); }
-
-  unsigned getCount() const {
-    assert (getKind() == Owned || getKind() == NotOwned);
-    return Data >> 3;
-  }
+  Kind getKind() const { return kind; }
+
+  unsigned getCount() const { return Cnt; }
+  
+  // Useful predicates.
   
   static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
   
@@ -496,6 +512,21 @@
     return getKind() == NotOwned;
   }
   
+  bool isReturnedOwned() const {
+    return getKind() == ReturnedOwned;
+  }
+  
+  bool isReturnedNotOwned() const {
+    return getKind() == ReturnedNotOwned;
+  }
+  
+  bool isNonLeakError() const {
+    Kind k = getKind();
+    return isError(k) && !isLeak(k);
+  }
+  
+  // State creation: normal state.
+  
   static RefVal makeOwned(unsigned Count = 0) {
     return RefVal(Owned, Count);
   }
@@ -503,15 +534,33 @@
   static RefVal makeNotOwned(unsigned Count = 0) {
     return RefVal(NotOwned, Count);
   }
+
+  static RefVal makeReturnedOwned(unsigned Count) {
+    return RefVal(ReturnedOwned, Count);
+  }
+  
+  static RefVal makeReturnedNotOwned() {
+    return RefVal(ReturnedNotOwned);
+  }
+  
+  // State creation: errors.
   
   static RefVal makeLeak() { return RefVal(ErrorLeak); }  
   static RefVal makeReleased() { return RefVal(Released); }
   static RefVal makeUseAfterRelease() { return RefVal(ErrorUseAfterRelease); }
   static RefVal makeReleaseNotOwned() { return RefVal(ErrorReleaseNotOwned); }
+    
+  // Comparison, profiling, and pretty-printing.
   
-  bool operator==(const RefVal& X) const { return Data == X.Data; }
-  void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger(Data); }
+  bool operator==(const RefVal& X) const {
+    return kind == X.kind && Cnt == X.Cnt;
+  }
   
+  void Profile(llvm::FoldingSetNodeID& ID) const {
+    ID.AddInteger((unsigned) kind);
+    ID.AddInteger(Cnt);
+  }
+
   void print(std::ostream& Out) const;
 };
   
@@ -526,12 +575,26 @@
     }
       
     case NotOwned: {
-      Out << "Not-Owned";
+      Out << "NotOwned";
+      unsigned cnt = getCount();
+      if (cnt) Out << " (+ " << cnt << ")";
+      break;
+    }
+      
+    case ReturnedOwned: { 
+      Out << "ReturnedOwned";
       unsigned cnt = getCount();
       if (cnt) Out << " (+ " << cnt << ")";
       break;
     }
       
+    case ReturnedNotOwned: {
+      Out << "ReturnedNotOwned";
+      unsigned cnt = getCount();
+      if (cnt) Out << " (+ " << cnt << ")";
+      break;
+    }
+            
     case Released:
       Out << "Released";
       break;
@@ -554,11 +617,6 @@
 // Transfer functions.
 //===----------------------------------------------------------------------===//
 
-static inline Selector GetUnarySelector(const char* name, ASTContext& Ctx) {
-  IdentifierInfo* II = &Ctx.Idents.get(name);
-  return Ctx.Selectors.getSelector(0, &II);
-}
-  
 class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
   
   // Type definitions.
@@ -670,6 +728,14 @@
   virtual void EvalEndPath(GRExprEngine& Engine,
                            GREndPathNodeBuilder<ValueState>& Builder);
   
+  // Return statements.
+  
+  virtual void EvalReturn(ExplodedNodeSet<ValueState>& Dst,
+                          GRExprEngine& Engine,
+                          GRStmtNodeBuilder<ValueState>& Builder,
+                          ReturnStmt* S,
+                          ExplodedNode<ValueState>* Pred);
+  
   // Error iterators.
 
   typedef UseAfterReleasesTy::iterator use_after_iterator;  
@@ -1008,7 +1074,8 @@
                                           ValueState* St, SymbolID sid,
                                           RefVal V, bool& hasLeak) {
     
-  hasLeak = V.isOwned() || V.isNotOwned() && V.getCount() > 0;
+  hasLeak = V.isOwned() || 
+            ((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0);
 
   if (!hasLeak)
     return NukeBinding(VMgr, St, sid);
@@ -1043,6 +1110,66 @@
     Leaks.push_back(std::make_pair(*I, N));
 }
 
+ // Return statements.
+
+void CFRefCount::EvalReturn(ExplodedNodeSet<ValueState>& Dst,
+                            GRExprEngine& Eng,
+                            GRStmtNodeBuilder<ValueState>& Builder,
+                            ReturnStmt* S,
+                            ExplodedNode<ValueState>* Pred) {
+  
+  Expr* RetE = S->getRetValue();
+  if (!RetE) return;
+  
+  ValueStateManager& StateMgr = Eng.getStateManager();
+  ValueState* St = Builder.GetState(Pred);
+  RVal V = StateMgr.GetRVal(St, RetE);
+  
+  if (!isa<lval::SymbolVal>(V))
+    return;
+  
+  // Get the reference count binding (if any).
+  SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
+  RefBindings B = GetRefBindings(*St);
+  RefBindings::TreeTy* T = B.SlimFind(Sym);
+  
+  if (!T)
+    return;
+  
+  // Change the reference count.
+  
+  RefVal X = T->getValue().second;  
+  
+  switch (X.getKind()) {
+      
+    case RefVal::Owned: { 
+      unsigned cnt = X.getCount();
+      X = RefVal::makeReturnedOwned(cnt);
+      break;
+    }
+      
+    case RefVal::NotOwned: {
+      unsigned cnt = X.getCount();
+      X = cnt ? RefVal::makeReturnedOwned(cnt - 1)
+              : RefVal::makeReturnedNotOwned();
+      break;
+    }
+      
+    default: 
+      // None of the error states should be possible at this point.
+      // A symbol could not have been leaked (yet) if we are returning it
+      // (and thus it is still live), and the other errors are hard errors.
+      assert(false);
+      return;
+  }
+  
+  // Update the binding.
+
+  ValueState StImpl = *St;
+  StImpl.CheckerState = RefBFactory.Add(B, Sym, X).getRoot();        
+  Builder.MakeNode(Dst, S, Pred, StateMgr.getPersistentState(StImpl));
+}
+
 
 CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
                                            RefVal V, ArgEffect E,
@@ -1091,16 +1218,16 @@
           assert (false);
           
         case RefVal::Owned: {
-          signed Count = ((signed) V.getCount()) - 1;
-          V = Count >= 0 ? RefVal::makeOwned(Count) : RefVal::makeReleased();
+          unsigned Count = V.getCount();
+          V = Count > 0 ? RefVal::makeOwned(Count - 1) : RefVal::makeReleased();
           break;
         }
           
         case RefVal::NotOwned: {
-          signed Count = ((signed) V.getCount()) - 1;
+          unsigned Count = V.getCount();
           
-          if (Count >= 0)
-            V = RefVal::makeNotOwned(Count);
+          if (Count > 0)
+            V = RefVal::makeNotOwned(Count - 1);
           else {
             V = RefVal::makeReleaseNotOwned();
             hasErr = V.getKind();





More information about the cfe-commits mailing list