/// An AST check that diagnose when the class requires a -dealloc method and /// is missing one. void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, BugReporter &BR) const { assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly); assert(!Mgr.getLangOpts().ObjCAutoRefCount); initIdentifierInfoAndSelectors(Mgr.getASTContext()); const ObjCInterfaceDecl *ID = D->getClassInterface(); // If the class is known to have a lifecycle with a separate teardown method // then it may not require a -dealloc method. if (classHasSeparateTeardown(ID)) return; // Does the class contain any synthesized properties that are retainable? // If not, skip the check entirely. const ObjCPropertyImplDecl *PropImplRequiringRelease = nullptr; bool HasOthers = false; for (const auto *I : D->property_impls()) { if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) { if (!PropImplRequiringRelease) PropImplRequiringRelease = I; else { HasOthers = true; break; } } } if (!PropImplRequiringRelease) return; const ObjCMethodDecl *MD = nullptr; // Scan the instance methods for "dealloc". for (const auto *I : D->instance_methods()) { if (I->getSelector() == DeallocSel) { MD = I; break; } } if (!MD) { // No dealloc found. const char* Name = "Missing -dealloc"; std::string Buf; llvm::raw_string_ostream OS(Buf); OS << "'" << *D << "' lacks a 'dealloc' instance method but " << "must release '" << *PropImplRequiringRelease->getPropertyIvarDecl() << "'"; if (HasOthers) OS << " and others"; PathDiagnosticLocation DLoc = PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC, OS.str(), DLoc); return; } }
static PathDiagnosticLocation makeLocation(const StmtSequence &S, AnalysisManager &Mgr) { ASTContext &ACtx = Mgr.getASTContext(); return PathDiagnosticLocation::createBegin( S.front(), ACtx.getSourceManager(), Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl())); }
void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D, AnalysisManager &mgr, BugReporter &BR) const { CFG *cfg = mgr.getCFG(D); if (!cfg) return; // A list of variables referenced in possibly overflowing malloc operands. llvm::SmallVector<MallocOverflowCheck, 2> PossibleMallocOverflows; for (CFG::iterator it = cfg->begin(), ei = cfg->end(); it != ei; ++it) { CFGBlock *block = *it; for (CFGBlock::iterator bi = block->begin(), be = block->end(); bi != be; ++bi) { if (const CFGStmt *CS = bi->getAs<CFGStmt>()) { if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) { // Get the callee. const FunctionDecl *FD = TheCall->getDirectCallee(); if (!FD) return; // Get the name of the callee. If it's a builtin, strip off the prefix. IdentifierInfo *FnInfo = FD->getIdentifier(); if (!FnInfo) return; if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) { if (TheCall->getNumArgs() == 1) CheckMallocArgument(PossibleMallocOverflows, TheCall->getArg(0), mgr.getASTContext()); } } } } } OutputPossibleOverflows(PossibleMallocOverflows, D, BR, mgr); }
void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM, BugReporter &BR) const { // Currently this matches CoreFoundation opaque pointer typedefs. auto CSuspiciousNumberObjectExprM = expr(ignoringParenImpCasts( expr(hasType( typedefType(hasDeclaration(anyOf( typedefDecl(hasName("CFNumberRef")), typedefDecl(hasName("CFBooleanRef"))))))) .bind("c_object"))); // Currently this matches XNU kernel number-object pointers. auto CppSuspiciousNumberObjectExprM = expr(ignoringParenImpCasts( expr(hasType(hasCanonicalType( pointerType(pointee(hasCanonicalType( recordType(hasDeclaration( anyOf( cxxRecordDecl(hasName("OSBoolean")), cxxRecordDecl(hasName("OSNumber")) .bind("osnumber")))))))))) .bind("cpp_object"))); // Currently this matches NeXTSTEP number objects. auto ObjCSuspiciousNumberObjectExprM = expr(ignoringParenImpCasts( expr(hasType(hasCanonicalType( objcObjectPointerType(pointee( qualType(hasCanonicalType( qualType(hasDeclaration( objcInterfaceDecl(hasName("NSNumber"))))))))))) .bind("objc_object"))); auto SuspiciousNumberObjectExprM = anyOf( CSuspiciousNumberObjectExprM, CppSuspiciousNumberObjectExprM, ObjCSuspiciousNumberObjectExprM); // Useful for predicates like "Unless we've seen the same object elsewhere". auto AnotherSuspiciousNumberObjectExprM = expr(anyOf( equalsBoundNode("c_object"), equalsBoundNode("objc_object"), equalsBoundNode("cpp_object"))); // The .bind here is in order to compose the error message more accurately. auto ObjCSuspiciousScalarBooleanTypeM = qualType(typedefType(hasDeclaration( typedefDecl(hasName("BOOL"))))).bind("objc_bool_type"); // The .bind here is in order to compose the error message more accurately. auto SuspiciousScalarBooleanTypeM = qualType(anyOf(qualType(booleanType()).bind("cpp_bool_type"), ObjCSuspiciousScalarBooleanTypeM)); // The .bind here is in order to compose the error message more accurately. // Also avoid intptr_t and uintptr_t because they were specifically created // for storing pointers. auto SuspiciousScalarNumberTypeM = qualType(hasCanonicalType(isInteger()), unless(typedefType(hasDeclaration( typedefDecl(matchesName("^::u?intptr_t$")))))) .bind("int_type"); auto SuspiciousScalarTypeM = qualType(anyOf(SuspiciousScalarBooleanTypeM, SuspiciousScalarNumberTypeM)); auto SuspiciousScalarExprM = expr(ignoringParenImpCasts(expr(hasType(SuspiciousScalarTypeM)))); auto ConversionThroughAssignmentM = binaryOperator(allOf(hasOperatorName("="), hasLHS(SuspiciousScalarExprM), hasRHS(SuspiciousNumberObjectExprM))); auto ConversionThroughBranchingM = ifStmt(allOf( hasCondition(SuspiciousNumberObjectExprM), unless(hasConditionVariableStatement(declStmt()) ))).bind("pedantic"); auto ConversionThroughCallM = callExpr(hasAnyArgument(allOf(hasType(SuspiciousScalarTypeM), ignoringParenImpCasts( SuspiciousNumberObjectExprM)))); // We bind "check_if_null" to modify the warning message // in case it was intended to compare a pointer to 0 with a relatively-ok // construct "x == 0" or "x != 0". auto ConversionThroughEquivalenceM = binaryOperator(allOf(anyOf(hasOperatorName("=="), hasOperatorName("!=")), hasEitherOperand(SuspiciousNumberObjectExprM), hasEitherOperand(SuspiciousScalarExprM .bind("check_if_null")))) .bind("comparison"); auto ConversionThroughComparisonM = binaryOperator(allOf(anyOf(hasOperatorName(">="), hasOperatorName(">"), hasOperatorName("<="), hasOperatorName("<")), hasEitherOperand(SuspiciousNumberObjectExprM), hasEitherOperand(SuspiciousScalarExprM))) .bind("comparison"); auto ConversionThroughConditionalOperatorM = conditionalOperator(allOf( hasCondition(SuspiciousNumberObjectExprM), unless(hasTrueExpression( hasDescendant(AnotherSuspiciousNumberObjectExprM))), unless(hasFalseExpression( hasDescendant(AnotherSuspiciousNumberObjectExprM))))) .bind("pedantic"); auto ConversionThroughExclamationMarkM = unaryOperator(allOf(hasOperatorName("!"), has(expr(SuspiciousNumberObjectExprM)))) .bind("pedantic"); auto ConversionThroughExplicitBooleanCastM = explicitCastExpr(allOf(hasType(SuspiciousScalarBooleanTypeM), has(expr(SuspiciousNumberObjectExprM)))); auto ConversionThroughExplicitNumberCastM = explicitCastExpr(allOf(hasType(SuspiciousScalarNumberTypeM), has(expr(SuspiciousNumberObjectExprM)))); auto ConversionThroughInitializerM = declStmt(hasSingleDecl( varDecl(hasType(SuspiciousScalarTypeM), hasInitializer(SuspiciousNumberObjectExprM)))); auto FinalM = stmt(anyOf(ConversionThroughAssignmentM, ConversionThroughBranchingM, ConversionThroughCallM, ConversionThroughComparisonM, ConversionThroughConditionalOperatorM, ConversionThroughEquivalenceM, ConversionThroughExclamationMarkM, ConversionThroughExplicitBooleanCastM, ConversionThroughExplicitNumberCastM, ConversionThroughInitializerM)).bind("conv"); MatchFinder F; Callback CB(this, BR, AM.getAnalysisDeclContext(D)); F.addMatcher(stmt(forEachDescendant(FinalM)), &CB); F.match(*D->getBody(), AM.getASTContext()); }