Newer
Older
addNSObjectMethSummary(GetNullarySelector("copy", Ctx), Summ);
Ted Kremenek
committed
// Create the "mutableCopy" selector.
addNSObjectMethSummary(GetNullarySelector("mutableCopy", Ctx), Summ);
// Create the "retain" selector.
E = RetEffect::MakeReceiverAlias();
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : IncRef);
addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ);
// Create the "release" selector.
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef);
addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ);
// Create the "drain" selector.
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef);
addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ);
// Create the "autorelease" selector.
Ted Kremenek
committed
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : Autorelease);
addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ);
// For NSWindow, allocated objects are (initially) self-owned.
RetainSummary *NSWindowSumm =
getPersistentSummary(RetEffect::MakeReceiverAlias(), SelfOwn);
addInstMethSummary("NSWindow", NSWindowSumm, "initWithContentRect",
"styleMask", "backing", "defer", NULL);
addInstMethSummary("NSWindow", NSWindowSumm, "initWithContentRect",
"styleMask", "backing", "defer", "screen", NULL);
Ted Kremenek
committed
// For NSPanel (which subclasses NSWindow), allocated objects are not
// self-owned.
addInstMethSummary("NSPanel", InitSumm, "initWithContentRect",
"styleMask", "backing", "defer", NULL);
Ted Kremenek
committed
addInstMethSummary("NSPanel", InitSumm, "initWithContentRect",
"styleMask", "backing", "defer", "screen", NULL);
// Create NSAssertionHandler summaries.
Ted Kremenek
committed
addPanicSummary("NSAssertionHandler", "handleFailureInFunction", "file",
"lineNumber", "description", NULL);
addPanicSummary("NSAssertionHandler", "handleFailureInMethod", "object",
"file", "lineNumber", "description", NULL);
Ted Kremenek
committed
}
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
// Reference-counting logic (typestate + counts).
//===----------------------------------------------------------------------===//
namespace {
class VISIBILITY_HIDDEN RefVal {
public:
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.
Ted Kremenek
committed
ErrorLeak, // A memory leak due to excessive reference counts.
ErrorLeakReturned // A memory leak due to the returning method not having
// the correct naming conventions.
};
private:
Kind kind;
unsigned Cnt;
QualType T;
RefVal(Kind k, unsigned cnt, QualType t) : kind(k), Cnt(cnt), T(t) {}
RefVal(Kind k, unsigned cnt = 0) : kind(k), Cnt(cnt) {}
public:
Kind getKind() const { return kind; }
unsigned getCount() const { return Cnt; }
QualType getType() const { return T; }
// Useful predicates.
static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
Ted Kremenek
committed
static bool isLeak(Kind k) { return k >= ErrorLeak; }
Ted Kremenek
committed
bool isOwned() const {
return getKind() == Owned;
}
bool isNotOwned() const {
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(QualType t, unsigned Count = 1) {
return RefVal(Owned, Count, t);
static RefVal makeNotOwned(QualType t, unsigned Count = 0) {
return RefVal(NotOwned, Count, t);
static RefVal makeReturnedOwned(unsigned Count) {
return RefVal(ReturnedOwned, Count);
}
static RefVal makeReturnedNotOwned() {
return RefVal(ReturnedNotOwned);
}
// Comparison, profiling, and pretty-printing.
bool operator==(const RefVal& X) const {
}
Ted Kremenek
committed
RefVal operator-(size_t i) const {
return RefVal(getKind(), getCount() - i, getType());
}
RefVal operator+(size_t i) const {
return RefVal(getKind(), getCount() + i, getType());
}
RefVal operator^(Kind k) const {
return RefVal(k, getCount(), getType());
}
void Profile(llvm::FoldingSetNodeID& ID) const {
ID.AddInteger((unsigned) kind);
ID.AddInteger(Cnt);
}
Ted Kremenek
committed
void print(std::ostream& Out) const;
Ted Kremenek
committed
void RefVal::print(std::ostream& Out) const {
if (!T.isNull())
Out << "Tracked Type:" << T.getAsString() << '\n';
Ted Kremenek
committed
switch (getKind()) {
default: assert(false);
case Owned: {
Out << "Owned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
Ted Kremenek
committed
break;
Ted Kremenek
committed
Out << "NotOwned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
break;
}
case ReturnedOwned: {
Out << "ReturnedOwned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
Ted Kremenek
committed
break;
Ted Kremenek
committed
case ReturnedNotOwned: {
Out << "ReturnedNotOwned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
break;
}
Ted Kremenek
committed
case Released:
Out << "Released";
break;
case ErrorLeak:
Out << "Leaked";
break;
Ted Kremenek
committed
case ErrorLeakReturned:
Out << "Leaked (Bad naming)";
break;
Ted Kremenek
committed
case ErrorUseAfterRelease:
Out << "Use-After-Release [ERROR]";
break;
case ErrorReleaseNotOwned:
Out << "Release of Not-Owned [ERROR]";
break;
}
}
Ted Kremenek
committed
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// RefBindings - State used to track object reference counts.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
typedef llvm::ImmutableMap<SymbolRef, RefVal> RefBindings;
Ted Kremenek
committed
static int RefBIndex = 0;
namespace clang {
Ted Kremenek
committed
template<>
struct GRStateTrait<RefBindings> : public GRStatePartialTrait<RefBindings> {
static inline void* GDMIndex() { return &RefBIndex; }
};
}
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// ARBindings - State used to track objects in autorelease pools.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
typedef llvm::ImmutableSet<SymbolRef> ARPoolContents;
typedef llvm::ImmutableList< std::pair<SymbolRef, ARPoolContents*> > ARBindings;
Ted Kremenek
committed
static int AutoRBIndex = 0;
namespace clang {
template<>
struct GRStateTrait<ARBindings> : public GRStatePartialTrait<ARBindings> {
static inline void* GDMIndex() { return &AutoRBIndex; }
};
}
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// Transfer functions.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
namespace {
class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
public:
Ted Kremenek
committed
typedef llvm::DenseMap<GRExprEngine::NodeTy*,std::pair<Expr*, SymbolRef> >
ReleasesNotOwnedTy;
Ted Kremenek
committed
typedef ReleasesNotOwnedTy UseAfterReleasesTy;
Ted Kremenek
committed
typedef llvm::DenseMap<GRExprEngine::NodeTy*,
Ted Kremenek
committed
std::vector<std::pair<SymbolRef,bool> >*>
LeaksTy;
class BindingsPrinter : public GRState::Printer {
Ted Kremenek
committed
public:
virtual void Print(std::ostream& Out, const GRState* state,
const char* nl, const char* sep);
Ted Kremenek
committed
};
private:
RetainSummaryManager Summaries;
const LangOptions& LOpts;
Ted Kremenek
committed
Ted Kremenek
committed
UseAfterReleasesTy UseAfterReleases;
ReleasesNotOwnedTy ReleasesNotOwned;
LeaksTy Leaks;
Ted Kremenek
committed
RefBindings Update(RefBindings B, SymbolRef sym, RefVal V, ArgEffect E,
Ted Kremenek
committed
RefVal::Kind& hasErr, RefBindings::Factory& RefBFactory);
Ted Kremenek
committed
RefVal::Kind& Update(GRStateRef& state, SymbolRef sym, RefVal V,
Ted Kremenek
committed
ArgEffect E, RefVal::Kind& hasErr) {
state = state.set<RefBindings>(Update(state.get<RefBindings>(), sym, V,
Ted Kremenek
committed
E, hasErr,
state.get_context<RefBindings>()));
Ted Kremenek
committed
return hasErr;
}
void ProcessNonLeakError(ExplodedNodeSet<GRState>& Dst,
GRStmtNodeBuilder<GRState>& Builder,
Expr* NodeExpr, Expr* ErrorExpr,
ExplodedNode<GRState>* Pred,
const GRState* St,
Ted Kremenek
committed
RefVal::Kind hasErr, SymbolRef Sym);
Ted Kremenek
committed
std::pair<GRStateRef, bool>
HandleSymbolDeath(GRStateManager& VMgr, const GRState* St,
Ted Kremenek
committed
const Decl* CD, SymbolRef sid, RefVal V, bool& hasLeak);
Ted Kremenek
committed
Ted Kremenek
committed
CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts)
: Summaries(Ctx, gcenabled),
Ted Kremenek
committed
LOpts(lopts) {}
virtual ~CFRefCount() {
for (LeaksTy::iterator I = Leaks.begin(), E = Leaks.end(); I!=E; ++I)
delete I->second;
}
virtual void RegisterChecks(GRExprEngine& Eng);
Ted Kremenek
committed
virtual void RegisterPrinters(std::vector<GRState::Printer*>& Printers) {
Printers.push_back(new BindingsPrinter());
Ted Kremenek
committed
}
bool isGCEnabled() const { return Summaries.isGCEnabled(); }
const LangOptions& getLangOptions() const { return LOpts; }
void EvalSummary(ExplodedNodeSet<GRState>& Dst,
GRExprEngine& Eng,
Expr* Ex,
Expr* Receiver,
RetainSummary* Summ,
ExprIterator arg_beg, ExprIterator arg_end,
virtual void EvalCall(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
Zhongxing Xu
committed
CallExpr* CE, SVal L,
Ted Kremenek
committed
virtual void EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Engine,
Ted Kremenek
committed
ObjCMessageExpr* ME,
Ted Kremenek
committed
bool EvalObjCMessageExprAux(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Engine,
Ted Kremenek
committed
ObjCMessageExpr* ME,
Ted Kremenek
committed
Ted Kremenek
committed
// Stores.
virtual void EvalStore(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Engine,
GRStmtNodeBuilder<GRState>& Builder,
Expr* E, ExplodedNode<GRState>* Pred,
Zhongxing Xu
committed
const GRState* St, SVal TargetLV, SVal Val);
Ted Kremenek
committed
// End-of-path.
virtual void EvalEndPath(GRExprEngine& Engine,
Ted Kremenek
committed
virtual void EvalDeadSymbols(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Engine,
GRStmtNodeBuilder<GRState>& Builder,
ExplodedNode<GRState>* Pred,
Ted Kremenek
committed
Stmt* S, const GRState* state,
SymbolReaper& SymReaper);
// Return statements.
virtual void EvalReturn(ExplodedNodeSet<GRState>& Dst,
GRExprEngine& Engine,
ReturnStmt* S,
// Assumptions.
virtual const GRState* EvalAssume(GRStateManager& VMgr,
Zhongxing Xu
committed
const GRState* St, SVal Cond,
bool Assumption, bool& isFeasible);
Ted Kremenek
committed
// Error iterators.
typedef UseAfterReleasesTy::iterator use_after_iterator;
typedef ReleasesNotOwnedTy::iterator bad_release_iterator;
typedef LeaksTy::iterator leaks_iterator;
Ted Kremenek
committed
use_after_iterator use_after_begin() { return UseAfterReleases.begin(); }
use_after_iterator use_after_end() { return UseAfterReleases.end(); }
Ted Kremenek
committed
bad_release_iterator bad_release_begin() { return ReleasesNotOwned.begin(); }
bad_release_iterator bad_release_end() { return ReleasesNotOwned.end(); }
leaks_iterator leaks_begin() { return Leaks.begin(); }
leaks_iterator leaks_end() { return Leaks.end(); }
};
} // end anonymous namespace
void CFRefCount::BindingsPrinter::Print(std::ostream& Out, const GRState* state,
const char* nl, const char* sep) {
Ted Kremenek
committed
RefBindings B = state->get<RefBindings>();
Ted Kremenek
committed
if (!B.isEmpty())
Ted Kremenek
committed
Out << sep << nl;
for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
Out << (*I).first << " : ";
(*I).second.print(Out);
Out << nl;
}
}
static inline ArgEffect GetArgE(RetainSummary* Summ, unsigned idx) {
Ted Kremenek
committed
return Summ ? Summ->getArg(idx) : MayEscape;
static inline RetEffect GetRetEffect(RetainSummary* Summ) {
return Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet();
static inline ArgEffect GetReceiverE(RetainSummary* Summ) {
return Summ ? Summ->getReceiverEffect() : DoNothing;
}
static inline bool IsEndPath(RetainSummary* Summ) {
return Summ ? Summ->isEndPath() : false;
}
void CFRefCount::ProcessNonLeakError(ExplodedNodeSet<GRState>& Dst,
GRStmtNodeBuilder<GRState>& Builder,
Expr* NodeExpr, Expr* ErrorExpr,
ExplodedNode<GRState>* Pred,
const GRState* St,
Ted Kremenek
committed
RefVal::Kind hasErr, SymbolRef Sym) {
Builder.BuildSinks = true;
GRExprEngine::NodeTy* N = Builder.MakeNode(Dst, NodeExpr, Pred, St);
if (!N) return;
switch (hasErr) {
default: assert(false);
case RefVal::ErrorUseAfterRelease:
UseAfterReleases[N] = std::make_pair(ErrorExpr, Sym);
break;
case RefVal::ErrorReleaseNotOwned:
ReleasesNotOwned[N] = std::make_pair(ErrorExpr, Sym);
break;
}
}
/// GetReturnType - Used to get the return type of a message expression or
/// function call with the intention of affixing that type to a tracked symbol.
/// While the the return type can be queried directly from RetEx, when
/// invoking class methods we augment to the return type to be that of
/// a pointer to the class (as opposed it just being id).
static QualType GetReturnType(Expr* RetE, ASTContext& Ctx) {
QualType RetTy = RetE->getType();
// FIXME: We aren't handling id<...>.
const PointerType* PT = RetTy->getAsPointerType();
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
if (!PT)
return RetTy;
// If RetEx is not a message expression just return its type.
// If RetEx is a message expression, return its types if it is something
/// more specific than id.
ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(RetE);
if (!ME || !Ctx.isObjCIdType(PT->getPointeeType()))
return RetTy;
ObjCInterfaceDecl* D = ME->getClassInfo().first;
// At this point we know the return type of the message expression is id.
// If we have an ObjCInterceDecl, we know this is a call to a class method
// whose type we can resolve. In such cases, promote the return type to
// Class*.
return !D ? RetTy : Ctx.getPointerType(Ctx.getObjCInterfaceType(D));
}
void CFRefCount::EvalSummary(ExplodedNodeSet<GRState>& Dst,
GRExprEngine& Eng,
Expr* Ex,
Expr* Receiver,
RetainSummary* Summ,
ExprIterator arg_beg, ExprIterator arg_end,
// Get the state.
Ted Kremenek
committed
GRStateRef state(Builder.GetState(Pred), Eng.getStateManager());
Ted Kremenek
committed
ASTContext& Ctx = Eng.getStateManager().getContext();
// Evaluate the effect of the arguments.
RefVal::Kind hasErr = (RefVal::Kind) 0;
unsigned idx = 0;
Expr* ErrorExpr = NULL;
Ted Kremenek
committed
SymbolRef ErrorSym = 0;
Ted Kremenek
committed
for (ExprIterator I = arg_beg; I != arg_end; ++I, ++idx) {
Zhongxing Xu
committed
SVal V = state.GetSVal(*I);
Zhongxing Xu
committed
if (isa<loc::SymbolVal>(V)) {
Ted Kremenek
committed
SymbolRef Sym = cast<loc::SymbolVal>(V).getSymbol();
Ted Kremenek
committed
if (RefBindings::data_type* T = state.get<RefBindings>(Sym))
if (Update(state, Sym, *T, GetArgE(Summ, idx), hasErr)) {
ErrorExpr = *I;
Ted Kremenek
committed
ErrorSym = Sym;
break;
}
Zhongxing Xu
committed
else if (isa<Loc>(V)) {
if (loc::MemRegionVal* MR = dyn_cast<loc::MemRegionVal>(&V)) {
if (GetArgE(Summ, idx) == DoNothingByRef)
continue;
// Invalidate the value of the variable passed by reference.
// FIXME: Either this logic should also be replicated in GRSimpleVals
// or should be pulled into a separate "constraint engine."
// FIXME: We can have collisions on the conjured symbol if the
// expression *I also creates conjured symbols. We probably want
// to identify conjured symbols by an expression pair: the enclosing
// expression (the context) and the expression itself. This should
// disambiguate conjured symbols.
Ted Kremenek
committed
const TypedRegion* R = dyn_cast<TypedRegion>(MR->getRegion());
// Blast through AnonTypedRegions to get the original region type.
while (R) {
const AnonTypedRegion* ATR = dyn_cast<AnonTypedRegion>(R);
if (!ATR) break;
R = dyn_cast<TypedRegion>(ATR->getSuperRegion());
}
Ted Kremenek
committed
if (R) {
// Is the invalidated variable something that we were tracking?
SVal X = state.GetSVal(Loc::MakeVal(R));
if (isa<loc::SymbolVal>(X)) {
SymbolRef Sym = cast<loc::SymbolVal>(X).getSymbol();
state = state.remove<RefBindings>(Sym);
}
Ted Kremenek
committed
// Set the value of the variable to be a conjured symbol.
unsigned Count = Builder.getCurrentBlockCount();
Ted Kremenek
committed
Ted Kremenek
committed
// FIXME: handle structs.
Ted Kremenek
committed
if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) {
Ted Kremenek
committed
SymbolRef NewSym =
Ted Kremenek
committed
Eng.getSymbolManager().getConjuredSymbol(*I, T, Count);
Ted Kremenek
committed
Loc::IsLocType(T)
? cast<SVal>(loc::SymbolVal(NewSym))
: cast<SVal>(nonloc::SymbolVal(NewSym)));
}
else {
state = state.BindLoc(*MR, UnknownVal());
Ted Kremenek
committed
}
Ted Kremenek
committed
}
else
state = state.BindLoc(*MR, UnknownVal());
}
else {
// Nuke all other arguments passed by reference.
Zhongxing Xu
committed
state = state.Unbind(cast<Loc>(V));
Zhongxing Xu
committed
else if (isa<nonloc::LocAsInteger>(V))
state = state.Unbind(cast<nonloc::LocAsInteger>(V).getLoc());
Zhongxing Xu
committed
SVal V = state.GetSVal(Receiver);
if (isa<loc::SymbolVal>(V)) {
Ted Kremenek
committed
SymbolRef Sym = cast<loc::SymbolVal>(V).getSymbol();
Ted Kremenek
committed
if (const RefVal* T = state.get<RefBindings>(Sym))
if (Update(state, Sym, *T, GetReceiverE(Summ), hasErr)) {
Ted Kremenek
committed
ErrorSym = Sym;
if (hasErr) {
Ted Kremenek
committed
ProcessNonLeakError(Dst, Builder, Ex, ErrorExpr, Pred, state,
hasErr, ErrorSym);
// Consult the summary for the return value.
RetEffect RE = GetRetEffect(Summ);
switch (RE.getKind()) {
default:
assert (false && "Unhandled RetEffect."); break;
Ted Kremenek
committed
case RetEffect::NoRet: {
// Make up a symbol for the return value (not reference counted).
// FIXME: This is basically copy-and-paste from GRSimpleVals. We
// should compose behavior, not copy it.
Ted Kremenek
committed
// FIXME: We eventually should handle structs and other compound types
// that are returned by value.
QualType T = Ex->getType();
Ted Kremenek
committed
if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) {
unsigned Count = Builder.getCurrentBlockCount();
Ted Kremenek
committed
SymbolRef Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
Zhongxing Xu
committed
SVal X = Loc::IsLocType(Ex->getType())
? cast<SVal>(loc::SymbolVal(Sym))
: cast<SVal>(nonloc::SymbolVal(Sym));
state = state.BindExpr(Ex, X, false);
Ted Kremenek
committed
break;
Ted Kremenek
committed
}
Ted Kremenek
committed
case RetEffect::Alias: {
assert (arg_end >= arg_beg);
assert (idx < (unsigned) (arg_end - arg_beg));
Zhongxing Xu
committed
SVal V = state.GetSVal(*(arg_beg+idx));
state = state.BindExpr(Ex, V, false);
break;
}
case RetEffect::ReceiverAlias: {
assert (Receiver);
Zhongxing Xu
committed
SVal V = state.GetSVal(Receiver);
state = state.BindExpr(Ex, V, false);
case RetEffect::OwnedAllocatedSymbol:
case RetEffect::OwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
Ted Kremenek
committed
SymbolRef Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
Ted Kremenek
committed
state = state.set<RefBindings>(Sym, RefVal::makeOwned(RetT));
state = state.BindExpr(Ex, loc::SymbolVal(Sym), false);
Ted Kremenek
committed
#if 0
RefBindings B = GetRefBindings(StImpl);
SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeOwned(RetT)));
Ted Kremenek
committed
#endif
// FIXME: Add a flag to the checker where allocations are allowed to fail.
if (RE.getKind() == RetEffect::OwnedAllocatedSymbol)
Ted Kremenek
committed
state = state.AddNE(Sym, Eng.getBasicVals().getZeroWithPtrWidth());
break;
}
case RetEffect::NotOwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
Ted Kremenek
committed
SymbolRef Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
Ted Kremenek
committed
state = state.set<RefBindings>(Sym, RefVal::makeNotOwned(RetT));
state = state.BindExpr(Ex, loc::SymbolVal(Sym), false);
break;
}
}
// Is this a sink?
if (IsEndPath(Summ))
Ted Kremenek
committed
Builder.MakeSinkNode(Dst, Ex, Pred, state);
Ted Kremenek
committed
Builder.MakeNode(Dst, Ex, Pred, state);
}
void CFRefCount::EvalCall(ExplodedNodeSet<GRState>& Dst,
GRExprEngine& Eng,
Zhongxing Xu
committed
CallExpr* CE, SVal L,
Zhongxing Xu
committed
RetainSummary* Summ = !isa<loc::FuncVal>(L) ? 0
: Summaries.getSummary(cast<loc::FuncVal>(L).getDecl());
EvalSummary(Dst, Eng, Builder, CE, 0, Summ,
CE->arg_begin(), CE->arg_end(), Pred);
Ted Kremenek
committed
}
void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
Ted Kremenek
committed
ObjCMessageExpr* ME,
RetainSummary* Summ;
Ted Kremenek
committed
if (Expr* Receiver = ME->getReceiver()) {
// We need the type-information of the tracked receiver object
// Retrieve it from the state.
ObjCInterfaceDecl* ID = 0;
// FIXME: Wouldn't it be great if this code could be reduced? It's just
// a chain of lookups.
const GRState* St = Builder.GetState(Pred);
Zhongxing Xu
committed
SVal V = Eng.getStateManager().GetSVal(St, Receiver );
Zhongxing Xu
committed
if (isa<loc::SymbolVal>(V)) {
Ted Kremenek
committed
SymbolRef Sym = cast<loc::SymbolVal>(V).getSymbol();
Ted Kremenek
committed
if (const RefVal* T = St->get<RefBindings>(Sym)) {
Ted Kremenek
committed
QualType Ty = T->getType();
if (const PointerType* PT = Ty->getAsPointerType()) {
QualType PointeeTy = PT->getPointeeType();
if (ObjCInterfaceType* IT = dyn_cast<ObjCInterfaceType>(PointeeTy))
ID = IT->getDecl();
}
}
}
Summ = Summaries.getMethodSummary(ME, ID);
Ted Kremenek
committed
Ted Kremenek
committed
// Special-case: are we sending a mesage to "self"?
// This is a hack. When we have full-IP this should be removed.
if (!Summ) {
ObjCMethodDecl* MD =
dyn_cast<ObjCMethodDecl>(&Eng.getGraph().getCodeDecl());
if (MD) {
if (Expr* Receiver = ME->getReceiver()) {
SVal X = Eng.getStateManager().GetSVal(St, Receiver);
if (loc::MemRegionVal* L = dyn_cast<loc::MemRegionVal>(&X))
Ted Kremenek
committed
if (L->getRegion() == Eng.getStateManager().getSelfRegion(St)) {
// Create a summmary where all of the arguments "StopTracking".
Summ = Summaries.getPersistentSummary(RetEffect::MakeNoRet(),
DoNothing,
StopTracking);
}
Ted Kremenek
committed
}
}
}
else
Ted Kremenek
committed
Summ = Summaries.getClassMethodSummary(ME->getClassName(),
ME->getSelector());
EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), Summ,
ME->arg_begin(), ME->arg_end(), Pred);
Ted Kremenek
committed
}
Ted Kremenek
committed
// Stores.
void CFRefCount::EvalStore(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
GRStmtNodeBuilder<GRState>& Builder,
Expr* E, ExplodedNode<GRState>* Pred,
Zhongxing Xu
committed
const GRState* St, SVal TargetLV, SVal Val) {
Ted Kremenek
committed
// Check if we have a binding for "Val" and if we are storing it to something
// we don't understand or otherwise the value "escapes" the function.
Zhongxing Xu
committed
if (!isa<loc::SymbolVal>(Val))
Ted Kremenek
committed
return;
// Are we storing to something that causes the value to "escape"?
bool escapes = false;
// A value escapes in three possible cases (this may change):
//
// (1) we are binding to something that is not a memory region.
// (2) we are binding to a memregion that does not have stack storage
// (3) we are binding to a memregion with stack storage that the store
// does not understand.
Ted Kremenek
committed
SymbolRef Sym = cast<loc::SymbolVal>(Val).getSymbol();
GRStateRef state(St, Eng.getStateManager());
Zhongxing Xu
committed
if (!isa<loc::MemRegionVal>(TargetLV))
Ted Kremenek
committed
escapes = true;
Ted Kremenek
committed
else {
const MemRegion* R = cast<loc::MemRegionVal>(TargetLV).getRegion();
Ted Kremenek
committed
escapes = !Eng.getStateManager().hasStackStorage(R);
if (!escapes) {
// To test (3), generate a new state with the binding removed. If it is
// the same state, then it escapes (since the store cannot represent
// the binding).
GRStateRef stateNew = state.BindLoc(cast<Loc>(TargetLV), Val);
escapes = (stateNew == state);
}
Ted Kremenek
committed
}
Ted Kremenek
committed
if (!escapes)
return;
// Do we have a reference count binding?
// FIXME: Is this step even needed? We do blow away the binding anyway.
Ted Kremenek
committed
if (!state.get<RefBindings>(Sym))
return;
Ted Kremenek
committed
Ted Kremenek
committed
// Nuke the binding.
Ted Kremenek
committed
state = state.remove<RefBindings>(Sym);
Ted Kremenek
committed
Ted Kremenek
committed
// Hand of the remaining logic to the parent implementation.
GRSimpleVals::EvalStore(Dst, Eng, Builder, E, Pred, state, TargetLV, Val);
}
Ted Kremenek
committed
// End-of-path.
Ted Kremenek
committed
Ted Kremenek
committed
std::pair<GRStateRef,bool>
CFRefCount::HandleSymbolDeath(GRStateManager& VMgr,
const GRState* St, const Decl* CD,
Ted Kremenek
committed
SymbolRef sid,
Ted Kremenek
committed
RefVal V, bool& hasLeak) {
Ted Kremenek
committed
GRStateRef state(St, VMgr);
Sanjiv Gupta
committed
assert ((!V.isReturnedOwned() || CD) &&
Ted Kremenek
committed
"CodeDecl must be available for reporting ReturnOwned errors.");
Ted Kremenek
committed
Ted Kremenek
committed
if (V.isReturnedOwned() && V.getCount() == 0)
if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) {
std::string s = MD->getSelector().getAsString();
if (!followsReturnRule(s.c_str())) {
Ted Kremenek
committed
hasLeak = true;
Ted Kremenek
committed
state = state.set<RefBindings>(sid, V ^ RefVal::ErrorLeakReturned);
return std::make_pair(state, true);
Ted Kremenek
committed
}
}
Ted Kremenek
committed
Ted Kremenek
committed
// All other cases.
hasLeak = V.isOwned() ||
((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0);
Ted Kremenek
committed
if (!hasLeak)
Ted Kremenek
committed
return std::make_pair(state.remove<RefBindings>(sid), false);
Ted Kremenek
committed
return std::make_pair(state.set<RefBindings>(sid, V ^ RefVal::ErrorLeak),
false);
}
void CFRefCount::EvalEndPath(GRExprEngine& Eng,
Ted Kremenek
committed
Ted Kremenek
committed
RefBindings B = St->get<RefBindings>();
Ted Kremenek
committed
Ted Kremenek
committed
llvm::SmallVector<std::pair<SymbolRef, bool>, 10> Leaked;
Ted Kremenek
committed
const Decl* CodeDecl = &Eng.getGraph().getCodeDecl();
Ted Kremenek
committed
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
bool hasLeak = false;
Ted Kremenek
committed
Ted Kremenek
committed
std::pair<GRStateRef, bool> X =
HandleSymbolDeath(Eng.getStateManager(), St, CodeDecl,
(*I).first, (*I).second, hasLeak);
Ted Kremenek
committed
St = X.first;
if (hasLeak) Leaked.push_back(std::make_pair((*I).first, X.second));
Ted Kremenek
committed
if (Leaked.empty())
return;
ExplodedNode<GRState>* N = Builder.MakeNode(St);
Ted Kremenek
committed
Ted Kremenek
committed
if (!N)
Ted Kremenek
committed
return;
Ted Kremenek
committed
std::vector<std::pair<SymbolRef,bool> >*& LeaksAtNode = Leaks[N];
assert (!LeaksAtNode);
Ted Kremenek
committed
LeaksAtNode = new std::vector<std::pair<SymbolRef,bool> >();
Ted Kremenek
committed
for (llvm::SmallVector<std::pair<SymbolRef,bool>, 10>::iterator
Ted Kremenek
committed
I = Leaked.begin(), E = Leaked.end(); I != E; ++I)
(*LeaksAtNode).push_back(*I);
Ted Kremenek
committed
}
Ted Kremenek
committed
// Dead symbols.
void CFRefCount::EvalDeadSymbols(ExplodedNodeSet<GRState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
GRStmtNodeBuilder<GRState>& Builder,
ExplodedNode<GRState>* Pred,
Ted Kremenek
committed
SymbolReaper& SymReaper) {
Ted Kremenek
committed
// FIXME: a lot of copy-and-paste from EvalEndPath. Refactor.
Ted Kremenek
committed
RefBindings B = St->get<RefBindings>();
Ted Kremenek
committed
llvm::SmallVector<std::pair<SymbolRef,bool>, 10> Leaked;
Ted Kremenek
committed
Ted Kremenek
committed
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
E = SymReaper.dead_end(); I != E; ++I) {
Ted Kremenek
committed
Ted Kremenek
committed
const RefVal* T = B.lookup(*I);
Ted Kremenek
committed
if (!T) continue;
Ted Kremenek
committed
bool hasLeak = false;
Ted Kremenek
committed
std::pair<GRStateRef, bool> X
= HandleSymbolDeath(Eng.getStateManager(), St, 0, *I, *T, hasLeak);
St = X.first;
Ted Kremenek
committed
Ted Kremenek
committed
if (hasLeak)
Ted Kremenek
committed
Leaked.push_back(std::make_pair(*I,X.second));
Ted Kremenek
committed
}
if (Leaked.empty())
return;
ExplodedNode<GRState>* N = Builder.MakeNode(Dst, S, Pred, St);
Ted Kremenek
committed
if (!N)
return;
Ted Kremenek
committed
std::vector<std::pair<SymbolRef,bool> >*& LeaksAtNode = Leaks[N];
Ted Kremenek
committed
assert (!LeaksAtNode);
Ted Kremenek
committed
LeaksAtNode = new std::vector<std::pair<SymbolRef,bool> >();
Ted Kremenek
committed
Ted Kremenek
committed
for (llvm::SmallVector<std::pair<SymbolRef,bool>, 10>::iterator
Ted Kremenek
committed
I = Leaked.begin(), E = Leaked.end(); I != E; ++I)
Ted Kremenek
committed
(*LeaksAtNode).push_back(*I);
}
// Return statements.
void CFRefCount::EvalReturn(ExplodedNodeSet<GRState>& Dst,
GRExprEngine& Eng,
ReturnStmt* S,
Expr* RetE = S->getRetValue();
if (!RetE) return;
Ted Kremenek
committed
GRStateRef state(Builder.GetState(Pred), Eng.getStateManager());
Zhongxing Xu
committed
SVal V = state.GetSVal(RetE);
Zhongxing Xu
committed
if (!isa<loc::SymbolVal>(V))
return;
// Get the reference count binding (if any).
Ted Kremenek
committed
SymbolRef Sym = cast<loc::SymbolVal>(V).getSymbol();
Ted Kremenek
committed
const RefVal* T = state.get<RefBindings>(Sym);
if (!T)
return;
Ted Kremenek
committed
// Change the reference count.
Ted Kremenek
committed
RefVal X = *T;
Ted Kremenek
committed
switch (X.getKind()) {
case RefVal::Owned: {
unsigned cnt = X.getCount();
Ted Kremenek
committed
assert (cnt > 0);
X = RefVal::makeReturnedOwned(cnt - 1);
break;
}
case RefVal::NotOwned: {
unsigned cnt = X.getCount();
X = cnt ? RefVal::makeReturnedOwned(cnt - 1)
: RefVal::makeReturnedNotOwned();
break;
}
default:
return;
}
// Update the binding.