Added transfer function support for ReturnStmt to support detecting leaks
involving objects that are returned but have an excessive reference count. llvm-svn: 49861
This commit is contained in:
		
							parent
							
								
									36c49278b5
								
							
						
					
					
						commit
						a506fec90a
					
				| 
						 | 
					@ -26,6 +26,15 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace clang;
 | 
					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
 | 
					// Symbolic Evaluation of Reference Counting Logic
 | 
				
			||||||
//===----------------------------------------------------------------------===//
 | 
					//===----------------------------------------------------------------------===//
 | 
				
			||||||
| 
						 | 
					@ -429,7 +438,7 @@ namespace {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    virtual const char* getDescription() const {
 | 
					    virtual const char* getDescription() const {
 | 
				
			||||||
      return "(CoreFoundation) Reference-counted object is used"
 | 
					      return "(CoreFoundation) Reference-counted object is used"
 | 
				
			||||||
      " after it is released.";
 | 
					             " after it is released.";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    virtual void EmitWarnings(BugReporter& BR);
 | 
					    virtual void EmitWarnings(BugReporter& BR);
 | 
				
			||||||
| 
						 | 
					@ -445,8 +454,8 @@ namespace {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    virtual const char* getDescription() const {
 | 
					    virtual const char* getDescription() const {
 | 
				
			||||||
      return "Incorrect decrement of the reference count of a "
 | 
					      return "Incorrect decrement of the reference count of a "
 | 
				
			||||||
      "CoreFoundation object:\n"
 | 
					             "CoreFoundation object:\n"
 | 
				
			||||||
      "The object is not owned at this point by the caller.";
 | 
					             "The object is not owned at this point by the caller.";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    virtual void EmitWarnings(BugReporter& BR);
 | 
					    virtual void EmitWarnings(BugReporter& BR);
 | 
				
			||||||
| 
						 | 
					@ -461,28 +470,35 @@ namespace {
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
class VISIBILITY_HIDDEN RefVal {
 | 
					class VISIBILITY_HIDDEN RefVal {
 | 
				
			||||||
  unsigned Data;
 | 
					public:  
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  RefVal(unsigned K, unsigned D) : Data((D << 3) | K) {
 | 
					  enum Kind {
 | 
				
			||||||
    assert ((K & ~0x7) == 0x0);
 | 
					    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) {
 | 
					private:
 | 
				
			||||||
    assert ((K & ~0x7) == 0x0);
 | 
					  
 | 
				
			||||||
  }
 | 
					  Kind kind;
 | 
				
			||||||
 | 
					  unsigned Cnt;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  RefVal(Kind k, unsigned cnt) : kind(k), Cnt(cnt) {}
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  RefVal(Kind k) : kind(k), Cnt(0) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:  
 | 
					public:  
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  enum Kind { Owned = 0, NotOwned = 1, Released = 2,
 | 
					  Kind getKind() const { return kind; }
 | 
				
			||||||
              ErrorUseAfterRelease = 3, ErrorReleaseNotOwned = 4,
 | 
					 | 
				
			||||||
              ErrorLeak = 5 };    
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Kind getKind() const { return (Kind) (Data & 0x7); }
 | 
					  unsigned getCount() const { return Cnt; }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  unsigned getCount() const {
 | 
					  // Useful predicates.
 | 
				
			||||||
    assert (getKind() == Owned || getKind() == NotOwned);
 | 
					 | 
				
			||||||
    return Data >> 3;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
 | 
					  static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
| 
						 | 
					@ -496,6 +512,21 @@ public:
 | 
				
			||||||
    return getKind() == NotOwned;
 | 
					    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) {
 | 
					  static RefVal makeOwned(unsigned Count = 0) {
 | 
				
			||||||
    return RefVal(Owned, Count);
 | 
					    return RefVal(Owned, Count);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -504,13 +535,31 @@ public:
 | 
				
			||||||
    return RefVal(NotOwned, Count);
 | 
					    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 makeLeak() { return RefVal(ErrorLeak); }  
 | 
				
			||||||
  static RefVal makeReleased() { return RefVal(Released); }
 | 
					  static RefVal makeReleased() { return RefVal(Released); }
 | 
				
			||||||
  static RefVal makeUseAfterRelease() { return RefVal(ErrorUseAfterRelease); }
 | 
					  static RefVal makeUseAfterRelease() { return RefVal(ErrorUseAfterRelease); }
 | 
				
			||||||
  static RefVal makeReleaseNotOwned() { return RefVal(ErrorReleaseNotOwned); }
 | 
					  static RefVal makeReleaseNotOwned() { return RefVal(ErrorReleaseNotOwned); }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
  bool operator==(const RefVal& X) const { return Data == X.Data; }
 | 
					  // Comparison, profiling, and pretty-printing.
 | 
				
			||||||
  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;
 | 
					  void print(std::ostream& Out) const;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -526,7 +575,21 @@ void RefVal::print(std::ostream& Out) const {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
    case NotOwned: {
 | 
					    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();
 | 
					      unsigned cnt = getCount();
 | 
				
			||||||
      if (cnt) Out << " (+ " << cnt << ")";
 | 
					      if (cnt) Out << " (+ " << cnt << ")";
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
| 
						 | 
					@ -554,11 +617,6 @@ void RefVal::print(std::ostream& Out) const {
 | 
				
			||||||
// Transfer functions.
 | 
					// 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 {
 | 
					class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  // Type definitions.
 | 
					  // Type definitions.
 | 
				
			||||||
| 
						 | 
					@ -670,6 +728,14 @@ public:
 | 
				
			||||||
  virtual void EvalEndPath(GRExprEngine& Engine,
 | 
					  virtual void EvalEndPath(GRExprEngine& Engine,
 | 
				
			||||||
                           GREndPathNodeBuilder<ValueState>& Builder);
 | 
					                           GREndPathNodeBuilder<ValueState>& Builder);
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					  // Return statements.
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  virtual void EvalReturn(ExplodedNodeSet<ValueState>& Dst,
 | 
				
			||||||
 | 
					                          GRExprEngine& Engine,
 | 
				
			||||||
 | 
					                          GRStmtNodeBuilder<ValueState>& Builder,
 | 
				
			||||||
 | 
					                          ReturnStmt* S,
 | 
				
			||||||
 | 
					                          ExplodedNode<ValueState>* Pred);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
  // Error iterators.
 | 
					  // Error iterators.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  typedef UseAfterReleasesTy::iterator use_after_iterator;  
 | 
					  typedef UseAfterReleasesTy::iterator use_after_iterator;  
 | 
				
			||||||
| 
						 | 
					@ -1008,7 +1074,8 @@ ValueState* CFRefCount::HandleSymbolDeath(ValueStateManager& VMgr,
 | 
				
			||||||
                                          ValueState* St, SymbolID sid,
 | 
					                                          ValueState* St, SymbolID sid,
 | 
				
			||||||
                                          RefVal V, bool& hasLeak) {
 | 
					                                          RefVal V, bool& hasLeak) {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
  hasLeak = V.isOwned() || V.isNotOwned() && V.getCount() > 0;
 | 
					  hasLeak = V.isOwned() || 
 | 
				
			||||||
 | 
					            ((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!hasLeak)
 | 
					  if (!hasLeak)
 | 
				
			||||||
    return NukeBinding(VMgr, St, sid);
 | 
					    return NukeBinding(VMgr, St, sid);
 | 
				
			||||||
| 
						 | 
					@ -1043,6 +1110,66 @@ void CFRefCount::EvalEndPath(GRExprEngine& Eng,
 | 
				
			||||||
    Leaks.push_back(std::make_pair(*I, N));
 | 
					    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,
 | 
					CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
 | 
				
			||||||
                                           RefVal V, ArgEffect E,
 | 
					                                           RefVal V, ArgEffect E,
 | 
				
			||||||
| 
						 | 
					@ -1091,16 +1218,16 @@ CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
 | 
				
			||||||
          assert (false);
 | 
					          assert (false);
 | 
				
			||||||
          
 | 
					          
 | 
				
			||||||
        case RefVal::Owned: {
 | 
					        case RefVal::Owned: {
 | 
				
			||||||
          signed Count = ((signed) V.getCount()) - 1;
 | 
					          unsigned Count = V.getCount();
 | 
				
			||||||
          V = Count >= 0 ? RefVal::makeOwned(Count) : RefVal::makeReleased();
 | 
					          V = Count > 0 ? RefVal::makeOwned(Count - 1) : RefVal::makeReleased();
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
          
 | 
					          
 | 
				
			||||||
        case RefVal::NotOwned: {
 | 
					        case RefVal::NotOwned: {
 | 
				
			||||||
          signed Count = ((signed) V.getCount()) - 1;
 | 
					          unsigned Count = V.getCount();
 | 
				
			||||||
          
 | 
					          
 | 
				
			||||||
          if (Count >= 0)
 | 
					          if (Count > 0)
 | 
				
			||||||
            V = RefVal::makeNotOwned(Count);
 | 
					            V = RefVal::makeNotOwned(Count - 1);
 | 
				
			||||||
          else {
 | 
					          else {
 | 
				
			||||||
            V = RefVal::makeReleaseNotOwned();
 | 
					            V = RefVal::makeReleaseNotOwned();
 | 
				
			||||||
            hasErr = V.getKind();
 | 
					            hasErr = V.getKind();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue