#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/ParentMap.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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 "llvm/Support/raw_ostream.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 > {
mutable std::unique_ptr<BugType> BT;
void checkForInvalidSelf(const Expr *E, CheckerContext &C,
const char *errorStr) const;
public:
ObjCSelfInitChecker() {}
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 override;
};
}
namespace {
enum SelfFlagEnum {
SelfFlag_None = 0x0,
SelfFlag_Self = 0x1,
SelfFlag_InitRes = 0x2
};
}
REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag, SymbolRef, SelfFlagEnum)
REGISTER_TRAIT_WITH_PROGRAMSTATE(CalledInit, bool)
REGISTER_TRAIT_WITH_PROGRAMSTATE(PreCallSelfFlags, SelfFlagEnum)
static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) {
if (SymbolRef sym = val.getAsSymbol())
if (const SelfFlagEnum *attachedFlags = state->get<SelfFlag>(sym))
return *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,
SelfFlagEnum(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.getSVal(E);
if (!hasSelfFlag(exprVal, SelfFlag_Self, C))
return false; if (hasSelfFlag(exprVal, SelfFlag_InitRes, C))
return false;
return true;
}
void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C,
const char *errorStr) const {
if (!E)
return;
if (!C.getState()->get<CalledInit>())
return;
if (!isInvalidSelf(E, C))
return;
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
if (!BT)
BT.reset(new BugType(this, "Missing \"self = [(super or self) init...]\"",
categories::CoreFoundationObjectiveC));
C.emitReport(std::make_unique<PathSensitiveBugReport>(*BT, errorStr, N));
}
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 = C.getSVal(Msg.getOriginExpr());
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)) {
SelfFlagEnum selfFlags =
getSelfFlags(state->getSVal(argV.castAs<Loc>()), C);
C.addTransition(state->set<PreCallSelfFlags>(selfFlags));
return;
} else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
SelfFlagEnum 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 = 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(argV.castAs<Loc>()), prevFlags, C);
return;
} else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
addSelfFlag(state, CE.getReturnValue(), prevFlags, C);
return;
}
}
C.addTransition(state);
}
void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad,
const Stmt *S,
CheckerContext &C) const {
if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
C.getCurrentAnalysisDeclContext()->getDecl())))
return;
ProgramStateRef state = C.getState();
if (isSelfVar(location, C))
addSelfFlag(state, state->getSVal(location.castAs<Loc>()), 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 {
SelfFlagTy FlagMap = State->get<SelfFlag>();
bool DidCallInit = State->get<CalledInit>();
SelfFlagEnum PreCallFlags = State->get<PreCallSelfFlags>();
if (FlagMap.isEmpty() && !DidCallInit && !PreCallFlags)
return;
Out << Sep << NL << *this << " :" << 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 (SelfFlagTy::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;
}
return ID != nullptr;
}
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 = location.castAs<loc::MemRegionVal>();
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>();
}
bool ento::shouldRegisterObjCSelfInitChecker(const CheckerManager &mgr) {
return true;
}