void iOSAppSecLeakingWebCachesChecker::checkASTDecl(const ObjCMethodDecl *pMD, AnalysisManager &rMgr, BugReporter &rBR) const { MSEC_DEBUG_FUNC( "redwud: ", "ENTER (Method)" ) ; do { if ( !pMD ) { break ; } Selector S = pMD ->getSelector() ; //There is no point of checking if you can't report it if ( !m_pInsecureInstBugType ) { MSEC_DEBUG("redwud: ","Reporting will fail!" ) ; break ; } // connection: if ( S.getIdentifierInfoForSlot( 0 ) != m_piiConnection) { //MSEC_DEBUG("redwud: ","!connection: " << S.getAsString() ) ; break ; } ASTContext &Ctx = rBR.getContext() ; // Two arguments if ( S.getNumArgs() != 2 ) { break ; } // willCacheResponse: if ( S.getIdentifierInfoForSlot(1) != m_piiWillCacheResponse ) { break ; } //Note: Intentionally made it common for both if ( !checkWillCacheResponse( pMD, Ctx ) ) { break ; } CMSecCommon::reportInsecureInstance( *m_pInsecureInstBugType , m_szReportDesc, rBR, pMD ) ; } while(_PASSING_) ; MSEC_DEBUG_FUNC( "redwud: ", "EXIT (Method)" ) ; }
ObjCInstanceTypeFamily Selector::getInstTypeMethodFamily(Selector sel) { IdentifierInfo *first = sel.getIdentifierInfoForSlot(0); if (!first) return OIT_None; StringRef name = first->getName(); if (name.empty()) return OIT_None; switch (name.front()) { case 'a': if (startsWithWord(name, "array")) return OIT_Array; break; case 'd': if (startsWithWord(name, "default")) return OIT_ReturnsSelf; if (startsWithWord(name, "dictionary")) return OIT_Dictionary; break; case 's': if (startsWithWord(name, "shared")) return OIT_ReturnsSelf; if (startsWithWord(name, "standard")) return OIT_Singleton; case 'i': if (startsWithWord(name, "init")) return OIT_Init; default: break; } return OIT_None; }
void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCInterfaceDecl *D) { for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end(); M != MEnd; ++M) { ObjCMethodDecl *Method = (*M); if (Method->isPropertyAccessor() || Method->param_size() != 0) continue; // Is this method candidate to be a getter? QualType GRT = Method->getResultType(); if (GRT->isVoidType()) continue; Selector GetterSelector = Method->getSelector(); IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0); Selector SetterSelector = SelectorTable::constructSetterSelector(PP.getIdentifierTable(), PP.getSelectorTable(), getterName); if (ObjCMethodDecl *SetterMethod = D->lookupMethod(SetterSelector, true)) { // Is this a valid setter, matching the target getter? QualType SRT = SetterMethod->getResultType(); if (!SRT->isVoidType()) continue; const ParmVarDecl *argDecl = *SetterMethod->param_begin(); QualType ArgType = argDecl->getType(); if (!Ctx.hasSameType(ArgType, GRT)) continue; edit::Commit commit(*Editor); edit::rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit); Editor->commit(commit); } } }
ObjCStringFormatFamily Selector::getStringFormatFamilyImpl(Selector sel) { IdentifierInfo *first = sel.getIdentifierInfoForSlot(0); if (!first) return SFF_None; StringRef name = first->getName(); switch (name.front()) { case 'a': if (name == "appendFormat") return SFF_NSString; break; case 'i': if (name == "initWithFormat") return SFF_NSString; break; case 'l': if (name == "localizedStringWithFormat") return SFF_NSString; break; case 's': if (name == "stringByAppendingFormat" || name == "stringWithFormat") return SFF_NSString; break; } return SFF_None; }
ObjCInstanceTypeFamily Selector::getInstTypeMethodFamily(Selector sel) { IdentifierInfo *first = sel.getIdentifierInfoForSlot(0); if (!first) return OIT_None; StringRef name = first->getName(); if (name.empty()) return OIT_None; switch (name.front()) { case 'a': if (startsWithWord(name, "alloc")) return OIT_MemManage; else if (startsWithWord(name, "array")) return OIT_Array; break; case 'd': if (startsWithWord(name, "dictionary")) return OIT_Dictionary; break; case 'i': if (startsWithWord(name, "init")) return OIT_MemManage; break; case 's': if (startsWithWord(name, "string")) return OIT_NSString; else if (startsWithWord(name, "set")) return OIT_NSSet; break; case 'U': if (startsWithWord(name, "URL")) return OIT_NSURL; break; default: break; } return OIT_None; }
bool operator<(DeclarationName LHS, DeclarationName RHS) { if (LHS.getNameKind() != RHS.getNameKind()) return LHS.getNameKind() < RHS.getNameKind(); switch (LHS.getNameKind()) { case DeclarationName::Identifier: return LHS.getAsIdentifierInfo()->getName() < RHS.getAsIdentifierInfo()->getName(); case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: { Selector LHSSelector = LHS.getObjCSelector(); Selector RHSSelector = RHS.getObjCSelector(); for (unsigned I = 0, N = std::min(LHSSelector.getNumArgs(), RHSSelector.getNumArgs()); I != N; ++I) { IdentifierInfo *LHSId = LHSSelector.getIdentifierInfoForSlot(I); IdentifierInfo *RHSId = RHSSelector.getIdentifierInfoForSlot(I); if (!LHSId || !RHSId) return LHSId && !RHSId; switch (LHSId->getName().compare(RHSId->getName())) { case -1: return true; case 1: return false; default: break; } } return LHSSelector.getNumArgs() < RHSSelector.getNumArgs(); } case DeclarationName::CXXConstructorName: case DeclarationName::CXXDestructorName: case DeclarationName::CXXConversionFunctionName: return QualTypeOrdering()(LHS.getCXXNameType(), RHS.getCXXNameType()); case DeclarationName::CXXOperatorName: return LHS.getCXXOverloadedOperator() < RHS.getCXXOverloadedOperator(); case DeclarationName::CXXUsingDirective: return false; } return false; }
void ODRHash::AddDeclarationName(DeclarationName Name) { // Index all DeclarationName and use index numbers to refer to them. auto Result = DeclNameMap.insert(std::make_pair(Name, DeclNameMap.size())); ID.AddInteger(Result.first->second); if (!Result.second) { // If found in map, the the DeclarationName has previously been processed. return; } // First time processing each DeclarationName, also process its details. AddBoolean(Name.isEmpty()); if (Name.isEmpty()) return; auto Kind = Name.getNameKind(); ID.AddInteger(Kind); switch (Kind) { case DeclarationName::Identifier: AddIdentifierInfo(Name.getAsIdentifierInfo()); break; case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: { Selector S = Name.getObjCSelector(); AddBoolean(S.isNull()); AddBoolean(S.isKeywordSelector()); AddBoolean(S.isUnarySelector()); unsigned NumArgs = S.getNumArgs(); for (unsigned i = 0; i < NumArgs; ++i) { AddIdentifierInfo(S.getIdentifierInfoForSlot(i)); } break; } case DeclarationName::CXXConstructorName: case DeclarationName::CXXDestructorName: AddQualType(Name.getCXXNameType()); break; case DeclarationName::CXXOperatorName: ID.AddInteger(Name.getCXXOverloadedOperator()); break; case DeclarationName::CXXLiteralOperatorName: AddIdentifierInfo(Name.getCXXLiteralIdentifier()); break; case DeclarationName::CXXConversionFunctionName: AddQualType(Name.getCXXNameType()); break; case DeclarationName::CXXUsingDirective: break; case DeclarationName::CXXDeductionGuideName: { auto *Template = Name.getCXXDeductionGuideTemplate(); AddBoolean(Template); if (Template) { AddDecl(Template); } } } }
unsigned serialization::ComputeHash(Selector Sel) { unsigned N = Sel.getNumArgs(); if (N == 0) ++N; unsigned R = 5381; for (unsigned I = 0; I != N; ++I) if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(I)) R = llvm::HashString(II->getName(), R); return R; }
bool ObjCMethodCall::argumentsMayEscape() const { if (isInSystemHeader() && !isInstanceMessage()) { Selector Sel = getSelector(); if (Sel.getNumArgs() == 1 && Sel.getIdentifierInfoForSlot(0)->isStr("valueWithPointer")) return true; } return CallEvent::argumentsMayEscape(); }
bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M, CheckerContext &C) const { Selector S = M.getSelector(); // Initialize the identifiers on first use. if (!CountSelectorII) CountSelectorII = &C.getASTContext().Idents.get("count"); // If the method returns collection count, record the value. return S.isUnarySelector() && (S.getIdentifierInfoForSlot(0) == CountSelectorII); }
void ODRHash::AddDeclarationName(DeclarationName Name) { AddBoolean(Name.isEmpty()); if (Name.isEmpty()) return; auto Kind = Name.getNameKind(); ID.AddInteger(Kind); switch (Kind) { case DeclarationName::Identifier: AddIdentifierInfo(Name.getAsIdentifierInfo()); break; case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: { Selector S = Name.getObjCSelector(); AddBoolean(S.isNull()); AddBoolean(S.isKeywordSelector()); AddBoolean(S.isUnarySelector()); unsigned NumArgs = S.getNumArgs(); for (unsigned i = 0; i < NumArgs; ++i) { AddIdentifierInfo(S.getIdentifierInfoForSlot(i)); } break; } case DeclarationName::CXXConstructorName: case DeclarationName::CXXDestructorName: AddQualType(Name.getCXXNameType()); break; case DeclarationName::CXXOperatorName: ID.AddInteger(Name.getCXXOverloadedOperator()); break; case DeclarationName::CXXLiteralOperatorName: AddIdentifierInfo(Name.getCXXLiteralIdentifier()); break; case DeclarationName::CXXConversionFunctionName: AddQualType(Name.getCXXNameType()); break; case DeclarationName::CXXUsingDirective: break; case DeclarationName::CXXDeductionGuideName: { auto *Template = Name.getCXXDeductionGuideTemplate(); AddBoolean(Template); if (Template) { AddDecl(Template); } } } }
ObjCMethodFamily Selector::getMethodFamilyImpl(Selector sel) { IdentifierInfo *first = sel.getIdentifierInfoForSlot(0); if (!first) return OMF_None; StringRef name = first->getName(); if (sel.isUnarySelector()) { if (name == "autorelease") return OMF_autorelease; if (name == "dealloc") return OMF_dealloc; if (name == "finalize") return OMF_finalize; if (name == "release") return OMF_release; if (name == "retain") return OMF_retain; if (name == "retainCount") return OMF_retainCount; if (name == "self") return OMF_self; if (name == "initialize") return OMF_initialize; } if (name == "performSelector" || name == "performSelectorInBackground" || name == "performSelectorOnMainThread") return OMF_performSelector; // The other method families may begin with a prefix of underscores. while (!name.empty() && name.front() == '_') name = name.substr(1); if (name.empty()) return OMF_None; switch (name.front()) { case 'a': if (startsWithWord(name, "alloc")) return OMF_alloc; break; case 'c': if (startsWithWord(name, "copy")) return OMF_copy; break; case 'i': if (startsWithWord(name, "init")) return OMF_init; break; case 'm': if (startsWithWord(name, "mutableCopy")) return OMF_mutableCopy; break; case 'n': if (startsWithWord(name, "new")) return OMF_new; break; default: break; } return OMF_None; }
/// \brief Get the ASTContext-specific selector. Selector GlobalSelector::getSelector(ASTContext &AST) const { if (isInvalid()) return Selector(); Selector GlobSel = Selector(reinterpret_cast<uintptr_t>(Val)); llvm::SmallVector<IdentifierInfo *, 8> Ids; for (unsigned i = 0, e = GlobSel.isUnarySelector() ? 1 : GlobSel.getNumArgs(); i != e; ++i) { IdentifierInfo *GlobII = GlobSel.getIdentifierInfoForSlot(i); IdentifierInfo *II = &AST.Idents.get(GlobII->getName(), GlobII->getName() + GlobII->getLength()); Ids.push_back(II); } return AST.Selectors.getSelector(GlobSel.getNumArgs(), Ids.data()); }
/// \brief Get a GlobalSelector for the ASTContext-specific selector. GlobalSelector GlobalSelector::get(Selector Sel, Program &Prog) { if (Sel.isNull()) return GlobalSelector(); ProgramImpl &ProgImpl = *static_cast<ProgramImpl*>(Prog.Impl); llvm::SmallVector<IdentifierInfo *, 8> Ids; for (unsigned i = 0, e = Sel.isUnarySelector() ? 1 : Sel.getNumArgs(); i != e; ++i) { IdentifierInfo *II = Sel.getIdentifierInfoForSlot(i); IdentifierInfo *GlobII = &ProgImpl.getIdents().get(II->getName(), II->getName() + II->getLength()); Ids.push_back(GlobII); } Selector GlobSel = ProgImpl.getSelectors().getSelector(Sel.getNumArgs(), Ids.data()); return GlobalSelector(GlobSel.getAsOpaquePtr()); }
void iOSAppSecAbusingURLSchemesChecker::checkASTDecl(const ObjCMethodDecl *pMD, AnalysisManager &rMgr, BugReporter &rBR) const { MSEC_DEBUG_FUNC( "redwud: ", "ENTER (Method)" ) ; do { if ( !pMD ) { break ; } Selector S = pMD ->getSelector() ; //There is no point of checking if you can't report it if ( !m_pInsecureInstBugType ) { MSEC_DEBUG("redwud: ","Reporting will fail!" ) ; break ; } // application: if ( S.getIdentifierInfoForSlot( 0 ) != m_piiApplication ) { //MSEC_DEBUG("redwud: ","!application: " << S.getAsString() ) ; break ; } unsigned iArgs = S.getNumArgs() ; ASTContext &Ctx = rBR.getContext() ; // //openURL: // do { if ( iArgs != 4 ) { break ; } if ( (S.getIdentifierInfoForSlot(1) != m_piiOpenURL) || (S.getIdentifierInfoForSlot(2) != m_piiSourceApplication) || (S.getIdentifierInfoForSlot(3) != m_piiAnnotation ) ) { break ; } if ( !checkOpenURL( pMD, Ctx ) ) { break ; } CMSecCommon::reportInsecureInstance( *m_pInsecureInstBugType , m_szReportDesc , rBR, pMD ) ; } while(_PASSING_) ; // //handleOpenURL: // do { if ( iArgs != 2 ) { break ; } if ( S.getIdentifierInfoForSlot(1) != m_piiHandleOpenURL ) { break ; } //Note: Intentionally made it common for both if ( !checkOpenURL( pMD, Ctx ) ) { break ; } CMSecCommon::reportInsecureInstance( *m_pInsecureInstBugType , m_szReportDesc, rBR, pMD ) ; } while(_PASSING_) ; //handleOpenURL: } while(_PASSING_) ; MSEC_DEBUG_FUNC( "redwud: ", "EXIT (Method)" ) ; }
//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") ; }