ObjCSelfInitChecker.cpp [plain text]
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/AST/ParentMap.h"
using namespace clang;
using namespace ento;
static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND);
static bool isInitializationMethod(const ObjCMethodDecl *MD);
static bool isInitMessage(const ObjCMethodCall &Msg);
static bool isSelfVar(SVal location, CheckerContext &C);
namespace {
class ObjCSelfInitChecker : public Checker< check::PostObjCMessage,
check::PostStmt<ObjCIvarRefExpr>,
check::PreStmt<ReturnStmt>,
check::PreCall,
check::PostCall,
check::Location,
check::Bind > {
public:
void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const;
void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const;
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
void checkLocation(SVal location, bool isLoad, const Stmt *S,
CheckerContext &C) const;
void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
void checkPreCall(const CallEvent &CE, CheckerContext &C) const;
void checkPostCall(const CallEvent &CE, CheckerContext &C) const;
void printState(raw_ostream &Out, ProgramStateRef State,
const char *NL, const char *Sep) const;
};
}
namespace {
class InitSelfBug : public BugType {
const std::string desc;
public:
InitSelfBug() : BugType("Missing \"self = [(super or self) init...]\"",
categories::CoreFoundationObjectiveC) {}
};
}
namespace {
enum SelfFlagEnum {
SelfFlag_None = 0x0,
SelfFlag_Self = 0x1,
SelfFlag_InitRes = 0x2
};
}
typedef llvm::ImmutableMap<SymbolRef, unsigned> SelfFlag;
namespace { struct CalledInit {}; }
namespace { struct PreCallSelfFlags {}; }
namespace clang {
namespace ento {
template<>
struct ProgramStateTrait<SelfFlag> : public ProgramStatePartialTrait<SelfFlag> {
static void *GDMIndex() { static int index = 0; return &index; }
};
template <>
struct ProgramStateTrait<CalledInit> : public ProgramStatePartialTrait<bool> {
static void *GDMIndex() { static int index = 0; return &index; }
};
template <>
struct ProgramStateTrait<PreCallSelfFlags> : public ProgramStatePartialTrait<unsigned> {
static void *GDMIndex() { static int index = 0; return &index; }
};
}
}
static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) {
if (SymbolRef sym = val.getAsSymbol())
if (const unsigned *attachedFlags = state->get<SelfFlag>(sym))
return (SelfFlagEnum)*attachedFlags;
return SelfFlag_None;
}
static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) {
return getSelfFlags(val, C.getState());
}
static void addSelfFlag(ProgramStateRef state, SVal val,
SelfFlagEnum flag, CheckerContext &C) {
if (SymbolRef sym = val.getAsSymbol())
state = state->set<SelfFlag>(sym, getSelfFlags(val, state) | flag);
C.addTransition(state);
}
static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) {
return getSelfFlags(val, C) & flag;
}
static bool isInvalidSelf(const Expr *E, CheckerContext &C) {
SVal exprVal = C.getState()->getSVal(E, C.getLocationContext());
if (!hasSelfFlag(exprVal, SelfFlag_Self, C))
return false; if (hasSelfFlag(exprVal, SelfFlag_InitRes, C))
return false;
return true;
}
static void checkForInvalidSelf(const Expr *E, CheckerContext &C,
const char *errorStr) {
if (!E)
return;
if (!C.getState()->get<CalledInit>())
return;
if (!isInvalidSelf(E, C))
return;
ExplodedNode *N = C.generateSink();
if (!N)
return;
BugReport *report =
new BugReport(*new InitSelfBug(), errorStr, N);
C.EmitReport(report);
}
void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
CheckerContext &C) const {
if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
C.getCurrentAnalysisDeclContext()->getDecl())))
return;
if (isInitMessage(Msg)) {
ProgramStateRef state = C.getState();
state = state->set<CalledInit>(true);
SVal V = state->getSVal(Msg.getOriginExpr(), C.getLocationContext());
addSelfFlag(state, V, SelfFlag_InitRes, C);
return;
}
}
void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E,
CheckerContext &C) const {
if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
C.getCurrentAnalysisDeclContext()->getDecl())))
return;
checkForInvalidSelf(E->getBase(), C,
"Instance variable used while 'self' is not set to the result of "
"'[(super or self) init...]'");
}
void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S,
CheckerContext &C) const {
if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
C.getCurrentAnalysisDeclContext()->getDecl())))
return;
checkForInvalidSelf(S->getRetValue(), C,
"Returning 'self' while it is not set to the result of "
"'[(super or self) init...]'");
}
void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE,
CheckerContext &C) const {
if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
C.getCurrentAnalysisDeclContext()->getDecl())))
return;
ProgramStateRef state = C.getState();
unsigned NumArgs = CE.getNumArgs();
for (unsigned i = 0; i < NumArgs; ++i) {
SVal argV = CE.getArgSVal(i);
if (isSelfVar(argV, C)) {
unsigned selfFlags = getSelfFlags(state->getSVal(cast<Loc>(argV)), C);
C.addTransition(state->set<PreCallSelfFlags>(selfFlags));
return;
} else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
unsigned selfFlags = getSelfFlags(argV, C);
C.addTransition(state->set<PreCallSelfFlags>(selfFlags));
return;
}
}
}
void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE,
CheckerContext &C) const {
if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
C.getCurrentAnalysisDeclContext()->getDecl())))
return;
ProgramStateRef state = C.getState();
SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>();
if (!prevFlags)
return;
state = state->remove<PreCallSelfFlags>();
unsigned NumArgs = CE.getNumArgs();
for (unsigned i = 0; i < NumArgs; ++i) {
SVal argV = CE.getArgSVal(i);
if (isSelfVar(argV, C)) {
addSelfFlag(state, state->getSVal(cast<Loc>(argV)), prevFlags, C);
return;
} else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
const Expr *CallExpr = CE.getOriginExpr();
if (CallExpr)
addSelfFlag(state, state->getSVal(CallExpr, C.getLocationContext()),
prevFlags, C);
return;
}
}
}
void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad,
const Stmt *S,
CheckerContext &C) const {
ProgramStateRef state = C.getState();
if (isSelfVar(location, C))
addSelfFlag(state, state->getSVal(cast<Loc>(location)), SelfFlag_Self, C);
}
void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S,
CheckerContext &C) const {
if ((isSelfVar(loc, C)) &&
!hasSelfFlag(val, SelfFlag_InitRes, C) &&
!hasSelfFlag(val, SelfFlag_Self, C) &&
!isSelfVar(val, C)) {
ProgramStateRef State = C.getState();
State = State->remove<CalledInit>();
if (SymbolRef sym = loc.getAsSymbol())
State = State->remove<SelfFlag>(sym);
C.addTransition(State);
}
}
void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State,
const char *NL, const char *Sep) const {
SelfFlag FlagMap = State->get<SelfFlag>();
bool DidCallInit = State->get<CalledInit>();
SelfFlagEnum PreCallFlags = (SelfFlagEnum)State->get<PreCallSelfFlags>();
if (FlagMap.isEmpty() && !DidCallInit && !PreCallFlags)
return;
Out << Sep << NL << "ObjCSelfInitChecker:" << NL;
if (DidCallInit)
Out << " An init method has been called." << NL;
if (PreCallFlags != SelfFlag_None) {
if (PreCallFlags & SelfFlag_Self) {
Out << " An argument of the current call came from the 'self' variable."
<< NL;
}
if (PreCallFlags & SelfFlag_InitRes) {
Out << " An argument of the current call came from an init method."
<< NL;
}
}
Out << NL;
for (SelfFlag::iterator I = FlagMap.begin(), E = FlagMap.end(); I != E; ++I) {
Out << I->first << " : ";
if (I->second == SelfFlag_None)
Out << "none";
if (I->second & SelfFlag_Self)
Out << "self variable";
if (I->second & SelfFlag_InitRes) {
if (I->second != SelfFlag_InitRes)
Out << " | ";
Out << "result of init method";
}
Out << NL;
}
}
static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) {
if (!ND)
return false;
const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ND);
if (!MD)
return false;
if (!isInitializationMethod(MD))
return false;
ASTContext &Ctx = MD->getASTContext();
IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
ObjCInterfaceDecl *ID = MD->getClassInterface()->getSuperClass();
for ( ; ID ; ID = ID->getSuperClass()) {
IdentifierInfo *II = ID->getIdentifier();
if (II == NSObjectII)
break;
}
if (!ID)
return false;
return true;
}
static bool isSelfVar(SVal location, CheckerContext &C) {
AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext();
if (!analCtx->getSelfDecl())
return false;
if (!isa<loc::MemRegionVal>(location))
return false;
loc::MemRegionVal MRV = cast<loc::MemRegionVal>(location);
if (const DeclRegion *DR = dyn_cast<DeclRegion>(MRV.stripCasts()))
return (DR->getDecl() == analCtx->getSelfDecl());
return false;
}
static bool isInitializationMethod(const ObjCMethodDecl *MD) {
return MD->getMethodFamily() == OMF_init;
}
static bool isInitMessage(const ObjCMethodCall &Call) {
return Call.getMethodFamily() == OMF_init;
}
void ento::registerObjCSelfInitChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCSelfInitChecker>();
}