diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp --- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -44,6 +44,12 @@ // optimize this on. static const unsigned MaxMemoizationEntries = 10000; +// The maximum number of steps thru ancestors allowed to run before +// give up. The limit could only be hit by infinity / too deep +// recursion or very complicated automatically generated types +// inheritance. +static const unsigned MaxDerivationComplexity = 10000; + enum class MatchType { Ancestors, @@ -1357,35 +1363,47 @@ // Returns true if the given C++ class is directly or indirectly derived // from a base type with the given name. A class is not considered to be // derived from itself. -bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration, +bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *DeclarationIn, const Matcher &Base, BoundNodesTreeBuilder *Builder, bool Directly) { - if (!Declaration->hasDefinition()) - return false; - for (const auto &It : Declaration->bases()) { - const Type *TypeNode = It.getType().getTypePtr(); + SmallVector DeclarationsStack; + int iterations = 0; + DeclarationsStack.push_back(DeclarationIn); + while (!DeclarationsStack.empty()) { + const CXXRecordDecl *Declaration = DeclarationsStack.pop_back_val(); + if (++iterations > MaxDerivationComplexity || + DeclarationsStack.size() > MaxDerivationComplexity) { + // This can happen in case of too deep derivation chain or recursion. + return false; + } + if (!Declaration->hasDefinition()) + return false; + for (const auto &It : Declaration->bases()) { + const Type *TypeNode = It.getType().getTypePtr(); - if (typeHasMatchingAlias(TypeNode, Base, Builder)) - return true; + if (typeHasMatchingAlias(TypeNode, Base, Builder)) + return true; - // FIXME: Going to the primary template here isn't really correct, but - // unfortunately we accept a Decl matcher for the base class not a Type - // matcher, so it's the best thing we can do with our current interface. - CXXRecordDecl *ClassDecl = getAsCXXRecordDeclOrPrimaryTemplate(TypeNode); - if (!ClassDecl) - continue; - if (ClassDecl == Declaration) { - // This can happen for recursive template definitions. - continue; - } - BoundNodesTreeBuilder Result(*Builder); - if (Base.matches(*ClassDecl, this, &Result)) { - *Builder = std::move(Result); - return true; + // FIXME: Going to the primary template here isn't really correct, but + // unfortunately we accept a Decl matcher for the base class not a Type + // matcher, so it's the best thing we can do with our current interface. + CXXRecordDecl *ClassDecl = getAsCXXRecordDeclOrPrimaryTemplate(TypeNode); + if (!ClassDecl) + continue; + if (ClassDecl == Declaration) { + // This can happen for recursive template definitions. + continue; + } + BoundNodesTreeBuilder Result(*Builder); + if (Base.matches(*ClassDecl, this, &Result)) { + *Builder = std::move(Result); + return true; + } + if (!Directly) { + DeclarationsStack.push_back(ClassDecl); + } } - if (!Directly && classIsDerivedFrom(ClassDecl, Base, Builder, Directly)) - return true; } return false; } diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -168,6 +168,14 @@ varDecl(hasType(namedDecl(hasName("S")))))); } +TEST(TypeMatcherDeathTest, isDerivedRecursion) { + EXPECT_TRUE(notMatches( + "template struct t1;" + "template struct t2: t1< T > {};" + "template struct t1: t2< T > {};", + cxxRecordDecl(isDerivedFrom("::T")))); +} + TEST(TypeMatcher, MatchesDeclTypes) { // TypedefType -> TypedefNameDecl EXPECT_TRUE(matches("typedef int I; void f(I i);",