Files
clang-p2996/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
Artem Dergachev bbc6d68297 [analyzer] Fix the "Zombie Symbols" bug.
It's an old bug that consists in stale references to symbols remaining in the
GDM if they disappear from other program state sections as a result of any
operation that isn't the actual dead symbol collection. The most common example
here is:

   FILE *fp = fopen("myfile.txt", "w");
   fp = 0; // leak of file descriptor

In this example the leak were not detected previously because the symbol
disappears from the public part of the program state due to evaluating
the assignment. For that reason the checker never receives a notification
that the symbol is dead, and never reports a leak.

This patch not only causes leak false negatives, but also a number of other
problems, including false positives on some checkers.

What's worse, even though the program state contains a finite number of symbols,
the set of symbols that dies is potentially infinite. This means that is
impossible to compute the set of all dead symbols to pass off to the checkers
for cleaning up their part of the GDM.

No longer compute the dead set at all. Disallow iterating over dead symbols.
Disallow querying if any symbols are dead. Remove the API for marking symbols
as dead, as it is no longer necessary. Update checkers accordingly.

Differential Revision: https://reviews.llvm.org/D18860

llvm-svn: 347953
2018-11-30 03:27:50 +00:00

1516 lines
49 KiB
C++

//==-- RetainCountChecker.cpp - Checks for leaks and other issues -*- C++ -*--//
//
// 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 RetainCountChecker, which implements
// a reference count checker for Core Foundation and Cocoa on (Mac OS X).
//
//===----------------------------------------------------------------------===//
#include "RetainCountChecker.h"
using namespace clang;
using namespace ento;
using namespace retaincountchecker;
using llvm::StrInStrNoCase;
REGISTER_MAP_WITH_PROGRAMSTATE(RefBindings, SymbolRef, RefVal)
namespace clang {
namespace ento {
namespace retaincountchecker {
const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym) {
return State->get<RefBindings>(Sym);
}
ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym,
RefVal Val) {
assert(Sym != nullptr);
return State->set<RefBindings>(Sym, Val);
}
ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) {
return State->remove<RefBindings>(Sym);
}
class UseAfterRelease : public CFRefBug {
public:
UseAfterRelease(const CheckerBase *checker)
: CFRefBug(checker, "Use-after-release") {}
const char *getDescription() const override {
return "Reference-counted object is used after it is released";
}
};
class BadRelease : public CFRefBug {
public:
BadRelease(const CheckerBase *checker) : CFRefBug(checker, "Bad release") {}
const char *getDescription() const override {
return "Incorrect decrement of the reference count of an object that is "
"not owned at this point by the caller";
}
};
class DeallocNotOwned : public CFRefBug {
public:
DeallocNotOwned(const CheckerBase *checker)
: CFRefBug(checker, "-dealloc sent to non-exclusively owned object") {}
const char *getDescription() const override {
return "-dealloc sent to object that may be referenced elsewhere";
}
};
class OverAutorelease : public CFRefBug {
public:
OverAutorelease(const CheckerBase *checker)
: CFRefBug(checker, "Object autoreleased too many times") {}
const char *getDescription() const override {
return "Object autoreleased too many times";
}
};
class ReturnedNotOwnedForOwned : public CFRefBug {
public:
ReturnedNotOwnedForOwned(const CheckerBase *checker)
: CFRefBug(checker, "Method should return an owned object") {}
const char *getDescription() const override {
return "Object with a +0 retain count returned to caller where a +1 "
"(owning) retain count is expected";
}
};
class Leak : public CFRefBug {
public:
Leak(const CheckerBase *checker, StringRef name) : CFRefBug(checker, name) {
// Leaks should not be reported if they are post-dominated by a sink.
setSuppressOnSink(true);
}
const char *getDescription() const override { return ""; }
bool isLeak() const override { return true; }
};
} // end namespace retaincountchecker
} // end namespace ento
} // end namespace clang
void RefVal::print(raw_ostream &Out) const {
if (!T.isNull())
Out << "Tracked " << T.getAsString() << " | ";
switch (getKind()) {
default: llvm_unreachable("Invalid RefVal kind");
case Owned: {
Out << "Owned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
break;
}
case NotOwned: {
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;
case ErrorDeallocNotOwned:
Out << "-dealloc (not-owned)";
break;
case ErrorLeak:
Out << "Leaked";
break;
case ErrorLeakReturned:
Out << "Leaked (Bad naming)";
break;
case ErrorUseAfterRelease:
Out << "Use-After-Release [ERROR]";
break;
case ErrorReleaseNotOwned:
Out << "Release of Not-Owned [ERROR]";
break;
case RefVal::ErrorOverAutorelease:
Out << "Over-autoreleased";
break;
case RefVal::ErrorReturnedNotOwned:
Out << "Non-owned object returned instead of owned";
break;
}
switch (getIvarAccessHistory()) {
case IvarAccessHistory::None:
break;
case IvarAccessHistory::AccessedDirectly:
Out << " [direct ivar access]";
break;
case IvarAccessHistory::ReleasedAfterDirectAccess:
Out << " [released after direct ivar access]";
}
if (ACnt) {
Out << " [autorelease -" << ACnt << ']';
}
}
namespace {
class StopTrackingCallback final : public SymbolVisitor {
ProgramStateRef state;
public:
StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {}
ProgramStateRef getState() const { return state; }
bool VisitSymbol(SymbolRef sym) override {
state = state->remove<RefBindings>(sym);
return true;
}
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Handle statements that may have an effect on refcounts.
//===----------------------------------------------------------------------===//
void RetainCountChecker::checkPostStmt(const BlockExpr *BE,
CheckerContext &C) const {
// Scan the BlockDecRefExprs for any object the retain count checker
// may be tracking.
if (!BE->getBlockDecl()->hasCaptures())
return;
ProgramStateRef state = C.getState();
auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
E = R->referenced_vars_end();
if (I == E)
return;
// FIXME: For now we invalidate the tracking of all symbols passed to blocks
// via captured variables, even though captured variables result in a copy
// and in implicit increment/decrement of a retain count.
SmallVector<const MemRegion*, 10> Regions;
const LocationContext *LC = C.getLocationContext();
MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
for ( ; I != E; ++I) {
const VarRegion *VR = I.getCapturedRegion();
if (VR->getSuperRegion() == R) {
VR = MemMgr.getVarRegion(VR->getDecl(), LC);
}
Regions.push_back(VR);
}
state = state->scanReachableSymbols<StopTrackingCallback>(Regions).getState();
C.addTransition(state);
}
void RetainCountChecker::checkPostStmt(const CastExpr *CE,
CheckerContext &C) const {
const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE);
if (!BE)
return;
ArgEffect AE = IncRef;
switch (BE->getBridgeKind()) {
case OBC_Bridge:
// Do nothing.
return;
case OBC_BridgeRetained:
AE = IncRef;
break;
case OBC_BridgeTransfer:
AE = DecRefBridgedTransferred;
break;
}
ProgramStateRef state = C.getState();
SymbolRef Sym = C.getSVal(CE).getAsLocSymbol();
if (!Sym)
return;
const RefVal* T = getRefBinding(state, Sym);
if (!T)
return;
RefVal::Kind hasErr = (RefVal::Kind) 0;
state = updateSymbol(state, Sym, *T, AE, hasErr, C);
if (hasErr) {
// FIXME: If we get an error during a bridge cast, should we report it?
return;
}
C.addTransition(state);
}
void RetainCountChecker::processObjCLiterals(CheckerContext &C,
const Expr *Ex) const {
ProgramStateRef state = C.getState();
const ExplodedNode *pred = C.getPredecessor();
for (const Stmt *Child : Ex->children()) {
SVal V = pred->getSVal(Child);
if (SymbolRef sym = V.getAsSymbol())
if (const RefVal* T = getRefBinding(state, sym)) {
RefVal::Kind hasErr = (RefVal::Kind) 0;
state = updateSymbol(state, sym, *T, MayEscape, hasErr, C);
if (hasErr) {
processNonLeakError(state, Child->getSourceRange(), hasErr, sym, C);
return;
}
}
}
// Return the object as autoreleased.
// RetEffect RE = RetEffect::MakeNotOwned(RetEffect::ObjC);
if (SymbolRef sym =
state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) {
QualType ResultTy = Ex->getType();
state = setRefBinding(state, sym,
RefVal::makeNotOwned(RetEffect::ObjC, ResultTy));
}
C.addTransition(state);
}
void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL,
CheckerContext &C) const {
// Apply the 'MayEscape' to all values.
processObjCLiterals(C, AL);
}
void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
CheckerContext &C) const {
// Apply the 'MayEscape' to all keys and values.
processObjCLiterals(C, DL);
}
void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
CheckerContext &C) const {
const ExplodedNode *Pred = C.getPredecessor();
ProgramStateRef State = Pred->getState();
if (SymbolRef Sym = Pred->getSVal(Ex).getAsSymbol()) {
QualType ResultTy = Ex->getType();
State = setRefBinding(State, Sym,
RefVal::makeNotOwned(RetEffect::ObjC, ResultTy));
}
C.addTransition(State);
}
void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
CheckerContext &C) const {
Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>();
if (!IVarLoc)
return;
ProgramStateRef State = C.getState();
SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol();
if (!Sym || !dyn_cast_or_null<ObjCIvarRegion>(Sym->getOriginRegion()))
return;
// Accessing an ivar directly is unusual. If we've done that, be more
// forgiving about what the surrounding code is allowed to do.
QualType Ty = Sym->getType();
RetEffect::ObjKind Kind;
if (Ty->isObjCRetainableType())
Kind = RetEffect::ObjC;
else if (coreFoundation::isCFObjectRef(Ty))
Kind = RetEffect::CF;
else
return;
// If the value is already known to be nil, don't bother tracking it.
ConstraintManager &CMgr = State->getConstraintManager();
if (CMgr.isNull(State, Sym).isConstrainedTrue())
return;
if (const RefVal *RV = getRefBinding(State, Sym)) {
// If we've seen this symbol before, or we're only seeing it now because
// of something the analyzer has synthesized, don't do anything.
if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None ||
isSynthesizedAccessor(C.getStackFrame())) {
return;
}
// Note that this value has been loaded from an ivar.
C.addTransition(setRefBinding(State, Sym, RV->withIvarAccess()));
return;
}
RefVal PlusZero = RefVal::makeNotOwned(Kind, Ty);
// In a synthesized accessor, the effective retain count is +0.
if (isSynthesizedAccessor(C.getStackFrame())) {
C.addTransition(setRefBinding(State, Sym, PlusZero));
return;
}
State = setRefBinding(State, Sym, PlusZero.withIvarAccess());
C.addTransition(State);
}
void RetainCountChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
RetainSummaryManager &Summaries = getSummaryManager(C);
// Leave null if no receiver.
QualType ReceiverType;
if (const auto *MC = dyn_cast<ObjCMethodCall>(&Call)) {
if (MC->isInstanceMessage()) {
SVal ReceiverV = MC->getReceiverSVal();
if (SymbolRef Sym = ReceiverV.getAsLocSymbol())
if (const RefVal *T = getRefBinding(C.getState(), Sym))
ReceiverType = T->getType();
}
}
const RetainSummary *Summ = Summaries.getSummary(Call, ReceiverType);
if (C.wasInlined) {
processSummaryOfInlined(*Summ, Call, C);
return;
}
checkSummary(*Summ, Call, C);
}
void RetainCountChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
ExprEngine &Eng) const {
// FIXME: This is a hack to make sure the summary log gets cleared between
// analyses of different code bodies.
//
// Why is this necessary? Because a checker's lifetime is tied to a
// translation unit, but an ExplodedGraph's lifetime is just a code body.
// Once in a blue moon, a new ExplodedNode will have the same address as an
// old one with an associated summary, and the bug report visitor gets very
// confused. (To make things worse, the summary lifetime is currently also
// tied to a code body, so we get a crash instead of incorrect results.)
//
// Why is this a bad solution? Because if the lifetime of the ExplodedGraph
// changes, things will start going wrong again. Really the lifetime of this
// log needs to be tied to either the specific nodes in it or the entire
// ExplodedGraph, not to a specific part of the code being analyzed.
//
// (Also, having stateful local data means that the same checker can't be
// used from multiple threads, but a lot of checkers have incorrect
// assumptions about that anyway. So that wasn't a priority at the time of
// this fix.)
//
// This happens at the end of analysis, but bug reports are emitted /after/
// this point. So we can't just clear the summary log now. Instead, we mark
// that the next time we access the summary log, it should be cleared.
// If we never reset the summary log during /this/ code body analysis,
// there were no new summaries. There might still have been summaries from
// the /last/ analysis, so clear them out to make sure the bug report
// visitors don't get confused.
if (ShouldResetSummaryLog)
SummaryLog.clear();
ShouldResetSummaryLog = !SummaryLog.empty();
}
CFRefBug *
RetainCountChecker::getLeakWithinFunctionBug(const LangOptions &LOpts) const {
if (!leakWithinFunction)
leakWithinFunction.reset(new Leak(this, "Leak"));
return leakWithinFunction.get();
}
CFRefBug *
RetainCountChecker::getLeakAtReturnBug(const LangOptions &LOpts) const {
if (!leakAtReturn)
leakAtReturn.reset(new Leak(this, "Leak of returned object"));
return leakAtReturn.get();
}
/// 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 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).
// FIXME: We may be able to do this with related result types instead.
// This function is probably overestimating.
static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
QualType RetTy = RetE->getType();
// If RetE is not a message expression just return its type.
// If RetE is a message expression, return its types if it is something
/// more specific than id.
if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(RetE))
if (const ObjCObjectPointerType *PT = RetTy->getAs<ObjCObjectPointerType>())
if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() ||
PT->isObjCClassType()) {
// At this point we know the return type of the message expression is
// id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this
// is a call to a class method whose type we can resolve. In such
// cases, promote the return type to XXX* (where XXX is the class).
const ObjCInterfaceDecl *D = ME->getReceiverInterface();
return !D ? RetTy :
Ctx.getObjCObjectPointerType(Ctx.getObjCInterfaceType(D));
}
return RetTy;
}
static Optional<RefVal> refValFromRetEffect(RetEffect RE,
QualType ResultTy) {
if (RE.isOwned()) {
return RefVal::makeOwned(RE.getObjKind(), ResultTy);
} else if (RE.notOwned()) {
return RefVal::makeNotOwned(RE.getObjKind(), ResultTy);
}
return None;
}
// We don't always get the exact modeling of the function with regards to the
// retain count checker even when the function is inlined. For example, we need
// to stop tracking the symbols which were marked with StopTrackingHard.
void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ,
const CallEvent &CallOrMsg,
CheckerContext &C) const {
ProgramStateRef state = C.getState();
// Evaluate the effect of the arguments.
for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) {
if (Summ.getArg(idx) == StopTrackingHard) {
SVal V = CallOrMsg.getArgSVal(idx);
if (SymbolRef Sym = V.getAsLocSymbol()) {
state = removeRefBinding(state, Sym);
}
}
}
// Evaluate the effect on the message receiver.
if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg)) {
if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {
if (Summ.getReceiverEffect() == StopTrackingHard) {
state = removeRefBinding(state, Sym);
}
}
}
// Consult the summary for the return value.
RetEffect RE = Summ.getRetEffect();
if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) {
if (RE.getKind() == RetEffect::NoRetHard)
state = removeRefBinding(state, Sym);
}
C.addTransition(state);
}
static ProgramStateRef updateOutParameter(ProgramStateRef State,
SVal ArgVal,
ArgEffect Effect) {
auto *ArgRegion = dyn_cast_or_null<TypedValueRegion>(ArgVal.getAsRegion());
if (!ArgRegion)
return State;
QualType PointeeTy = ArgRegion->getValueType();
if (!coreFoundation::isCFObjectRef(PointeeTy))
return State;
SVal PointeeVal = State->getSVal(ArgRegion);
SymbolRef Pointee = PointeeVal.getAsLocSymbol();
if (!Pointee)
return State;
switch (Effect) {
case UnretainedOutParameter:
State = setRefBinding(State, Pointee,
RefVal::makeNotOwned(RetEffect::CF, PointeeTy));
break;
case RetainedOutParameter:
// Do nothing. Retained out parameters will either point to a +1 reference
// or NULL, but the way you check for failure differs depending on the API.
// Consequently, we don't have a good way to track them yet.
break;
default:
llvm_unreachable("only for out parameters");
}
return State;
}
void RetainCountChecker::checkSummary(const RetainSummary &Summ,
const CallEvent &CallOrMsg,
CheckerContext &C) const {
ProgramStateRef state = C.getState();
// Evaluate the effect of the arguments.
RefVal::Kind hasErr = (RefVal::Kind) 0;
SourceRange ErrorRange;
SymbolRef ErrorSym = nullptr;
for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) {
SVal V = CallOrMsg.getArgSVal(idx);
ArgEffect Effect = Summ.getArg(idx);
if (Effect == RetainedOutParameter || Effect == UnretainedOutParameter) {
state = updateOutParameter(state, V, Effect);
} else if (SymbolRef Sym = V.getAsLocSymbol()) {
if (const RefVal *T = getRefBinding(state, Sym)) {
state = updateSymbol(state, Sym, *T, Effect, hasErr, C);
if (hasErr) {
ErrorRange = CallOrMsg.getArgSourceRange(idx);
ErrorSym = Sym;
break;
}
}
}
}
// Evaluate the effect on the message receiver / `this` argument.
bool ReceiverIsTracked = false;
if (!hasErr) {
if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg)) {
if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {
if (const RefVal *T = getRefBinding(state, Sym)) {
ReceiverIsTracked = true;
state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(),
hasErr, C);
if (hasErr) {
ErrorRange = MsgInvocation->getOriginExpr()->getReceiverRange();
ErrorSym = Sym;
}
}
}
} else if (const auto *MCall = dyn_cast<CXXMemberCall>(&CallOrMsg)) {
if (SymbolRef Sym = MCall->getCXXThisVal().getAsLocSymbol()) {
if (const RefVal *T = getRefBinding(state, Sym)) {
state = updateSymbol(state, Sym, *T, Summ.getThisEffect(),
hasErr, C);
if (hasErr) {
ErrorRange = MCall->getOriginExpr()->getSourceRange();
ErrorSym = Sym;
}
}
}
}
}
// Process any errors.
if (hasErr) {
processNonLeakError(state, ErrorRange, hasErr, ErrorSym, C);
return;
}
// Consult the summary for the return value.
RetEffect RE = Summ.getRetEffect();
if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) {
if (ReceiverIsTracked)
RE = getSummaryManager(C).getObjAllocRetEffect();
else
RE = RetEffect::MakeNoRet();
}
if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) {
QualType ResultTy = CallOrMsg.getResultType();
if (RE.notOwned()) {
const Expr *Ex = CallOrMsg.getOriginExpr();
assert(Ex);
ResultTy = GetReturnType(Ex, C.getASTContext());
}
if (Optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy))
state = setRefBinding(state, Sym, *updatedRefVal);
}
// This check is actually necessary; otherwise the statement builder thinks
// we've hit a previously-found path.
// Normally addTransition takes care of this, but we want the node pointer.
ExplodedNode *NewNode;
if (state == C.getState()) {
NewNode = C.getPredecessor();
} else {
NewNode = C.addTransition(state);
}
// Annotate the node with summary we used.
if (NewNode) {
// FIXME: This is ugly. See checkEndAnalysis for why it's necessary.
if (ShouldResetSummaryLog) {
SummaryLog.clear();
ShouldResetSummaryLog = false;
}
SummaryLog[NewNode] = &Summ;
}
}
ProgramStateRef
RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
RefVal V, ArgEffect E, RefVal::Kind &hasErr,
CheckerContext &C) const {
bool IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount;
switch (E) {
default:
break;
case IncRefMsg:
E = IgnoreRetainMsg ? DoNothing : IncRef;
break;
case DecRefMsg:
E = IgnoreRetainMsg ? DoNothing: DecRef;
break;
case DecRefMsgAndStopTrackingHard:
E = IgnoreRetainMsg ? StopTracking : DecRefAndStopTrackingHard;
break;
case MakeCollectable:
E = DoNothing;
}
// Handle all use-after-releases.
if (V.getKind() == RefVal::Released) {
V = V ^ RefVal::ErrorUseAfterRelease;
hasErr = V.getKind();
return setRefBinding(state, sym, V);
}
switch (E) {
case DecRefMsg:
case IncRefMsg:
case MakeCollectable:
case DecRefMsgAndStopTrackingHard:
llvm_unreachable("DecRefMsg/IncRefMsg/MakeCollectable already converted");
case UnretainedOutParameter:
case RetainedOutParameter:
llvm_unreachable("Applies to pointer-to-pointer parameters, which should "
"not have ref state.");
case Dealloc:
switch (V.getKind()) {
default:
llvm_unreachable("Invalid RefVal state for an explicit dealloc.");
case RefVal::Owned:
// The object immediately transitions to the released state.
V = V ^ RefVal::Released;
V.clearCounts();
return setRefBinding(state, sym, V);
case RefVal::NotOwned:
V = V ^ RefVal::ErrorDeallocNotOwned;
hasErr = V.getKind();
break;
}
break;
case MayEscape:
if (V.getKind() == RefVal::Owned) {
V = V ^ RefVal::NotOwned;
break;
}
LLVM_FALLTHROUGH;
case DoNothing:
return state;
case Autorelease:
// Update the autorelease counts.
V = V.autorelease();
break;
case StopTracking:
case StopTrackingHard:
return removeRefBinding(state, sym);
case IncRef:
switch (V.getKind()) {
default:
llvm_unreachable("Invalid RefVal state for a retain.");
case RefVal::Owned:
case RefVal::NotOwned:
V = V + 1;
break;
}
break;
case DecRef:
case DecRefBridgedTransferred:
case DecRefAndStopTrackingHard:
switch (V.getKind()) {
default:
// case 'RefVal::Released' handled above.
llvm_unreachable("Invalid RefVal state for a release.");
case RefVal::Owned:
assert(V.getCount() > 0);
if (V.getCount() == 1) {
if (E == DecRefBridgedTransferred ||
V.getIvarAccessHistory() ==
RefVal::IvarAccessHistory::AccessedDirectly)
V = V ^ RefVal::NotOwned;
else
V = V ^ RefVal::Released;
} else if (E == DecRefAndStopTrackingHard) {
return removeRefBinding(state, sym);
}
V = V - 1;
break;
case RefVal::NotOwned:
if (V.getCount() > 0) {
if (E == DecRefAndStopTrackingHard)
return removeRefBinding(state, sym);
V = V - 1;
} else if (V.getIvarAccessHistory() ==
RefVal::IvarAccessHistory::AccessedDirectly) {
// Assume that the instance variable was holding on the object at
// +1, and we just didn't know.
if (E == DecRefAndStopTrackingHard)
return removeRefBinding(state, sym);
V = V.releaseViaIvar() ^ RefVal::Released;
} else {
V = V ^ RefVal::ErrorReleaseNotOwned;
hasErr = V.getKind();
}
break;
}
break;
}
return setRefBinding(state, sym, V);
}
void RetainCountChecker::processNonLeakError(ProgramStateRef St,
SourceRange ErrorRange,
RefVal::Kind ErrorKind,
SymbolRef Sym,
CheckerContext &C) const {
// HACK: Ignore retain-count issues on values accessed through ivars,
// because of cases like this:
// [_contentView retain];
// [_contentView removeFromSuperview];
// [self addSubview:_contentView]; // invalidates 'self'
// [_contentView release];
if (const RefVal *RV = getRefBinding(St, Sym))
if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
return;
ExplodedNode *N = C.generateErrorNode(St);
if (!N)
return;
CFRefBug *BT;
switch (ErrorKind) {
default:
llvm_unreachable("Unhandled error.");
case RefVal::ErrorUseAfterRelease:
if (!useAfterRelease)
useAfterRelease.reset(new UseAfterRelease(this));
BT = useAfterRelease.get();
break;
case RefVal::ErrorReleaseNotOwned:
if (!releaseNotOwned)
releaseNotOwned.reset(new BadRelease(this));
BT = releaseNotOwned.get();
break;
case RefVal::ErrorDeallocNotOwned:
if (!deallocNotOwned)
deallocNotOwned.reset(new DeallocNotOwned(this));
BT = deallocNotOwned.get();
break;
}
assert(BT);
auto report = llvm::make_unique<CFRefReport>(
*BT, C.getASTContext().getLangOpts(), SummaryLog, N, Sym);
report->addRange(ErrorRange);
C.emitReport(std::move(report));
}
//===----------------------------------------------------------------------===//
// Handle the return values of retain-count-related functions.
//===----------------------------------------------------------------------===//
bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
// Get the callee. We're only interested in simple C functions.
ProgramStateRef state = C.getState();
const FunctionDecl *FD = C.getCalleeDecl(CE);
if (!FD)
return false;
RetainSummaryManager &SmrMgr = getSummaryManager(C);
QualType ResultTy = CE->getCallReturnType(C.getASTContext());
// See if the function has 'rc_ownership_trusted_implementation'
// annotate attribute. If it does, we will not inline it.
bool hasTrustedImplementationAnnotation = false;
const LocationContext *LCtx = C.getLocationContext();
using BehaviorSummary = RetainSummaryManager::BehaviorSummary;
Optional<BehaviorSummary> BSmr =
SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation);
// See if it's one of the specific functions we know how to eval.
if (!BSmr)
return false;
// Bind the return value.
if (BSmr == BehaviorSummary::Identity ||
BSmr == BehaviorSummary::IdentityOrZero) {
SVal RetVal = state->getSVal(CE->getArg(0), LCtx);
// If the receiver is unknown or the function has
// 'rc_ownership_trusted_implementation' annotate attribute, conjure a
// return value.
if (RetVal.isUnknown() ||
(hasTrustedImplementationAnnotation && !ResultTy.isNull())) {
SValBuilder &SVB = C.getSValBuilder();
RetVal =
SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount());
}
state = state->BindExpr(CE, LCtx, RetVal, /*Invalidate=*/false);
if (BSmr == BehaviorSummary::IdentityOrZero) {
// Add a branch where the output is zero.
ProgramStateRef NullOutputState = C.getState();
// Assume that output is zero on the other branch.
NullOutputState = NullOutputState->BindExpr(
CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false);
C.addTransition(NullOutputState);
// And on the original branch assume that both input and
// output are non-zero.
if (auto L = RetVal.getAs<DefinedOrUnknownSVal>())
state = state->assume(*L, /*Assumption=*/true);
}
}
C.addTransition(state);
return true;
}
ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S,
CheckerContext &C) const {
ExplodedNode *Pred = C.getPredecessor();
// Only adjust the reference count if this is the top-level call frame,
// and not the result of inlining. In the future, we should do
// better checking even for inlined calls, and see if they match
// with their expected semantics (e.g., the method should return a retained
// object, etc.).
if (!C.inTopFrame())
return Pred;
if (!S)
return Pred;
const Expr *RetE = S->getRetValue();
if (!RetE)
return Pred;
ProgramStateRef state = C.getState();
SymbolRef Sym =
state->getSValAsScalarOrLoc(RetE, C.getLocationContext()).getAsLocSymbol();
if (!Sym)
return Pred;
// Get the reference count binding (if any).
const RefVal *T = getRefBinding(state, Sym);
if (!T)
return Pred;
// Change the reference count.
RefVal X = *T;
switch (X.getKind()) {
case RefVal::Owned: {
unsigned cnt = X.getCount();
assert(cnt > 0);
X.setCount(cnt - 1);
X = X ^ RefVal::ReturnedOwned;
break;
}
case RefVal::NotOwned: {
unsigned cnt = X.getCount();
if (cnt) {
X.setCount(cnt - 1);
X = X ^ RefVal::ReturnedOwned;
} else {
X = X ^ RefVal::ReturnedNotOwned;
}
break;
}
default:
return Pred;
}
// Update the binding.
state = setRefBinding(state, Sym, X);
Pred = C.addTransition(state);
// At this point we have updated the state properly.
// Everything after this is merely checking to see if the return value has
// been over- or under-retained.
// Did we cache out?
if (!Pred)
return nullptr;
// Update the autorelease counts.
static CheckerProgramPointTag AutoreleaseTag(this, "Autorelease");
state = handleAutoreleaseCounts(state, Pred, &AutoreleaseTag, C, Sym, X, S);
// Have we generated a sink node?
if (!state)
return nullptr;
// Get the updated binding.
T = getRefBinding(state, Sym);
assert(T);
X = *T;
// Consult the summary of the enclosing method.
RetainSummaryManager &Summaries = getSummaryManager(C);
const Decl *CD = &Pred->getCodeDecl();
RetEffect RE = RetEffect::MakeNoRet();
// FIXME: What is the convention for blocks? Is there one?
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) {
const RetainSummary *Summ = Summaries.getMethodSummary(MD);
RE = Summ->getRetEffect();
} else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) {
if (!isa<CXXMethodDecl>(FD)) {
const RetainSummary *Summ = Summaries.getFunctionSummary(FD);
RE = Summ->getRetEffect();
}
}
return checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
}
ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
CheckerContext &C,
ExplodedNode *Pred,
RetEffect RE, RefVal X,
SymbolRef Sym,
ProgramStateRef state) const {
// HACK: Ignore retain-count issues on values accessed through ivars,
// because of cases like this:
// [_contentView retain];
// [_contentView removeFromSuperview];
// [self addSubview:_contentView]; // invalidates 'self'
// [_contentView release];
if (X.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
return Pred;
// Any leaks or other errors?
if (X.isReturnedOwned() && X.getCount() == 0) {
if (RE.getKind() != RetEffect::NoRet) {
if (!RE.isOwned()) {
// The returning type is a CF, we expect the enclosing method should
// return ownership.
X = X ^ RefVal::ErrorLeakReturned;
// Generate an error node.
state = setRefBinding(state, Sym, X);
static CheckerProgramPointTag ReturnOwnLeakTag(this, "ReturnsOwnLeak");
ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag);
if (N) {
const LangOptions &LOpts = C.getASTContext().getLangOpts();
auto R = llvm::make_unique<CFRefLeakReport>(
*getLeakAtReturnBug(LOpts), LOpts, SummaryLog, N, Sym, C,
IncludeAllocationLine);
C.emitReport(std::move(R));
}
return N;
}
}
} else if (X.isReturnedNotOwned()) {
if (RE.isOwned()) {
if (X.getIvarAccessHistory() ==
RefVal::IvarAccessHistory::AccessedDirectly) {
// Assume the method was trying to transfer a +1 reference from a
// strong ivar to the caller.
state = setRefBinding(state, Sym,
X.releaseViaIvar() ^ RefVal::ReturnedOwned);
} else {
// Trying to return a not owned object to a caller expecting an
// owned object.
state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned);
static CheckerProgramPointTag
ReturnNotOwnedTag(this, "ReturnNotOwnedForOwned");
ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag);
if (N) {
if (!returnNotOwnedForOwned)
returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this));
auto R = llvm::make_unique<CFRefReport>(
*returnNotOwnedForOwned, C.getASTContext().getLangOpts(),
SummaryLog, N, Sym);
C.emitReport(std::move(R));
}
return N;
}
}
}
return Pred;
}
//===----------------------------------------------------------------------===//
// Check various ways a symbol can be invalidated.
//===----------------------------------------------------------------------===//
void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S,
CheckerContext &C) const {
// Are we storing to something that causes the value to "escape"?
bool escapes = true;
// 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.
ProgramStateRef state = C.getState();
if (auto regionLoc = loc.getAs<loc::MemRegionVal>()) {
escapes = !regionLoc->getRegion()->hasStackStorage();
if (!escapes) {
// To test (3), generate a new state with the binding added. If it is
// the same state, then it escapes (since the store cannot represent
// the binding).
// Do this only if we know that the store is not supposed to generate the
// same state.
SVal StoredVal = state->getSVal(regionLoc->getRegion());
if (StoredVal != val)
escapes = (state == (state->bindLoc(*regionLoc, val, C.getLocationContext())));
}
if (!escapes) {
// Case 4: We do not currently model what happens when a symbol is
// assigned to a struct field, so be conservative here and let the symbol
// go. TODO: This could definitely be improved upon.
escapes = !isa<VarRegion>(regionLoc->getRegion());
}
}
// If we are storing the value into an auto function scope variable annotated
// with (__attribute__((cleanup))), stop tracking the value to avoid leak
// false positives.
if (const auto *LVR = dyn_cast_or_null<VarRegion>(loc.getAsRegion())) {
const VarDecl *VD = LVR->getDecl();
if (VD->hasAttr<CleanupAttr>()) {
escapes = true;
}
}
// If our store can represent the binding and we aren't storing to something
// that doesn't have local storage then just return and have the simulation
// state continue as is.
if (!escapes)
return;
// Otherwise, find all symbols referenced by 'val' that we are tracking
// and stop tracking them.
state = state->scanReachableSymbols<StopTrackingCallback>(val).getState();
C.addTransition(state);
}
ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state,
SVal Cond,
bool Assumption) const {
// 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.
RefBindingsTy B = state->get<RefBindings>();
if (B.isEmpty())
return state;
bool changed = false;
RefBindingsTy::Factory &RefBFactory = state->get_context<RefBindings>();
for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
// Check if the symbol is null stop tracking the symbol.
ConstraintManager &CMgr = state->getConstraintManager();
ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey());
if (AllocFailed.isConstrainedTrue()) {
changed = true;
B = RefBFactory.remove(B, I.getKey());
}
}
if (changed)
state = state->set<RefBindings>(B);
return state;
}
ProgramStateRef
RetainCountChecker::checkRegionChanges(ProgramStateRef state,
const InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
const LocationContext *LCtx,
const CallEvent *Call) const {
if (!invalidated)
return state;
llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols;
for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(),
E = ExplicitRegions.end(); I != E; ++I) {
if (const SymbolicRegion *SR = (*I)->StripCasts()->getAs<SymbolicRegion>())
WhitelistedSymbols.insert(SR->getSymbol());
}
for (SymbolRef sym :
llvm::make_range(invalidated->begin(), invalidated->end())) {
if (WhitelistedSymbols.count(sym))
continue;
// Remove any existing reference-count binding.
state = removeRefBinding(state, sym);
}
return state;
}
ProgramStateRef
RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
ExplodedNode *Pred,
const ProgramPointTag *Tag,
CheckerContext &Ctx,
SymbolRef Sym,
RefVal V,
const ReturnStmt *S) const {
unsigned ACnt = V.getAutoreleaseCount();
// No autorelease counts? Nothing to be done.
if (!ACnt)
return state;
unsigned Cnt = V.getCount();
// FIXME: Handle sending 'autorelease' to already released object.
if (V.getKind() == RefVal::ReturnedOwned)
++Cnt;
// If we would over-release here, but we know the value came from an ivar,
// assume it was a strong ivar that's just been relinquished.
if (ACnt > Cnt &&
V.getIvarAccessHistory() == RefVal::IvarAccessHistory::AccessedDirectly) {
V = V.releaseViaIvar();
--ACnt;
}
if (ACnt <= Cnt) {
if (ACnt == Cnt) {
V.clearCounts();
if (V.getKind() == RefVal::ReturnedOwned) {
V = V ^ RefVal::ReturnedNotOwned;
} else {
V = V ^ RefVal::NotOwned;
}
} else {
V.setCount(V.getCount() - ACnt);
V.setAutoreleaseCount(0);
}
return setRefBinding(state, Sym, V);
}
// HACK: Ignore retain-count issues on values accessed through ivars,
// because of cases like this:
// [_contentView retain];
// [_contentView removeFromSuperview];
// [self addSubview:_contentView]; // invalidates 'self'
// [_contentView release];
if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
return state;
// Woah! More autorelease counts then retain counts left.
// Emit hard error.
V = V ^ RefVal::ErrorOverAutorelease;
state = setRefBinding(state, Sym, V);
ExplodedNode *N = Ctx.generateSink(state, Pred, Tag);
if (N) {
SmallString<128> sbuf;
llvm::raw_svector_ostream os(sbuf);
os << "Object was autoreleased ";
if (V.getAutoreleaseCount() > 1)
os << V.getAutoreleaseCount() << " times but the object ";
else
os << "but ";
os << "has a +" << V.getCount() << " retain count";
if (!overAutorelease)
overAutorelease.reset(new OverAutorelease(this));
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
auto R = llvm::make_unique<CFRefReport>(*overAutorelease, LOpts, SummaryLog,
N, Sym, os.str());
Ctx.emitReport(std::move(R));
}
return nullptr;
}
ProgramStateRef
RetainCountChecker::handleSymbolDeath(ProgramStateRef state,
SymbolRef sid, RefVal V,
SmallVectorImpl<SymbolRef> &Leaked) const {
bool hasLeak;
// HACK: Ignore retain-count issues on values accessed through ivars,
// because of cases like this:
// [_contentView retain];
// [_contentView removeFromSuperview];
// [self addSubview:_contentView]; // invalidates 'self'
// [_contentView release];
if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
hasLeak = false;
else if (V.isOwned())
hasLeak = true;
else if (V.isNotOwned() || V.isReturnedOwned())
hasLeak = (V.getCount() > 0);
else
hasLeak = false;
if (!hasLeak)
return removeRefBinding(state, sid);
Leaked.push_back(sid);
return setRefBinding(state, sid, V ^ RefVal::ErrorLeak);
}
ExplodedNode *
RetainCountChecker::processLeaks(ProgramStateRef state,
SmallVectorImpl<SymbolRef> &Leaked,
CheckerContext &Ctx,
ExplodedNode *Pred) const {
// Generate an intermediate node representing the leak point.
ExplodedNode *N = Ctx.addTransition(state, Pred);
if (N) {
for (SmallVectorImpl<SymbolRef>::iterator
I = Leaked.begin(), E = Leaked.end(); I != E; ++I) {
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
CFRefBug *BT = Pred ? getLeakWithinFunctionBug(LOpts)
: getLeakAtReturnBug(LOpts);
assert(BT && "BugType not initialized.");
Ctx.emitReport(llvm::make_unique<CFRefLeakReport>(
*BT, LOpts, SummaryLog, N, *I, Ctx, IncludeAllocationLine));
}
}
return N;
}
static bool isISLObjectRef(QualType Ty) {
return StringRef(Ty.getAsString()).startswith("isl_");
}
void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const {
if (!Ctx.inTopFrame())
return;
RetainSummaryManager &SmrMgr = getSummaryManager(Ctx);
const LocationContext *LCtx = Ctx.getLocationContext();
const FunctionDecl *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
if (!FD || SmrMgr.isTrustedReferenceCountImplementation(FD))
return;
ProgramStateRef state = Ctx.getState();
const RetainSummary *FunctionSummary = SmrMgr.getFunctionSummary(FD);
ArgEffects CalleeSideArgEffects = FunctionSummary->getArgEffects();
for (unsigned idx = 0, e = FD->getNumParams(); idx != e; ++idx) {
const ParmVarDecl *Param = FD->getParamDecl(idx);
SymbolRef Sym = state->getSVal(state->getRegion(Param, LCtx)).getAsSymbol();
QualType Ty = Param->getType();
const ArgEffect *AE = CalleeSideArgEffects.lookup(idx);
if (AE && *AE == DecRef && isISLObjectRef(Ty)) {
state = setRefBinding(
state, Sym, RefVal::makeOwned(RetEffect::ObjKind::Generalized, Ty));
} else if (isISLObjectRef(Ty)) {
state = setRefBinding(
state, Sym,
RefVal::makeNotOwned(RetEffect::ObjKind::Generalized, Ty));
}
}
Ctx.addTransition(state);
}
void RetainCountChecker::checkEndFunction(const ReturnStmt *RS,
CheckerContext &Ctx) const {
ExplodedNode *Pred = processReturn(RS, Ctx);
// Created state cached out.
if (!Pred) {
return;
}
ProgramStateRef state = Pred->getState();
RefBindingsTy B = state->get<RefBindings>();
// Don't process anything within synthesized bodies.
const LocationContext *LCtx = Pred->getLocationContext();
if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized()) {
assert(!LCtx->inTopFrame());
return;
}
for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
state = handleAutoreleaseCounts(state, Pred, /*Tag=*/nullptr, Ctx,
I->first, I->second);
if (!state)
return;
}
// If the current LocationContext has a parent, don't check for leaks.
// We will do that later.
// FIXME: we should instead check for imbalances of the retain/releases,
// and suggest annotations.
if (LCtx->getParent())
return;
B = state->get<RefBindings>();
SmallVector<SymbolRef, 10> Leaked;
for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I)
state = handleSymbolDeath(state, I->first, I->second, Leaked);
processLeaks(state, Leaked, Ctx, Pred);
}
void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
CheckerContext &C) const {
ExplodedNode *Pred = C.getPredecessor();
ProgramStateRef state = C.getState();
RefBindingsTy B = state->get<RefBindings>();
SmallVector<SymbolRef, 10> Leaked;
// Update counts from autorelease pools
for (const auto &I: state->get<RefBindings>()) {
SymbolRef Sym = I.first;
if (SymReaper.isDead(Sym)) {
static CheckerProgramPointTag Tag(this, "DeadSymbolAutorelease");
const RefVal &V = I.second;
state = handleAutoreleaseCounts(state, Pred, &Tag, C, Sym, V);
if (!state)
return;
// Fetch the new reference count from the state, and use it to handle
// this symbol.
state = handleSymbolDeath(state, Sym, *getRefBinding(state, Sym), Leaked);
}
}
if (Leaked.empty()) {
C.addTransition(state);
return;
}
Pred = processLeaks(state, Leaked, C, Pred);
// Did we cache out?
if (!Pred)
return;
// Now generate a new node that nukes the old bindings.
// The only bindings left at this point are the leaked symbols.
RefBindingsTy::Factory &F = state->get_context<RefBindings>();
B = state->get<RefBindings>();
for (SmallVectorImpl<SymbolRef>::iterator I = Leaked.begin(),
E = Leaked.end();
I != E; ++I)
B = F.remove(B, *I);
state = state->set<RefBindings>(B);
C.addTransition(state, Pred);
}
void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
const char *NL, const char *Sep) const {
RefBindingsTy B = State->get<RefBindings>();
if (B.isEmpty())
return;
Out << Sep << NL;
for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
Out << I->first << " : ";
I->second.print(Out);
Out << NL;
}
}
//===----------------------------------------------------------------------===//
// Checker registration.
//===----------------------------------------------------------------------===//
void ento::registerRetainCountChecker(CheckerManager &Mgr) {
auto *Chk = Mgr.registerChecker<RetainCountChecker>();
AnalyzerOptions &Options = Mgr.getAnalyzerOptions();
Chk->IncludeAllocationLine = Options.getCheckerBooleanOption(
"leak-diagnostics-reference-allocation", false, Chk);
Chk->ShouldCheckOSObjectRetainCount = Options.getCheckerBooleanOption(
"CheckOSObject", true, Chk);
}