Newer
Older
static RefVal makeUseAfterRelease() { return RefVal(ErrorUseAfterRelease); }
static RefVal makeReleaseNotOwned() { return RefVal(ErrorReleaseNotOwned); }
// 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 ErrorUseAfterRelease:
Out << "Use-After-Release [ERROR]";
break;
case ErrorReleaseNotOwned:
Out << "Release of Not-Owned [ERROR]";
break;
}
}
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// Transfer functions.
//===----------------------------------------------------------------------===//
class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
public:
typedef llvm::ImmutableMap<SymbolID, RefVal> RefBindings;
typedef RefBindings::Factory RefBFactoryTy;
typedef llvm::DenseMap<GRExprEngine::NodeTy*,std::pair<Expr*, SymbolID> >
ReleasesNotOwnedTy;
typedef ReleasesNotOwnedTy UseAfterReleasesTy;
typedef llvm::DenseMap<GRExprEngine::NodeTy*, std::vector<SymbolID>*>
LeaksTy;
Ted Kremenek
committed
class BindingsPrinter : public ValueState::CheckerStatePrinter {
public:
virtual void PrintCheckerState(std::ostream& Out, void* State,
const char* nl, const char* sep);
};
private:
Ted Kremenek
committed
// Instance variables.
RetainSummaryManager Summaries;
const bool EmitStandardWarnings;
const LangOptions& LOpts;
RefBFactoryTy RefBFactory;
UseAfterReleasesTy UseAfterReleases;
ReleasesNotOwnedTy ReleasesNotOwned;
LeaksTy Leaks;
Ted Kremenek
committed
BindingsPrinter Printer;
Selector RetainSelector;
Selector ReleaseSelector;
Selector AutoreleaseSelector;
public:
static RefBindings GetRefBindings(ValueState& StImpl) {
return RefBindings((RefBindings::TreeTy*) StImpl.CheckerState);
}
private:
static void SetRefBindings(ValueState& StImpl, RefBindings B) {
StImpl.CheckerState = B.getRoot();
}
RefBindings Remove(RefBindings B, SymbolID sym) {
return RefBFactory.Remove(B, sym);
}
RefBindings Update(RefBindings B, SymbolID sym, RefVal V, ArgEffect E,
RefVal::Kind& hasErr);
void ProcessNonLeakError(ExplodedNodeSet<ValueState>& Dst,
GRStmtNodeBuilder<ValueState>& Builder,
Expr* NodeExpr, Expr* ErrorExpr,
ExplodedNode<ValueState>* Pred,
ValueState* St,
RefVal::Kind hasErr, SymbolID Sym);
ValueState* HandleSymbolDeath(ValueStateManager& VMgr, ValueState* St,
SymbolID sid, RefVal V, bool& hasLeak);
ValueState* NukeBinding(ValueStateManager& VMgr, ValueState* St,
SymbolID sid);
Ted Kremenek
committed
Ted Kremenek
committed
CFRefCount(ASTContext& Ctx, bool gcenabled, bool StandardWarnings,
const LangOptions& lopts)
: Summaries(Ctx, gcenabled),
Ted Kremenek
committed
EmitStandardWarnings(StandardWarnings),
LOpts(lopts),
RetainSelector(GetNullarySelector("retain", Ctx)),
ReleaseSelector(GetNullarySelector("release", Ctx)),
AutoreleaseSelector(GetNullarySelector("autorelease", Ctx)) {}
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 ValueState::CheckerStatePrinter* getCheckerStatePrinter() {
return &Printer;
}
bool isGCEnabled() const { return Summaries.isGCEnabled(); }
const LangOptions& getLangOptions() const { return LOpts; }
void EvalSummary(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
Expr* Ex,
Expr* Receiver,
RetainSummary* Summ,
ExprIterator arg_beg, ExprIterator arg_end,
ExplodedNode<ValueState>* Pred);
virtual void EvalCall(ExplodedNodeSet<ValueState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
ExplodedNode<ValueState>* Pred);
Ted Kremenek
committed
Ted Kremenek
committed
virtual void EvalObjCMessageExpr(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder<ValueState>& Builder,
ObjCMessageExpr* ME,
ExplodedNode<ValueState>* Pred);
bool EvalObjCMessageExprAux(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder<ValueState>& Builder,
ObjCMessageExpr* ME,
ExplodedNode<ValueState>* Pred);
Ted Kremenek
committed
// Stores.
virtual void EvalStore(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder<ValueState>& Builder,
Expr* E, ExplodedNode<ValueState>* Pred,
ValueState* St, RVal TargetLV, RVal Val);
Ted Kremenek
committed
// End-of-path.
virtual void EvalEndPath(GRExprEngine& Engine,
GREndPathNodeBuilder<ValueState>& Builder);
Ted Kremenek
committed
virtual void EvalDeadSymbols(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder<ValueState>& Builder,
ExplodedNode<ValueState>* Pred,
Stmt* S,
Ted Kremenek
committed
ValueState* St,
const ValueStateManager::DeadSymbolsTy& Dead);
// Return statements.
virtual void EvalReturn(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder<ValueState>& Builder,
ReturnStmt* S,
ExplodedNode<ValueState>* Pred);
// Assumptions.
virtual ValueState* EvalAssume(GRExprEngine& Engine, ValueState* St,
RVal 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
Ted Kremenek
committed
void CFRefCount::BindingsPrinter::PrintCheckerState(std::ostream& Out,
void* State, const char* nl,
const char* sep) {
RefBindings B((RefBindings::TreeTy*) State);
if (State)
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;
}
void CFRefCount::ProcessNonLeakError(ExplodedNodeSet<ValueState>& Dst,
GRStmtNodeBuilder<ValueState>& Builder,
Expr* NodeExpr, Expr* ErrorExpr,
ExplodedNode<ValueState>* Pred,
ValueState* St,
RefVal::Kind hasErr, SymbolID 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;
}
}
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
/// 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.getCanonicalType()->getAsPointerType();
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<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
Expr* Ex,
Expr* Receiver,
RetainSummary* Summ,
ExprIterator arg_beg, ExprIterator arg_end,
ExplodedNode<ValueState>* Pred) {
// Get the state.
ValueStateManager& StateMgr = Eng.getStateManager();
ValueState* St = Builder.GetState(Pred);
// Evaluate the effect of the arguments.
ValueState StVals = *St;
RefVal::Kind hasErr = (RefVal::Kind) 0;
unsigned idx = 0;
Expr* ErrorExpr = NULL;
SymbolID ErrorSym = 0;
for (ExprIterator I = arg_beg; I != arg_end; ++I, ++idx) {
RVal V = StateMgr.GetRVal(St, *I);
if (isa<lval::SymbolVal>(V)) {
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
RefBindings B = GetRefBindings(StVals);
if (RefBindings::TreeTy* T = B.SlimFind(Sym)) {
B = Update(B, Sym, T->getValue().second, GetArgE(Summ, idx), hasErr);
SetRefBindings(StVals, B);
if (hasErr) {
ErrorExpr = *I;
ErrorSym = T->getValue().first;
break;
}
else if (isa<LVal>(V)) {
// Nuke all arguments passed by reference.
StateMgr.Unbind(StVals, cast<LVal>(V));
else if (isa<nonlval::LValAsInteger>(V))
StateMgr.Unbind(StVals, cast<nonlval::LValAsInteger>(V).getLVal());
if (!ErrorExpr && Receiver) {
RVal V = StateMgr.GetRVal(St, Receiver);
if (isa<lval::SymbolVal>(V)) {
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
RefBindings B = GetRefBindings(StVals);
if (RefBindings::TreeTy* T = B.SlimFind(Sym)) {
B = Update(B, Sym, T->getValue().second, GetReceiverE(Summ), hasErr);
SetRefBindings(StVals, B);
if (hasErr) {
ErrorExpr = Receiver;
ErrorSym = T->getValue().first;
}
}
}
}
St = StateMgr.getPersistentState(StVals);
if (hasErr) {
ProcessNonLeakError(Dst, Builder, Ex, ErrorExpr, Pred, St,
hasErr, ErrorSym);
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.
if (Ex->getType() != Eng.getContext().VoidTy) {
unsigned Count = Builder.getCurrentBlockCount();
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
Ted Kremenek
committed
RVal X = LVal::IsLValType(Ex->getType())
? cast<RVal>(lval::SymbolVal(Sym))
: cast<RVal>(nonlval::SymbolVal(Sym));
St = StateMgr.SetRVal(St, Ex, X, Eng.getCFG().isBlkExpr(Ex), false);
Ted Kremenek
committed
break;
case RetEffect::Alias: {
assert (arg_end >= arg_beg);
assert (idx < (unsigned) (arg_end - arg_beg));
RVal V = StateMgr.GetRVal(St, *(arg_beg+idx));
St = StateMgr.SetRVal(St, Ex, V, Eng.getCFG().isBlkExpr(Ex), false);
break;
}
case RetEffect::ReceiverAlias: {
assert (Receiver);
RVal V = StateMgr.GetRVal(St, Receiver);
St = StateMgr.SetRVal(St, Ex, V, Eng.getCFG().isBlkExpr(Ex), false);
break;
}
case RetEffect::OwnedAllocatedSymbol:
case RetEffect::OwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
ValueState StImpl = *St;
RefBindings B = GetRefBindings(StImpl);
SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeOwned(RetT)));
St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl),
Ex, lval::SymbolVal(Sym),
Eng.getCFG().isBlkExpr(Ex), false);
// FIXME: Add a flag to the checker where allocations are allowed to fail.
if (RE.getKind() == RetEffect::OwnedAllocatedSymbol)
St = StateMgr.AddNE(St, Sym, Eng.getBasicVals().getZeroWithPtrWidth());
break;
}
case RetEffect::NotOwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(Ex, Count);
ValueState StImpl = *St;
RefBindings B = GetRefBindings(StImpl);
SetRefBindings(StImpl, RefBFactory.Add(B, Sym,
RefVal::makeNotOwned(RetT)));
St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl),
Ex, lval::SymbolVal(Sym),
Eng.getCFG().isBlkExpr(Ex), false);
break;
}
}
Builder.MakeNode(Dst, Ex, Pred, St);
}
void CFRefCount::EvalCall(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
CallExpr* CE, RVal L,
ExplodedNode<ValueState>* Pred) {
RetainSummary* Summ = NULL;
// Get the summary.
if (isa<lval::FuncVal>(L)) {
lval::FuncVal FV = cast<lval::FuncVal>(L);
FunctionDecl* FD = FV.getDecl();
}
EvalSummary(Dst, Eng, Builder, CE, 0, Summ,
CE->arg_begin(), CE->arg_end(), Pred);
Ted Kremenek
committed
}
Ted Kremenek
committed
void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
ObjCMessageExpr* ME,
ExplodedNode<ValueState>* Pred) {
RetainSummary* Summ;
Ted Kremenek
committed
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
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.
ValueState* St = Builder.GetState(Pred);
RVal V = Eng.getStateManager().GetRVal(St, Receiver );
if (isa<lval::SymbolVal>(V)) {
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
if (RefBindings::TreeTy* T = GetRefBindings(*St).SlimFind(Sym)) {
QualType Ty = T->getValue().second.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);
}
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
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
// Stores.
void CFRefCount::EvalStore(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
Expr* E, ExplodedNode<ValueState>* Pred,
ValueState* St, RVal TargetLV, RVal Val) {
// 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.
if (!isa<lval::SymbolVal>(Val))
return;
// Are we storing to something that causes the value to "escape"?
bool escapes = false;
if (!isa<lval::DeclVal>(TargetLV))
escapes = true;
else
escapes = cast<lval::DeclVal>(TargetLV).getDecl()->hasGlobalStorage();
if (!escapes)
return;
SymbolID Sym = cast<lval::SymbolVal>(Val).getSymbol();
RefBindings B = GetRefBindings(*St);
RefBindings::TreeTy* T = B.SlimFind(Sym);
if (!T)
return;
// Nuke the binding.
St = NukeBinding(Eng.getStateManager(), St, Sym);
Ted Kremenek
committed
// Hand of the remaining logic to the parent implementation.
GRSimpleVals::EvalStore(Dst, Eng, Builder, E, Pred, St, TargetLV, Val);
}
ValueState* CFRefCount::NukeBinding(ValueStateManager& VMgr, ValueState* St,
SymbolID sid) {
ValueState StImpl = *St;
RefBindings B = GetRefBindings(StImpl);
StImpl.CheckerState = RefBFactory.Remove(B, sid).getRoot();
return VMgr.getPersistentState(StImpl);
}
Ted Kremenek
committed
// End-of-path.
ValueState* CFRefCount::HandleSymbolDeath(ValueStateManager& VMgr,
ValueState* St, SymbolID sid,
RefVal V, bool& hasLeak) {
hasLeak = V.isOwned() ||
((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0);
if (!hasLeak)
return NukeBinding(VMgr, St, sid);
RefBindings B = GetRefBindings(*St);
ValueState StImpl = *St;
StImpl.CheckerState = RefBFactory.Add(B, sid, V^RefVal::ErrorLeak).getRoot();
return VMgr.getPersistentState(StImpl);
}
void CFRefCount::EvalEndPath(GRExprEngine& Eng,
Ted Kremenek
committed
GREndPathNodeBuilder<ValueState>& Builder) {
ValueState* St = Builder.getState();
RefBindings B = GetRefBindings(*St);
Ted Kremenek
committed
llvm::SmallVector<SymbolID, 10> Leaked;
Ted Kremenek
committed
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
bool hasLeak = false;
Ted Kremenek
committed
St = HandleSymbolDeath(Eng.getStateManager(), St,
(*I).first, (*I).second, hasLeak);
if (hasLeak) Leaked.push_back((*I).first);
}
Ted Kremenek
committed
if (Leaked.empty())
return;
ExplodedNode<ValueState>* N = Builder.MakeNode(St);
Ted Kremenek
committed
Ted Kremenek
committed
if (!N)
Ted Kremenek
committed
return;
std::vector<SymbolID>*& LeaksAtNode = Leaks[N];
assert (!LeaksAtNode);
LeaksAtNode = new std::vector<SymbolID>();
for (llvm::SmallVector<SymbolID, 10>::iterator 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<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
ExplodedNode<ValueState>* Pred,
Stmt* S,
Ted Kremenek
committed
ValueState* St,
const ValueStateManager::DeadSymbolsTy& Dead) {
Ted Kremenek
committed
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
// FIXME: a lot of copy-and-paste from EvalEndPath. Refactor.
RefBindings B = GetRefBindings(*St);
llvm::SmallVector<SymbolID, 10> Leaked;
for (ValueStateManager::DeadSymbolsTy::const_iterator
I=Dead.begin(), E=Dead.end(); I!=E; ++I) {
RefBindings::TreeTy* T = B.SlimFind(*I);
if (!T)
continue;
bool hasLeak = false;
St = HandleSymbolDeath(Eng.getStateManager(), St,
*I, T->getValue().second, hasLeak);
if (hasLeak) Leaked.push_back(*I);
}
if (Leaked.empty())
return;
ExplodedNode<ValueState>* N = Builder.MakeNode(Dst, S, Pred, St);
if (!N)
return;
std::vector<SymbolID>*& LeaksAtNode = Leaks[N];
assert (!LeaksAtNode);
LeaksAtNode = new std::vector<SymbolID>();
for (llvm::SmallVector<SymbolID, 10>::iterator I=Leaked.begin(),
E = Leaked.end(); I != E; ++I)
(*LeaksAtNode).push_back(*I);
}
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
// 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();
Ted Kremenek
committed
assert (cnt > 0);
X = RefVal::makeReturnedOwned(cnt - 1);
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
break;
}
case RefVal::NotOwned: {
unsigned cnt = X.getCount();
X = cnt ? RefVal::makeReturnedOwned(cnt - 1)
: RefVal::makeReturnedNotOwned();
break;
}
default:
return;
}
// Update the binding.
ValueState StImpl = *St;
StImpl.CheckerState = RefBFactory.Add(B, Sym, X).getRoot();
Builder.MakeNode(Dst, S, Pred, StateMgr.getPersistentState(StImpl));
}
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
// Assumptions.
ValueState* CFRefCount::EvalAssume(GRExprEngine& Eng, ValueState* St,
RVal Cond, bool Assumption,
bool& isFeasible) {
// FIXME: We may add to the interface of EvalAssume the list of symbols
// whose assumptions have changed. For now we just iterate through the
// bindings and check if any of the tracked symbols are NULL. This isn't
// too bad since the number of symbols we will track in practice are
// probably small and EvalAssume is only called at branches and a few
// other places.
RefBindings B = GetRefBindings(*St);
if (B.isEmpty())
return St;
bool changed = false;
for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
// Check if the symbol is null (or equal to any constant).
// If this is the case, stop tracking the symbol.
if (St->getSymVal(I.getKey())) {
changed = true;
B = RefBFactory.Remove(B, I.getKey());
}
}
if (!changed)
return St;
ValueState StImpl = *St;
StImpl.CheckerState = B.getRoot();
return Eng.getStateManager().getPersistentState(StImpl);
}
CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
RefVal V, ArgEffect E,
RefVal::Kind& hasErr) {
// FIXME: This dispatch can potentially be sped up by unifiying it into
// a single switch statement. Opt for simplicity for now.
switch (E) {
default:
assert (false && "Unhandled CFRef transition.");
Ted Kremenek
committed
case MayEscape:
if (V.getKind() == RefVal::Owned) {
Ted Kremenek
committed
break;
}
// Fall-through.
case DoNothing:
if (!isGCEnabled() && V.getKind() == RefVal::Released) {
hasErr = V.getKind();
break;
}
return B;
Ted Kremenek
committed
Ted Kremenek
committed
case Autorelease:
case StopTracking:
return RefBFactory.Remove(B, sym);
case IncRef:
switch (V.getKind()) {
default:
assert(false);
case RefVal::Owned:
case RefVal::NotOwned:
break;
case RefVal::Released:
if (isGCEnabled())
hasErr = V.getKind();
}
break;
}
Ted Kremenek
committed
break;
case DecRef:
switch (V.getKind()) {
default:
assert (false);
case RefVal::Owned:
V = V.getCount() > 1 ? V - 1 : V ^ RefVal::Released;
break;
case RefVal::NotOwned:
if (V.getCount() > 0)
V = V - 1;
hasErr = V.getKind();
break;
case RefVal::Released:
hasErr = V.getKind();
break;
}
Ted Kremenek
committed
break;
}
return RefBFactory.Add(B, sym, V);
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// Error reporting.
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
namespace {
//===-------------===//
// Bug Descriptions. //
//===-------------===//
class VISIBILITY_HIDDEN CFRefBug : public BugTypeCacheLocation {
protected:
CFRefCount& TF;
public:
CFRefBug(CFRefCount& tf) : TF(tf) {}
CFRefCount& getTF() { return TF; }
const CFRefCount& getTF() const { return TF; }
Ted Kremenek
committed
virtual bool isLeak() const { return false; }
};
class VISIBILITY_HIDDEN UseAfterRelease : public CFRefBug {
public:
UseAfterRelease(CFRefCount& tf) : CFRefBug(tf) {}
virtual const char* getName() const {
}
virtual const char* getDescription() const {
return "Reference-counted object is used"
" after it is released.";
}
virtual void EmitWarnings(BugReporter& BR);
};
class VISIBILITY_HIDDEN BadRelease : public CFRefBug {
public:
BadRelease(CFRefCount& tf) : CFRefBug(tf) {}
virtual const char* getName() const {
}
virtual const char* getDescription() const {
return "Incorrect decrement of the reference count of a "
"The object is not owned at this point by the caller.";
}
virtual void EmitWarnings(BugReporter& BR);
};
class VISIBILITY_HIDDEN Leak : public CFRefBug {
public:
Leak(CFRefCount& tf) : CFRefBug(tf) {}
virtual const char* getName() const {
if (getTF().isGCEnabled())
return "Memory Leak (GC)";
if (getTF().getLangOptions().getGCMode() == LangOptions::HybridGC)
return "Memory Leak (Hybrid MM, non-GC)";
assert (getTF().getLangOptions().getGCMode() == LangOptions::NonGC);
return "Memory Leak";
}
virtual const char* getDescription() const {
}
virtual void EmitWarnings(BugReporter& BR);
virtual void GetErrorNodes(std::vector<ExplodedNode<ValueState>*>& Nodes);
Ted Kremenek
committed
virtual bool isLeak() const { return true; }
virtual bool isCached(BugReport& R);
};
//===---------===//
// Bug Reports. //
//===---------===//
class VISIBILITY_HIDDEN CFRefReport : public RangedBugReport {
SymbolID Sym;