Newer
Older
// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--//
Ted Kremenek
committed
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the methods for CFRefCount, which implements
Ted Kremenek
committed
// a reference count checker for Core Foundation (Mac OS X).
//
//===----------------------------------------------------------------------===//
#include "GRSimpleVals.h"
Ted Kremenek
committed
#include "clang/Analysis/PathSensitive/ValueState.h"
Ted Kremenek
committed
#include "clang/Analysis/PathDiagnostic.h"
Ted Kremenek
committed
#include "clang/Analysis/LocalCheckers.h"
Ted Kremenek
committed
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Analysis/PathSensitive/BugReporter.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableMap.h"
Ted Kremenek
committed
#include "llvm/Support/Compiler.h"
Ted Kremenek
committed
#include <ostream>
Ted Kremenek
committed
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
//===----------------------------------------------------------------------===//
namespace {
enum ArgEffect { IncRef, DecRef, DoNothing };
typedef std::vector<ArgEffect> ArgEffects;
}
Ted Kremenek
committed
namespace llvm {
template <> struct FoldingSetTrait<ArgEffects> {
static void Profile(const ArgEffects& X, FoldingSetNodeID& ID) {
for (ArgEffects::const_iterator I = X.begin(), E = X.end(); I!= E; ++I)
ID.AddInteger((unsigned) *I);
};
} // end llvm namespace
namespace {
class RetEffect {
public:
Ted Kremenek
committed
enum Kind { NoRet = 0x0, Alias = 0x1, OwnedSymbol = 0x2,
NotOwnedSymbol = 0x3 };
private:
unsigned Data;
RetEffect(Kind k, unsigned D) { Data = (D << 2) | (unsigned) k; }
public:
Kind getKind() const { return (Kind) (Data & 0x3); }
unsigned getValue() const {
assert(getKind() == Alias);
return Data >> 2;
Ted Kremenek
committed
static RetEffect MakeAlias(unsigned Idx) { return RetEffect(Alias, Idx); }
static RetEffect MakeOwned() { return RetEffect(OwnedSymbol, 0); }
static RetEffect MakeNotOwned() { return RetEffect(NotOwnedSymbol, 0); }
Ted Kremenek
committed
static RetEffect MakeNoRet() { return RetEffect(NoRet, 0); }
operator Kind() const { return getKind(); }
void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger(Data); }
};
class CFRefSummary : public llvm::FoldingSetNode {
ArgEffects* Args;
RetEffect Ret;
public:
CFRefSummary(ArgEffects* A, RetEffect R) : Args(A), Ret(R) {}
unsigned getNumArgs() const { return Args->size(); }
ArgEffect getArg(unsigned idx) const {
assert (idx < getNumArgs());
return (*Args)[idx];
}
RetEffect getRet() const {
return Ret;
}
typedef ArgEffects::const_iterator arg_iterator;
arg_iterator begin_args() const { return Args->begin(); }
arg_iterator end_args() const { return Args->end(); }
Ted Kremenek
committed
static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects* A, RetEffect R) {
ID.AddPointer(A);
ID.Add(R);
}
void Profile(llvm::FoldingSetNodeID& ID) const {
Profile(ID, Args, Ret);
}
};
Ted Kremenek
committed
class CFRefSummaryManager {
typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<ArgEffects> > AESetTy;
typedef llvm::FoldingSet<CFRefSummary> SummarySetTy;
typedef llvm::DenseMap<FunctionDecl*, CFRefSummary*> SummaryMapTy;
ASTContext& Ctx;
SummarySetTy SummarySet;
SummaryMapTy SummaryMap;
AESetTy AESet;
llvm::BumpPtrAllocator BPAlloc;
ArgEffects ScratchArgs;
ArgEffects* getArgEffects();
CFRefSummary* getCannedCFSummary(FunctionTypeProto* FT, bool isRetain);
CFRefSummary* getCFSummary(FunctionDecl* FD, const char* FName);
CFRefSummary* getCFSummaryCreateRule(FunctionTypeProto* FT);
CFRefSummary* getCFSummaryGetRule(FunctionTypeProto* FT);
CFRefSummary* getPersistentSummary(ArgEffects* AE, RetEffect RE);
Ted Kremenek
committed
void FillDoNothing(unsigned Args);
CFRefSummaryManager(ASTContext& ctx) : Ctx(ctx) {}
~CFRefSummaryManager();
Ted Kremenek
committed
CFRefSummary* getSummary(FunctionDecl* FD, ASTContext& Ctx);
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Implementation of checker data structures.
//===----------------------------------------------------------------------===//
CFRefSummaryManager::~CFRefSummaryManager() {
Ted Kremenek
committed
// FIXME: The ArgEffects could eventually be allocated from BPAlloc,
// mitigating the need to do explicit cleanup of the
// Argument-Effect summaries.
Ted Kremenek
committed
for (AESetTy::iterator I = AESet.begin(), E = AESet.end(); I!=E; ++I)
I->getValue().~ArgEffects();
Ted Kremenek
committed
}
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
ArgEffects* CFRefSummaryManager::getArgEffects() {
llvm::FoldingSetNodeID profile;
profile.Add(ScratchArgs);
void* InsertPos;
llvm::FoldingSetNodeWrapper<ArgEffects>* E =
AESet.FindNodeOrInsertPos(profile, InsertPos);
if (E) {
ScratchArgs.clear();
return &E->getValue();
}
E = (llvm::FoldingSetNodeWrapper<ArgEffects>*)
BPAlloc.Allocate<llvm::FoldingSetNodeWrapper<ArgEffects> >();
new (E) llvm::FoldingSetNodeWrapper<ArgEffects>(ScratchArgs);
AESet.InsertNode(E, InsertPos);
ScratchArgs.clear();
return &E->getValue();
}
CFRefSummary* CFRefSummaryManager::getPersistentSummary(ArgEffects* AE,
RetEffect RE) {
llvm::FoldingSetNodeID profile;
CFRefSummary::Profile(profile, AE, RE);
void* InsertPos;
CFRefSummary* Summ = SummarySet.FindNodeOrInsertPos(profile, InsertPos);
if (Summ)
return Summ;
Summ = (CFRefSummary*) BPAlloc.Allocate<CFRefSummary>();
new (Summ) CFRefSummary(AE, RE);
SummarySet.InsertNode(Summ, InsertPos);
return Summ;
}
CFRefSummary* CFRefSummaryManager::getSummary(FunctionDecl* FD,
ASTContext& Ctx) {
SourceLocation Loc = FD->getLocation();
if (!Loc.isFileID())
return NULL;
{ // Look into our cache of summaries to see if we have already computed
// a summary for this FunctionDecl.
SummaryMapTy::iterator I = SummaryMap.find(FD);
if (I != SummaryMap.end())
return I->second;
}
#if 0
SourceManager& SrcMgr = Ctx.getSourceManager();
unsigned fid = Loc.getFileID();
const FileEntry* FE = SrcMgr.getFileEntryForID(fid);
if (!FE)
return NULL;
const char* DirName = FE->getDir()->getName();
assert (DirName);
assert (strlen(DirName) > 0);
if (!strstr(DirName, "CoreFoundation")) {
SummaryMap[FD] = NULL;
return NULL;
}
#endif
const char* FName = FD->getIdentifier()->getName();
if (FName[0] == 'C' && FName[1] == 'F') {
CFRefSummary* S = getCFSummary(FD, FName);
SummaryMap[FD] = S;
return S;
}
Ted Kremenek
committed
Ted Kremenek
committed
}
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
CFRefSummary* CFRefSummaryManager::getCFSummary(FunctionDecl* FD,
const char* FName) {
// For now, only generate summaries for functions that have a prototype.
FunctionTypeProto* FT =
dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
if (!FT)
return NULL;
FName += 2;
if (strcmp(FName, "Retain") == 0)
return getCannedCFSummary(FT, true);
if (strcmp(FName, "Release") == 0)
return getCannedCFSummary(FT, false);
assert (ScratchArgs.empty());
bool usesCreateRule = false;
if (strstr(FName, "Create"))
usesCreateRule = true;
if (!usesCreateRule && strstr(FName, "Copy"))
usesCreateRule = true;
if (usesCreateRule)
return getCFSummaryCreateRule(FT);
if (strstr(FName, "Get"))
return getCFSummaryGetRule(FT);
return NULL;
}
CFRefSummary* CFRefSummaryManager::getCannedCFSummary(FunctionTypeProto* FT,
bool isRetain) {
if (FT->getNumArgs() != 1)
return NULL;
TypedefType* ArgT = dyn_cast<TypedefType>(FT->getArgType(0).getTypePtr());
if (!ArgT)
return NULL;
// For CFRetain/CFRelease, the first (and only) argument is of type
// "CFTypeRef".
const char* TDName = ArgT->getDecl()->getIdentifier()->getName();
assert (TDName);
if (strcmp("CFTypeRef", TDName) != 0)
return NULL;
if (!ArgT->isPointerType())
return NULL;
QualType RetTy = FT->getResultType();
if (isRetain) {
// CFRetain: the return type should also be "CFTypeRef".
if (RetTy.getTypePtr() != ArgT)
return NULL;
Ted Kremenek
committed
// 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;
Ted Kremenek
committed
assert (ScratchArgs.empty());
ScratchArgs.push_back(DecRef);
return getPersistentSummary(getArgEffects(), RetEffect::MakeNoRet());
}
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
}
static bool isCFRefType(QualType T) {
if (!T->isPointerType())
return false;
// Check the typedef for the name "CF" and the substring "Ref".
TypedefType* TD = dyn_cast<TypedefType>(T.getTypePtr());
if (!TD)
return false;
const char* TDName = TD->getDecl()->getIdentifier()->getName();
assert (TDName);
if (TDName[0] != 'C' || TDName[1] != 'F')
return false;
if (strstr(TDName, "Ref") == 0)
return false;
return true;
}
Ted Kremenek
committed
void CFRefSummaryManager::FillDoNothing(unsigned Args) {
for (unsigned i = 0; i != Args; ++i)
ScratchArgs.push_back(DoNothing);
}
CFRefSummary*
CFRefSummaryManager::getCFSummaryCreateRule(FunctionTypeProto* FT) {
if (!isCFRefType(FT->getResultType()))
return NULL;
assert (ScratchArgs.empty());
// FIXME: Add special-cases for functions that retain/release. For now
// just handle the default case.
Ted Kremenek
committed
FillDoNothing(FT->getNumArgs());
return getPersistentSummary(getArgEffects(), RetEffect::MakeOwned());
}
CFRefSummary*
CFRefSummaryManager::getCFSummaryGetRule(FunctionTypeProto* FT) {
QualType RetTy = FT->getResultType();
// FIXME: For now we assume that all pointer types returned are referenced
// counted. Since this is the "Get" rule, we assume non-ownership, which
// works fine for things that are not reference counted. We do this because
// some generic data structures return "void*". We need something better
// in the future.
if (!isCFRefType(RetTy) && !RetTy->isPointerType())
return NULL;
assert (ScratchArgs.empty());
// FIXME: Add special-cases for functions that retain/release. For now
// just handle the default case.
Ted Kremenek
committed
FillDoNothing(FT->getNumArgs());
return getPersistentSummary(getArgEffects(), RetEffect::MakeNotOwned());
}
//===----------------------------------------------------------------------===//
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.
ErrorLeak // A memory leak due to excessive reference counts.
};
private:
Kind kind;
unsigned Cnt;
RefVal(Kind k, unsigned cnt) : kind(k), Cnt(cnt) {}
RefVal(Kind k) : kind(k), Cnt(0) {}
public:
Kind getKind() const { return kind; }
unsigned getCount() const { return Cnt; }
// Useful predicates.
static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
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(unsigned Count = 0) {
return RefVal(Owned, Count);
}
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 kind == X.kind && Cnt == X.Cnt;
}
Ted Kremenek
committed
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 {
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:
Ted Kremenek
committed
// Type definitions.
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.
CFRefSummaryManager Summaries;
RefBFactoryTy RefBFactory;
UseAfterReleasesTy UseAfterReleases;
ReleasesNotOwnedTy ReleasesNotOwned;
LeaksTy Leaks;
Ted Kremenek
committed
BindingsPrinter Printer;
Selector RetainSelector;
Selector ReleaseSelector;
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
CFRefCount(ASTContext& Ctx)
: Summaries(Ctx),
RetainSelector(GetUnarySelector("retain", Ctx)),
ReleaseSelector(GetUnarySelector("release", 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;
}
// Calls.
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);
// 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(CFRefSummary* Summ, unsigned idx) {
return Summ ? Summ->getArg(idx) : DoNothing;
}
static inline RetEffect GetRetE(CFRefSummary* Summ) {
return Summ ? Summ->getRet() : RetEffect::MakeNoRet();
}
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;
}
}
Ted Kremenek
committed
void CFRefCount::EvalCall(ExplodedNodeSet<ValueState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
ExplodedNode<ValueState>* Pred) {
Ted Kremenek
committed
ValueStateManager& StateMgr = Eng.getStateManager();
Ted Kremenek
committed
CFRefSummary* Summ = NULL;
Ted Kremenek
committed
// Get the summary.
Ted Kremenek
committed
if (isa<lval::FuncVal>(L)) {
lval::FuncVal FV = cast<lval::FuncVal>(L);
FunctionDecl* FD = FV.getDecl();
Summ = Summaries.getSummary(FD, Eng.getContext());
}
Ted Kremenek
committed
// Get the state.
ValueState* St = Builder.GetState(Pred);
// Evaluate the effects of the call.
ValueState StVals = *St;
RefVal::Kind hasErr = (RefVal::Kind) 0;
// This function has a summary. Evaluate the effect of the arguments.
unsigned idx = 0;
Expr* ErrorExpr = NULL;
SymbolID ErrorSym = 0;
for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end();
I != E; ++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.
// FIXME: This is basically copy-and-paste from GRSimpleVals. We
// should compose behavior, not copy it.
StateMgr.Unbind(StVals, cast<LVal>(V));
else if (isa<nonlval::LValAsInteger>(V))
StateMgr.Unbind(StVals, cast<nonlval::LValAsInteger>(V).getLVal());
St = StateMgr.getPersistentState(StVals);
if (hasErr) {
ProcessNonLeakError(Dst, Builder, CE, ErrorExpr, Pred, St,
hasErr, ErrorSym);
// Finally, consult the summary for the return value.
RetEffect RE = GetRetE(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 (CE->getType() != Eng.getContext().VoidTy) {
unsigned Count = Builder.getCurrentBlockCount();
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count);
RVal X = CE->getType()->isPointerType()
? cast<RVal>(lval::SymbolVal(Sym))
: cast<RVal>(nonlval::SymbolVal(Sym));
St = StateMgr.SetRVal(St, CE, X, Eng.getCFG().isBlkExpr(CE), false);
}
Ted Kremenek
committed
break;
case RetEffect::Alias: {
unsigned idx = RE.getValue();
assert (idx < CE->getNumArgs());
RVal V = StateMgr.GetRVal(St, CE->getArg(idx));
Ted Kremenek
committed
St = StateMgr.SetRVal(St, CE, V, Eng.getCFG().isBlkExpr(CE), false);
break;
}
case RetEffect::OwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
Ted Kremenek
committed
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count);
ValueState StImpl = *St;
RefBindings B = GetRefBindings(StImpl);
SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeOwned()));
St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl),
CE, lval::SymbolVal(Sym),
Ted Kremenek
committed
Eng.getCFG().isBlkExpr(CE), false);
break;
}
case RetEffect::NotOwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
Ted Kremenek
committed
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count);
ValueState StImpl = *St;
RefBindings B = GetRefBindings(StImpl);
SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeNotOwned()));
St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl),
CE, lval::SymbolVal(Sym),
Ted Kremenek
committed
Eng.getCFG().isBlkExpr(CE), false);
break;
}
}
Ted Kremenek
committed
}
Ted Kremenek
committed
void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
ObjCMessageExpr* ME,
ExplodedNode<ValueState>* Pred) {
if (EvalObjCMessageExprAux(Dst, Eng, Builder, ME, Pred))
GRSimpleVals::EvalObjCMessageExpr(Dst, Eng, Builder, ME, Pred);
}
bool CFRefCount::EvalObjCMessageExprAux(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
ObjCMessageExpr* ME,
ExplodedNode<ValueState>* Pred) {
// Handle "toll-free bridging" of calls to "Release" and "Retain".
// FIXME: track the underlying object type associated so that we can
// flag illegal uses of toll-free bridging (or at least handle it
// at casts).
Ted Kremenek
committed
Selector S = ME->getSelector();
if (!S.isUnarySelector())
return true;
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
Expr* Receiver = ME->getReceiver();
if (!Receiver)
return true;
// Check if we are calling "Retain" or "Release".
bool isRetain = false;
if (S == RetainSelector)
isRetain = true;
else if (S != ReleaseSelector)
return true;
// We have "Retain" or "Release". Get the reference binding.
ValueStateManager& StateMgr = Eng.getStateManager();
ValueState* St = Builder.GetState(Pred);
RVal V = StateMgr.GetRVal(St, Receiver);
if (!isa<lval::SymbolVal>(V))
return true;
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
RefBindings B = GetRefBindings(*St);
RefBindings::TreeTy* T = B.SlimFind(Sym);
if (!T)
return true;
RefVal::Kind hasErr = (RefVal::Kind) 0;
B = Update(B, Sym, T->getValue().second, isRetain ? IncRef : DecRef, hasErr);
// Create a new state with the updated bindings.
ValueState StVals = *St;
SetRefBindings(StVals, B);
St = StateMgr.getPersistentState(StVals);
// Create an error node if it exists.
if (hasErr)
ProcessNonLeakError(Dst, Builder, ME, Receiver, Pred, St, hasErr, Sym);
else
Builder.MakeNode(Dst, ME, Pred, St);
return false;
Ted Kremenek
committed
}
Ted Kremenek
committed
// 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;