void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg, CheckerContext &C) const { // When encountering a message that does initialization (init rule), // tag the return value so that we know later on that if self has this value // then it is properly initialized. // FIXME: A callback should disable checkers at the start of functions. if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( C.getCurrentAnalysisDeclContext()->getDecl()))) return; if (isInitMessage(msg)) { // Tag the return value as the result of an initializer. const ProgramState *state = C.getState(); // FIXME this really should be context sensitive, where we record // the current stack frame (for IPA). Also, we need to clean this // value out when we return from this method. state = state->set<CalledInit>(true); SVal V = state->getSVal(msg.getOriginExpr(), C.getLocationContext()); addSelfFlag(state, V, SelfFlag_InitRes, C); return; } // We don't check for an invalid 'self' in an obj-c message expression to cut // down false positives where logging functions get information from self // (like its class) or doing "invalidation" on self when the initialization // fails. }
void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE, CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. 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)) { // If the address of 'self' is being passed to the call, assume that the // 'self' after the call will have the same flags. // EX: log(&self) addSelfFlag(state, state->getSVal(cast<Loc>(argV)), prevFlags, C); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { // If 'self' is passed to the call by value, assume that the function // returns 'self'. So assign the flags, which were set on 'self' to the // return value. // EX: self = performMoreInitialization(self) addSelfFlag(state, CE.getReturnValue(), prevFlags, C); return; } } C.addTransition(state); }
void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE, CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( C.getCurrentAnalysisDeclContext()->getDecl()))) return; ProgramStateRef state = C.getState(); unsigned NumArgs = CE.getNumArgs(); // If we passed 'self' as and argument to the call, record it in the state // to be propagated after the call. // Note, we could have just given up, but try to be more optimistic here and // assume that the functions are going to continue initialization or will not // modify self. 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 DanglingDelegateChecker::resetInitialStateIfNeeded(CheckerContext &context) const { // nothing to do if we did it already ProgramStateRef state = context.getState(); if (state->get<InitStateWasSet>()) { return; } // record the InterfaceDecl if any const ObjCInterfaceDecl *topInterface = getCurrentTopClassInterface(context); if (topInterface) { state = state->set<CurrentObjCInterfaceDecl>((void *)topInterface); // see if we are in a 'pseudo-init' method const ObjCMethodDecl *decl = dyn_cast_or_null<ObjCMethodDecl>(context.getCurrentAnalysisDeclContext()->getDecl()); if (decl) { const ObjCImplFacts *facts = getCurrentFacts(topInterface); if (facts && facts->_pseudoInitMethods.find(decl->getNameAsString()) != facts->_pseudoInitMethods.end()) { // override the maps to assume that everything was cleared already const std::map<const ObjCIvarDecl *, IvarFacts> &ivarFactsMap = facts->_ivarFactsMap; for (auto it = ivarFactsMap.begin(), end = ivarFactsMap.end(); it != end; ++it) { state = state->set<IvarMap>(it->first, IvarDynamicState(it->second)); } } } } // in any case mark the state as initialized state = state->set<InitStateWasSet>(true); context.addTransition(state); }
void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. 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::checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. 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...]'"); }
/// \brief Returns true if the location is 'self'. 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.getRegion())) return (DR->getDecl() == analCtx->getSelfDecl()); return false; }
void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, const Stmt *S, CheckerContext &C) const { if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( C.getCurrentAnalysisDeclContext()->getDecl()))) return; // Tag the result of a load from 'self' so that we can easily know that the // value is the object that 'self' points to. ProgramStateRef state = C.getState(); if (isSelfVar(location, C)) addSelfFlag(state, state->getSVal(location.castAs<Loc>()), SelfFlag_Self, C); }
void IdempotentOperationChecker::checkPreStmt(const BinaryOperator *B, CheckerContext &C) const { // Find or create an entry in the hash for this BinaryOperator instance. // If we haven't done a lookup before, it will get default initialized to // 'Possible'. At this stage we do not store the ExplodedNode, as it has not // been created yet. BinaryOperatorData &Data = hash[B]; Assumption &A = Data.assumption; AnalysisDeclContext *AC = C.getCurrentAnalysisDeclContext(); // If we already have visited this node on a path that does not contain an // idempotent operation, return immediately. if (A == Impossible) return; // Retrieve both sides of the operator and determine if they can vary (which // may mean this is a false positive. const Expr *LHS = B->getLHS(); const Expr *RHS = B->getRHS(); // At this stage we can calculate whether each side contains a false positive // that applies to all operators. We only need to calculate this the first // time. bool LHSContainsFalsePositive = false, RHSContainsFalsePositive = false; if (A == Possible) { // An expression contains a false positive if it can't vary, or if it // contains a known false positive VarDecl. LHSContainsFalsePositive = !CanVary(LHS, AC) || containsNonLocalVarDecl(LHS); RHSContainsFalsePositive = !CanVary(RHS, AC) || containsNonLocalVarDecl(RHS); } ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); SVal LHSVal = state->getSVal(LHS, LCtx); SVal RHSVal = state->getSVal(RHS, LCtx); // If either value is unknown, we can't be 100% sure of all paths. if (LHSVal.isUnknownOrUndef() || RHSVal.isUnknownOrUndef()) { A = Impossible; return; } BinaryOperator::Opcode Op = B->getOpcode(); // Dereference the LHS SVal if this is an assign operation switch (Op) { default: break; // Fall through intentional case BO_AddAssign: case BO_SubAssign: case BO_MulAssign: case BO_DivAssign: case BO_AndAssign: case BO_OrAssign: case BO_XorAssign: case BO_ShlAssign: case BO_ShrAssign: case BO_Assign: // Assign statements have one extra level of indirection if (!isa<Loc>(LHSVal)) { A = Impossible; return; } LHSVal = state->getSVal(cast<Loc>(LHSVal), LHS->getType()); } // We now check for various cases which result in an idempotent operation. // x op x switch (Op) { default: break; // We don't care about any other operators. // Fall through intentional case BO_Assign: // x Assign x can be used to silence unused variable warnings intentionally. // If this is a self assignment and the variable is referenced elsewhere, // and the assignment is not a truncation or extension, then it is a false // positive. if (isSelfAssign(LHS, RHS)) { if (!isUnused(LHS, AC) && !isTruncationExtensionAssignment(LHS, RHS)) { UpdateAssumption(A, Equal); return; } else { A = Impossible; return; } } case BO_SubAssign: case BO_DivAssign: case BO_AndAssign: case BO_OrAssign: case BO_XorAssign: case BO_Sub: case BO_Div: case BO_And: case BO_Or: case BO_Xor: case BO_LOr: case BO_LAnd: case BO_EQ: case BO_NE: if (LHSVal != RHSVal || LHSContainsFalsePositive || RHSContainsFalsePositive) break; UpdateAssumption(A, Equal); return; } // x op 1 switch (Op) { default: break; // We don't care about any other operators. // Fall through intentional case BO_MulAssign: case BO_DivAssign: case BO_Mul: case BO_Div: case BO_LOr: case BO_LAnd: if (!RHSVal.isConstant(1) || RHSContainsFalsePositive) break; UpdateAssumption(A, RHSis1); return; } // 1 op x switch (Op) { default: break; // We don't care about any other operators. // Fall through intentional case BO_MulAssign: case BO_Mul: case BO_LOr: case BO_LAnd: if (!LHSVal.isConstant(1) || LHSContainsFalsePositive) break; UpdateAssumption(A, LHSis1); return; } // x op 0 switch (Op) { default: break; // We don't care about any other operators. // Fall through intentional case BO_AddAssign: case BO_SubAssign: case BO_MulAssign: case BO_AndAssign: case BO_OrAssign: case BO_XorAssign: case BO_Add: case BO_Sub: case BO_Mul: case BO_And: case BO_Or: case BO_Xor: case BO_Shl: case BO_Shr: case BO_LOr: case BO_LAnd: if (!RHSVal.isConstant(0) || RHSContainsFalsePositive) break; UpdateAssumption(A, RHSis0); return; } // 0 op x switch (Op) { default: break; // We don't care about any other operators. // Fall through intentional //case BO_AddAssign: // Common false positive case BO_SubAssign: // Check only if unsigned case BO_MulAssign: case BO_DivAssign: case BO_AndAssign: //case BO_OrAssign: // Common false positive //case BO_XorAssign: // Common false positive case BO_ShlAssign: case BO_ShrAssign: case BO_Add: case BO_Sub: case BO_Mul: case BO_Div: case BO_And: case BO_Or: case BO_Xor: case BO_Shl: case BO_Shr: case BO_LOr: case BO_LAnd: if (!LHSVal.isConstant(0) || LHSContainsFalsePositive) break; UpdateAssumption(A, LHSis0); return; } // If we get to this point, there has been a valid use of this operation. A = Impossible; }