int DeclarationName::compare(DeclarationName LHS, DeclarationName RHS) { if (LHS.getNameKind() != RHS.getNameKind()) return (LHS.getNameKind() < RHS.getNameKind() ? -1 : 1); switch (LHS.getNameKind()) { case DeclarationName::Identifier: { IdentifierInfo *LII = LHS.getAsIdentifierInfo(); IdentifierInfo *RII = RHS.getAsIdentifierInfo(); if (!LII) return RII ? -1 : 0; if (!RII) return 1; return LII->getName().compare(RII->getName()); } case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: { Selector LHSSelector = LHS.getObjCSelector(); Selector RHSSelector = RHS.getObjCSelector(); unsigned LN = LHSSelector.getNumArgs(), RN = RHSSelector.getNumArgs(); for (unsigned I = 0, N = std::min(LN, RN); I != N; ++I) { switch (LHSSelector.getNameForSlot(I).compare( RHSSelector.getNameForSlot(I))) { case -1: return true; case 1: return false; default: break; } } return compareInt(LN, RN); } case DeclarationName::CXXConstructorName: case DeclarationName::CXXDestructorName: case DeclarationName::CXXConversionFunctionName: if (QualTypeOrdering()(LHS.getCXXNameType(), RHS.getCXXNameType())) return -1; if (QualTypeOrdering()(RHS.getCXXNameType(), LHS.getCXXNameType())) return 1; return 0; case DeclarationName::CXXOperatorName: return compareInt(LHS.getCXXOverloadedOperator(), RHS.getCXXOverloadedOperator()); case DeclarationName::CXXLiteralOperatorName: return LHS.getCXXLiteralIdentifier()->getName().compare( RHS.getCXXLiteralIdentifier()->getName()); case DeclarationName::CXXUsingDirective: return 0; } return 0; }
DeclarationName::DeclarationName(Selector Sel) { if (!Sel.getAsOpaquePtr()) { Ptr = 0; return; } switch (Sel.getNumArgs()) { case 0: Ptr = reinterpret_cast<uintptr_t>(Sel.getAsIdentifierInfo()); assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo"); Ptr |= StoredObjCZeroArgSelector; break; case 1: Ptr = reinterpret_cast<uintptr_t>(Sel.getAsIdentifierInfo()); assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo"); Ptr |= StoredObjCOneArgSelector; break; default: Ptr = Sel.InfoPtr & ~Selector::ArgFlags; assert((Ptr & PtrMask) == 0 && "Improperly aligned MultiKeywordSelector"); Ptr |= StoredDeclarationNameExtra; break; } }
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; }
/// \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()); }
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(); }
/// \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 NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const { // Check if the method is annotated with analyzer_noreturn. if (const ObjCMethodDecl *MD = Msg.getDecl()) { MD = MD->getCanonicalDecl(); if (MD->hasAttr<AnalyzerNoReturnAttr>()) { C.generateSink(); return; } } // HACK: This entire check is to handle two messages in the Cocoa frameworks: // -[NSAssertionHandler // handleFailureInMethod:object:file:lineNumber:description:] // -[NSAssertionHandler // handleFailureInFunction:file:lineNumber:description:] // Eventually these should be annotated with __attribute__((noreturn)). // Because ObjC messages use dynamic dispatch, it is not generally safe to // assume certain methods can't return. In cases where it is definitely valid, // see if you can mark the methods noreturn or analyzer_noreturn instead of // adding more explicit checks to this method. if (!Msg.isInstanceMessage()) return; const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface(); if (!Receiver) return; if (!Receiver->getIdentifier()->isStr("NSAssertionHandler")) return; Selector Sel = Msg.getSelector(); switch (Sel.getNumArgs()) { default: return; case 4: lazyInitKeywordSelector(HandleFailureInFunctionSel, C.getASTContext(), "handleFailureInFunction", "file", "lineNumber", "description", nullptr); if (Sel != HandleFailureInFunctionSel) return; break; case 5: lazyInitKeywordSelector(HandleFailureInMethodSel, C.getASTContext(), "handleFailureInMethod", "object", "file", "lineNumber", "description", nullptr); if (Sel != HandleFailureInMethodSel) return; break; } // If we got here, it's one of the messages we care about. C.generateSink(); }
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)" ) ; }
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); } } } }
void NoReturnSubprogramChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const { // HACK: This entire check is to handle two messages in the Cocoa frameworks: // -[NSAssertionHandler // handleFailureInMethod:object:file:lineNumber:description:] // -[NSAssertionHandler // handleFailureInSubprogram:file:lineNumber:description:] // Eventually these should be annotated with __attribute__((noreturn)). // Because ObjC messages use dynamic dispatch, it is not generally safe to // assume certain methods can't return. In cases where it is definitely valid, // see if you can mark the methods noreturn or analyzer_noreturn instead of // adding more explicit checks to this method. if (!Msg.isInstanceMessage()) return; const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface(); if (!Receiver) return; if (!Receiver->getIdentifier()->isStr("NSAssertionHandler")) return; Selector Sel = Msg.getSelector(); switch (Sel.getNumArgs()) { default: return; case 4: if (!isMultiArgSelector(&Sel, "handleFailureInSubprogram", "file", "lineNumber", "description", NULL)) return; break; case 5: if (!isMultiArgSelector(&Sel, "handleFailureInMethod", "object", "file", "lineNumber", "description", NULL)) return; break; } // If we got here, it's one of the messages we care about. C.generateSink(); }
bool Sema::CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs, Selector Sel, ObjCMethodDecl *Method, bool isClassMessage, SourceLocation lbrac, SourceLocation rbrac, QualType &ReturnType, ExprValueKind &VK) { if (!Method) { // Apply default argument promotion as for (C99 6.5.2.2p6). for (unsigned i = 0; i != NumArgs; i++) { if (Args[i]->isTypeDependent()) continue; DefaultArgumentPromotion(Args[i]); } unsigned DiagID = isClassMessage ? diag::warn_class_method_not_found : diag::warn_inst_method_not_found; Diag(lbrac, DiagID) << Sel << isClassMessage << SourceRange(lbrac, rbrac); ReturnType = Context.getObjCIdType(); VK = VK_RValue; return false; } ReturnType = Method->getSendResultType(); VK = Expr::getValueKindForType(Method->getResultType()); unsigned NumNamedArgs = Sel.getNumArgs(); // Method might have more arguments than selector indicates. This is due // to addition of c-style arguments in method. if (Method->param_size() > Sel.getNumArgs()) NumNamedArgs = Method->param_size(); // FIXME. This need be cleaned up. if (NumArgs < NumNamedArgs) { Diag(lbrac, diag::err_typecheck_call_too_few_args) << 2 << NumNamedArgs << NumArgs; return false; } bool IsError = false; for (unsigned i = 0; i < NumNamedArgs; i++) { // We can't do any type-checking on a type-dependent argument. if (Args[i]->isTypeDependent()) continue; Expr *argExpr = Args[i]; ParmVarDecl *Param = Method->param_begin()[i]; assert(argExpr && "CheckMessageArgumentTypes(): missing expression"); if (RequireCompleteType(argExpr->getSourceRange().getBegin(), Param->getType(), PDiag(diag::err_call_incomplete_argument) << argExpr->getSourceRange())) return true; InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, Param); ExprResult ArgE = PerformCopyInitialization(Entity, lbrac, Owned(argExpr)); if (ArgE.isInvalid()) IsError = true; else Args[i] = ArgE.takeAs<Expr>(); } // Promote additional arguments to variadic methods. if (Method->isVariadic()) { for (unsigned i = NumNamedArgs; i < NumArgs; ++i) { if (Args[i]->isTypeDependent()) continue; IsError |= DefaultVariadicArgumentPromotion(Args[i], VariadicMethod, 0); } } else { // Check for extra arguments to non-variadic methods. if (NumArgs != NumNamedArgs) { Diag(Args[NumNamedArgs]->getLocStart(), diag::err_typecheck_call_too_many_args) << 2 /*method*/ << NumNamedArgs << NumArgs << Method->getSourceRange() << SourceRange(Args[NumNamedArgs]->getLocStart(), Args[NumArgs-1]->getLocEnd()); } } DiagnoseSentinelCalls(Method, lbrac, Args, NumArgs); return IsError; }
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") ; }