Fixed some logic errors in the CF ref count checker; we now can detect simple

use-after-release errors.  Added test case.

llvm-svn: 49509
This commit is contained in:
Ted Kremenek 2008-04-10 23:44:06 +00:00
parent de615836f3
commit 4b77209694
3 changed files with 59 additions and 22 deletions

View File

@ -48,7 +48,8 @@ namespace {
class RetEffect {
public:
enum Kind { Alias = 0x0, OwnedSymbol = 0x1, NotOwnedSymbol = 0x2 };
enum Kind { NoRet = 0x0, Alias = 0x1, OwnedSymbol = 0x2,
NotOwnedSymbol = 0x3 };
private:
unsigned Data;
@ -69,6 +70,8 @@ public:
static RetEffect MakeNotOwned() { return RetEffect(NotOwnedSymbol, 0); }
static RetEffect MakeNoRet() { return RetEffect(NoRet, 0); }
operator Kind() const { return getKind(); }
void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger(Data); }
@ -133,6 +136,10 @@ class CFRefSummaryManager {
CFRefSummary* getPersistentSummary(ArgEffects* AE, RetEffect RE);
CFRefSummary* getDoNothingSummary(unsigned Args);
void FillDoNothing(unsigned Args);
public:
CFRefSummaryManager(ASTContext& ctx) : Ctx(ctx) {}
~CFRefSummaryManager();
@ -158,8 +165,6 @@ CFRefSummaryManager::~CFRefSummaryManager() {
ArgEffects* CFRefSummaryManager::getArgEffects() {
assert (!ScratchArgs.empty());
llvm::FoldingSetNodeID profile;
profile.Add(ScratchArgs);
void* InsertPos;
@ -314,20 +319,22 @@ CFRefSummary* CFRefSummaryManager::getCannedCFSummary(FunctionTypeProto* FT,
// CFRetain: the return type should also be "CFTypeRef".
if (RetTy.getTypePtr() != ArgT)
return NULL;
// The function's interface checks out. Generate a canned summary.
assert (ScratchArgs.empty());
ScratchArgs.push_back(IncRef);
return getPersistentSummary(getArgEffects(), RetEffect::MakeAlias(0));
}
else {
// CFRelease: the return type should be void.
if (RetTy != Ctx.VoidTy)
return NULL;
}
// The function's interface checks out. Generate a canned summary.
assert (ScratchArgs.empty());
ScratchArgs.push_back(isRetain ? IncRef : DecRef);
return getPersistentSummary(getArgEffects(), RetEffect::MakeAlias(0));
assert (ScratchArgs.empty());
ScratchArgs.push_back(DecRef);
return getPersistentSummary(getArgEffects(), RetEffect::MakeNoRet());
}
}
static bool isCFRefType(QualType T) {
@ -354,21 +361,28 @@ static bool isCFRefType(QualType T) {
return true;
}
void CFRefSummaryManager::FillDoNothing(unsigned Args) {
for (unsigned i = 0; i != Args; ++i)
ScratchArgs.push_back(DoNothing);
}
CFRefSummary* CFRefSummaryManager::getDoNothingSummary(unsigned Args) {
FillDoNothing(Args);
return getPersistentSummary(getArgEffects(), RetEffect::MakeNoRet());
}
CFRefSummary*
CFRefSummaryManager::getCFSummaryCreateRule(FunctionTypeProto* FT) {
if (!isCFRefType(FT->getResultType()))
return NULL;
return getDoNothingSummary(FT->getNumArgs());
assert (ScratchArgs.empty());
// FIXME: Add special-cases for functions that retain/release. For now
// just handle the default case.
for (unsigned i = 0, n = FT->getNumArgs(); i != n; ++i)
ScratchArgs.push_back(DoNothing);
FillDoNothing(FT->getNumArgs());
return getPersistentSummary(getArgEffects(), RetEffect::MakeOwned());
}
@ -376,16 +390,14 @@ CFRefSummary*
CFRefSummaryManager::getCFSummaryGetRule(FunctionTypeProto* FT) {
if (!isCFRefType(FT->getResultType()))
return NULL;
return getDoNothingSummary(FT->getNumArgs());
assert (ScratchArgs.empty());
// FIXME: Add special-cases for functions that retain/release. For now
// just handle the default case.
for (unsigned i = 0, n = FT->getNumArgs(); i != n; ++i)
ScratchArgs.push_back(DoNothing);
FillDoNothing(FT->getNumArgs());
return getPersistentSummary(getArgEffects(), RetEffect::MakeNotOwned());
}
@ -711,11 +723,11 @@ void CFRefCount::EvalCall(ExplodedNodeSet<ValueState>& Dst,
if (hasError) {
St = StateMgr.getPersistentState(StVals);
GRExprEngine::NodeTy* N = Builder.generateNode(CE, St, Pred);
Builder.BuildSinks = true;
GRExprEngine::NodeTy* N = Builder.MakeNode(Dst, CE, Pred, St);
if (N) {
N->markAsSink();
switch (hasError) {
default: assert(false);
case RefVal::ErrorUseAfterRelease:
@ -741,6 +753,9 @@ void CFRefCount::EvalCall(ExplodedNodeSet<ValueState>& Dst,
default:
assert (false && "Unhandled RetEffect."); break;
case RetEffect::NoRet:
break;
case RetEffect::Alias: {
unsigned idx = RE.getValue();
assert (idx < CE->getNumArgs());
@ -810,7 +825,8 @@ CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
assert(false);
case RefVal::Owned:
V = RefVal::makeOwned(V.getCount()+1); break;
V = RefVal::makeOwned(V.getCount()+1);
break;
case RefVal::NotOwned:
V = RefVal::makeNotOwned(V.getCount()+1);
@ -822,6 +838,8 @@ CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
break;
}
break;
case DecRef:
switch (V.getKind()) {
default:
@ -851,6 +869,8 @@ CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
hasError = V.getKind();
break;
}
break;
}
return RefBFactory.Add(B, sym, V);

View File

@ -678,6 +678,8 @@ void GRExprEngine::VisitCall(CallExpr* CE, NodeTy* Pred,
unsigned size = Dst.size();
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
EvalCall(Dst, CE, cast<LVal>(L), *DI);
if (!Builder->BuildSinks && Dst.size() == size)

View File

@ -0,0 +1,15 @@
// RUN: clang -checker-cfref -verify %s
#include <CoreFoundation/CFDate.h>
CFAbsoluteTime f1() {
CFAbsoluteTime t = CFAbsoluteTimeGetCurrent();
CFDateRef date = CFDateCreate(NULL, t);
CFRetain(date);
CFRelease(date);
t = CFDateGetAbsoluteTime(date);
CFRelease(date);
t = CFDateGetAbsoluteTime(date); // expected-warning{{Reference-counted object is used after it is released.}}
return t;
}