Added transfer function support for ReturnStmt to support detecting leaks

involving objects that are returned but have an excessive reference count.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@49861 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Ted Kremenek 2008-04-17 18:12:53 +00:00
Родитель 165b954fb2
Коммит 4fd8897f81
1 изменённых файлов: 160 добавлений и 33 удалений

Просмотреть файл

@ -26,6 +26,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 @@ namespace {
}
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 @@ namespace {
}
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 {
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); }
Kind getKind() const { return kind; }
unsigned getCount() const {
assert (getKind() == Owned || getKind() == NotOwned);
return Data >> 3;
}
unsigned getCount() const { return Cnt; }
// Useful predicates.
static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
@ -496,6 +512,21 @@ public:
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 @@ public:
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 @@ void RefVal::print(std::ostream& Out) const {
}
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 @@ void RefVal::print(std::ostream& Out) const {
// 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 @@ public:
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* CFRefCount::HandleSymbolDeath(ValueStateManager& VMgr,
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 @@ void CFRefCount::EvalEndPath(GRExprEngine& Eng,
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 @@ CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
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();