void NilArgChecker::warnIfNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned int Arg, FoundationClass Class, bool CanBeSubscript) const { // Check if the argument is nil. ProgramStateRef State = C.getState(); if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) return; // NOTE: We cannot throw non-fatal errors from warnIfNilExpr, // because it's called multiple times from some callers, so it'd cause // an unwanted state split if two or more non-fatal errors are thrown // within the same checker callback. For now we don't want to, but // it'll need to be fixed if we ever want to. if (ExplodedNode *N = C.generateErrorNode()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) { if (Class == FC_NSArray) { os << "Array element cannot be nil"; } else if (Class == FC_NSDictionary) { if (Arg == 0) { os << "Value stored into '"; os << GetReceiverInterfaceName(msg) << "' cannot be nil"; } else { assert(Arg == 1); os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil"; } } else llvm_unreachable("Missing foundation class for the subscript expr"); } else { if (Class == FC_NSDictionary) { if (Arg == 0) os << "Value argument "; else { assert(Arg == 1); os << "Key argument "; } os << "to '"; msg.getSelector().print(os); os << "' cannot be nil"; } else { os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"; msg.getSelector().print(os); os << "' cannot be nil"; } } generateBugReport(N, os.str(), msg.getArgSourceRange(Arg), msg.getArgExpr(Arg), C); } }
void NilArgChecker::WarnIfNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned int Arg, FoundationClass Class, bool CanBeSubscript) const { // Check if the argument is nil. ProgramStateRef State = C.getState(); if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) return; if (!BT) BT.reset(new APIMisuse("nil argument")); if (ExplodedNode *N = C.generateSink()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) { if (Class == FC_NSArray) { os << "Array element cannot be nil"; } else if (Class == FC_NSDictionary) { if (Arg == 0) { os << "Value stored into '"; os << GetReceiverInterfaceName(msg) << "' cannot be nil"; } else { assert(Arg == 1); os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil"; } } else llvm_unreachable("Missing foundation class for the subscript expr"); } else { if (Class == FC_NSDictionary) { if (Arg == 0) os << "Value argument "; else { assert(Arg == 1); os << "Key argument "; } os << "to '" << msg.getSelector().getAsString() << "' cannot be nil"; } else { os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" << msg.getSelector().getAsString() << "' cannot be nil"; } } BugReport *R = new BugReport(*BT, os.str(), N); R->addRange(msg.getArgSourceRange(Arg)); bugreporter::trackNullOrUndefValue(N, msg.getArgExpr(Arg), *R); C.emitReport(R); } }
void NilArgChecker::warnIfNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned int Arg, FoundationClass Class, bool CanBeSubscript) const { // Check if the argument is nil. ProgramStateRef State = C.getState(); if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) return; if (ExplodedNode *N = C.generateErrorNode()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) { if (Class == FC_NSArray) { os << "Array element cannot be nil"; } else if (Class == FC_NSDictionary) { if (Arg == 0) { os << "Value stored into '"; os << GetReceiverInterfaceName(msg) << "' cannot be nil"; } else { assert(Arg == 1); os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil"; } } else llvm_unreachable("Missing foundation class for the subscript expr"); } else { if (Class == FC_NSDictionary) { if (Arg == 0) os << "Value argument "; else { assert(Arg == 1); os << "Key argument "; } os << "to '"; msg.getSelector().print(os); os << "' cannot be nil"; } else { os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"; msg.getSelector().print(os); os << "' cannot be nil"; } } generateBugReport(N, os.str(), msg.getArgSourceRange(Arg), msg.getArgExpr(Arg), C); } }
/// Returns the released value if M is a call a setter that releases /// and nils out its underlying instance variable. SymbolRef ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, CheckerContext &C) const { SVal ReceiverVal = M.getReceiverSVal(); if (!ReceiverVal.isValid()) return nullptr; if (M.getNumArgs() == 0) return nullptr; if (!M.getArgExpr(0)->getType()->isObjCRetainableType()) return nullptr; // Is the first argument nil? SVal Arg = M.getArgSVal(0); ProgramStateRef notNilState, nilState; std::tie(notNilState, nilState) = M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>()); if (!(nilState && !notNilState)) return nullptr; const ObjCPropertyDecl *Prop = M.getAccessedProperty(); if (!Prop) return nullptr; ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl(); if (!PropIvarDecl) return nullptr; ProgramStateRef State = C.getState(); SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal); Optional<Loc> LValLoc = LVal.getAs<Loc>(); if (!LValLoc) return nullptr; SVal CurrentValInIvar = State->getSVal(LValLoc.getValue()); return CurrentValInIvar.getAsSymbol(); }
void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!BT) { BT.reset(new APIMisuse(this, "Arguments passed to variadic method aren't all " "Objective-C pointer types")); ASTContext &Ctx = C.getASTContext(); arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); dictionaryWithObjectsAndKeysS = GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); } if (!isVariadicMessage(msg)) return; // We are not interested in the selector arguments since they have // well-defined types, so the compiler will issue a warning for them. unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); // We're not interested in the last argument since it has to be nil or the // compiler would have issued a warning for it elsewhere. unsigned variadicArgsEnd = msg.getNumArgs() - 1; if (variadicArgsEnd <= variadicArgsBegin) return; // Verify that all arguments have Objective-C types. Optional<ExplodedNode*> errorNode; for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { QualType ArgTy = msg.getArgExpr(I)->getType(); if (ArgTy->isObjCObjectPointerType()) continue; // Block pointers are treaded as Objective-C pointers. if (ArgTy->isBlockPointerType()) continue; // Ignore pointer constants. if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) continue; // Ignore pointer types annotated with 'NSObject' attribute. if (C.getASTContext().isObjCNSObjectType(ArgTy)) continue; // Ignore CF references, which can be toll-free bridged. if (coreFoundation::isCFObjectRef(ArgTy)) continue; // Generate only one error node to use for all bug reports. if (!errorNode.hasValue()) errorNode = C.generateNonFatalErrorNode(); if (!errorNode.getValue()) continue; SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); StringRef TypeName = GetReceiverInterfaceName(msg); if (!TypeName.empty()) os << "Argument to '" << TypeName << "' method '"; else os << "Argument to method '"; msg.getSelector().print(os); os << "' should be an Objective-C pointer type, not '"; ArgTy.print(os, C.getLangOpts()); os << "'"; auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue()); R->addRange(msg.getArgSourceRange(I)); C.emitReport(std::move(R)); } }
//FIXME: Consider other methods than setObject like dictionaryWithObjectsAndKeys /// Process call to NSMutableArray:setObject:forKey: void iOSAppSecInsecureKeyChainStorageChecker::checkPreObjCMessage (const ObjCMethodCall &M, CheckerContext &C) const { MSEC_DEBUG_FUNC("redwud:","ENTER") ; do { const ObjCInterfaceDecl *pRxInterface = M.getReceiverInterface() ; if ( !pRxInterface ) { break ; } ASTContext &Ctx = C.getASTContext() ; Selector selCurr = M.getSelector() ; initIdentifierInfo( Ctx ) ; //TODO: Check this with property, this might not work on it //NSMutableDictionary if ( pRxInterface ->getIdentifier() != m_piiNSMutableDictionary ) { break ; } //setObject IdentifierInfo *piiSetObject = selCurr.getIdentifierInfoForSlot(0) ; if ( piiSetObject != m_piiSetObject ) { break ; } //forKey IdentifierInfo *piiForKey = selCurr.getIdentifierInfoForSlot(1) ; if ( piiForKey != m_piiForKey ) { break ; } // MSEC_DEBUG("redwud: ", "'" << selCurr.getAsString() << "' num args: " << selCurr.getNumArgs() ) ; if ( selCurr.getNumArgs() != 2 ) { // Unlikely to be of concerned break ; } ProgramStateRef pProgState = C.getState() ; const LocationContext *pLCtx = C.getLocationContext() ; //Get the value for "aKey" parameter (2nd) // Checking this first because checking the first parameter takes a bit longer const Expr *pKeyExpr = M.getArgExpr(1) ; SVal argValKey = pProgState ->getSVal( pKeyExpr, pLCtx ) ; if ( !CMSecCommon::isSValContains( argValKey, "kSecAttrAccessible" ) ) { // Not of concern break ; } //Get the value for "anObject" parameter (1st) const Expr *pObjExpr = M.getArgExpr(0) ; SVal argValAnObject = pProgState ->getSVal( pObjExpr, pLCtx ) ; //Get receiver as symbol, should be used in either condition SymbolRef pSymQuery = M.getReceiverSVal().getAsSymbol() ; if ( !pSymQuery ) { // redwud: Can't save empty receiver symbol, // so there is no point of moving on, // there must be something wrong with this break ; } //Idea: if [query] is currently being tracked change it to different status, e.g. secure // if not tracked add new secure state bool bInsecureObject = isInsecureObject( argValAnObject ) ; pProgState = pProgState ->set <StreamMap>( pSymQuery, bInsecureObject ? KeyChainState::getNotSecure() : KeyChainState::getSecure() ) ; // Add transition of state //redwud: it seems that the states are transitioned at some point C.addTransition( pProgState ) ; MSEC_DEBUG( "redwud: ", "Finish checking!" ) ; } while (_PASSING_) ; MSEC_DEBUG_FUNC("redwud:","EXIT") ; }