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:
Ted Kremenek 2008-04-17 18:12:53 +00:00
parent 36c49278b5
commit a506fec90a
1 changed files with 160 additions and 33 deletions

View File

@ -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 { unsigned getCount() const { return Cnt; }
assert (getKind() == Owned || getKind() == NotOwned);
return Data >> 3; // Useful predicates.
}
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);
} }
@ -503,15 +534,33 @@ public:
static RefVal makeNotOwned(unsigned Count = 0) { static RefVal makeNotOwned(unsigned Count = 0) {
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); }
// Comparison, profiling, and pretty-printing.
bool operator==(const RefVal& X) const { return Data == X.Data; } bool operator==(const RefVal& X) const {
void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger(Data); } 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,12 +575,26 @@ void RefVal::print(std::ostream& Out) const {
} }
case NotOwned: { case NotOwned: {
Out << "Not-Owned"; Out << "NotOwned";
unsigned cnt = getCount(); unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")"; if (cnt) Out << " (+ " << cnt << ")";
break; 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: case Released:
Out << "Released"; Out << "Released";
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();