1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-25 09:55:29 +02:00

Bug 512297 - Impose a limit on the nesting depth of template arguments

This avoid stack overflows when processing code that has very deeply
nested template arguments.

Change-Id: I748e2d827fd1e7842737ec0652cf3733ae9962b1
This commit is contained in:
Nathan Ridge 2017-09-26 22:27:27 -04:00
parent c7e475e50e
commit bf73bb58bc
8 changed files with 154 additions and 28 deletions

View file

@ -28,6 +28,7 @@ import org.eclipse.cdt.core.dom.IName;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
@ -37,6 +38,8 @@ import org.eclipse.cdt.core.dom.ast.IASTImplicitNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.dom.ast.IASTProblemDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTProblemStatement;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
@ -92,6 +95,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateTypeParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable;
import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.internal.core.dom.parser.IntegralValue;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTNameBase;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType;
@ -10383,4 +10387,51 @@ public class AST2TemplateTests extends AST2CPPTestBase {
public void testMemberOfUnknownMemberClass_519819() throws Exception {
parseAndCheckBindings();
}
// template <class> class any {};
// typedef any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<any<any<any<any<any<any<
// any<any<any<any<any<any<any<any<any<int>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>>> parser_killer_type;
public void testTemplateArgumentNestingDepthLimit_512297() throws Exception {
BindingAssertionHelper helper = getAssertionHelper();
IASTTranslationUnit tu = helper.getTranslationUnit();
IASTDeclaration[] declarations = tu.getDeclarations();
assertEquals(2, declarations.length);
assertInstance(declarations[1], IASTProblemDeclaration.class);
IASTProblemDeclaration problemDecl = (IASTProblemDeclaration) declarations[1];
IASTProblem problem = problemDecl.getProblem().getOriginalProblem();
assertNotNull(problem);
assertEquals(IProblem.TEMPLATE_ARGUMENT_NESTING_DEPTH_LIMIT_EXCEEDED, problem.getID());
}
}

View file

@ -30,4 +30,17 @@ public interface IASTProblem extends IProblem, IASTNode {
*/
@Override
public IASTProblem copy(CopyStyle style);
/**
* If this problem was triggered by another problem, returns that problem,
* otherwise returns null.
* @since 6.4
*/
public IASTProblem getOriginalProblem();
/**
* Record another problem as being the original cause of this one.
* @since 6.4
*/
public void setOriginalProblem(IASTProblem original);
}

View file

@ -111,7 +111,7 @@ public interface IProblem {
public final static int SYNTAX_RELATED = 0x04000000;
/**
* IProblem relates to an implementation of design limitation
* IProblem relates to an implementation or design limitation
*/
public final static int INTERNAL_RELATED = 0x10000000;
@ -356,6 +356,12 @@ public interface IProblem {
*/
public final static int MISSING_SEMICOLON = SYNTAX_RELATED | 0x002;
/**
* The parser's template argument nesting depth limit was exceeded.
* @since 6.4
*/
public final static int TEMPLATE_ARGUMENT_NESTING_DEPTH_LIMIT_EXCEEDED = INTERNAL_RELATED | 0x01;
/**
* @deprecated Not used.
* @noreference This field is not intended to be referenced by clients.

View file

@ -98,11 +98,14 @@ public class ASTProblem extends ASTNode implements IASTProblem {
ParserMessages.getString("ParserProblemFactory.error.syntax.syntaxError")); //$NON-NLS-1$
errorMessages.put(Integer.valueOf(MISSING_SEMICOLON),
ParserMessages.getString("ParserProblemFactory.error.syntax.missingSemicolon")); //$NON-NLS-1$
errorMessages.put(Integer.valueOf(TEMPLATE_ARGUMENT_NESTING_DEPTH_LIMIT_EXCEEDED),
ParserMessages.getString("ParserProblemFactory.error.syntax.templateArgumentNestingDepthLimitExceeded")); //$NON-NLS-1$
}
private final int id;
private final char[] arg;
private boolean isError;
private IASTProblem originalProblem = null;
public ASTProblem(IASTNode parent, ASTNodeProperty property, int id, char[] arg, boolean isError,
int startNumber, int endNumber) {
@ -158,20 +161,27 @@ public class ASTProblem extends ASTNode implements IASTProblem {
return ParserMessages.getFormattedString("BaseProblemFactory.problemPattern", args); //$NON-NLS-1$
}
public static String getMessage(int id, String arg) {
private static String getMessage(int id, String arg, IASTProblem originalProblem) {
String msg = errorMessages.get(Integer.valueOf(id));
if (msg == null)
msg = ""; //$NON-NLS-1$
if (arg != null) {
return MessageFormat.format(msg, new Object[] {arg});
msg = MessageFormat.format(msg, new Object[] {arg});
}
if (originalProblem != null) {
msg = MessageFormat.format("{0}: {1}", msg, originalProblem.getMessage()); //$NON-NLS-1$
}
return msg;
}
public static String getMessage(int id, String arg) {
return getMessage(id, arg, null);
}
@Override
public String getMessage() {
return getMessage(id, arg == null ? null : new String(arg));
return getMessage(id, arg == null ? null : new String(arg), originalProblem);
}
@Override
@ -219,4 +229,16 @@ public class ASTProblem extends ASTNode implements IASTProblem {
}
return INT_VALUE_NOT_PROVIDED;
}
@Override
public IASTProblem getOriginalProblem() {
return originalProblem;
}
@Override
public void setOriginalProblem(IASTProblem original) {
originalProblem = original;
}
}

View file

@ -739,11 +739,18 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
return result;
}
protected IASTProblemDeclaration skipProblemDeclaration(int offset) {
protected IASTProblemDeclaration skipProblemDeclaration(int offset) {
return skipProblemDeclaration(offset, null);
}
protected IASTProblemDeclaration skipProblemDeclaration(int offset, IASTProblem origProblem) {
failParse();
declarationMark= null;
int endOffset = skipToSemiOrClosingBrace(offset, false);
IASTProblem problem= createProblem(IProblem.SYNTAX_ERROR, offset, endOffset-offset);
if (origProblem != null && origProblem.getID() != IProblem.SYNTAX_ERROR) {
problem.setOriginalProblem(origProblem);
}
return buildProblemDeclaration(problem);
}
@ -1771,7 +1778,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser {
}
}
return new IASTDeclaration[] {skipProblemDeclaration(offset)};
return new IASTDeclaration[] {skipProblemDeclaration(offset, origProblem)};
}
protected IASTDeclaration asmDeclaration() throws EndOfFileException, BacktrackException {

View file

@ -14,6 +14,7 @@ package org.eclipse.cdt.internal.core.dom.parser;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.parser.IProblem;
/**
* @author jcamelon
@ -95,4 +96,9 @@ public class BacktrackException extends Exception {
public StackTraceElement[] getStackTrace() {
return EMPTY_STACK;
}
// Don't try alternative parses if this returns true.
public boolean isFatal() {
return problem != null && problem.getID() == IProblem.TEMPLATE_ARGUMENT_NESTING_DEPTH_LIMIT_EXCEEDED;
}
}

View file

@ -185,6 +185,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
private static final int DEFAULT_PARM_LIST_SIZE = 4;
private static final int DEFAULT_CATCH_HANDLER_LIST_SIZE= 4;
private static final int TEMPLATE_ARGUMENT_NESTING_DEPTH_LIMIT = 256;
// This is a parameter to the protected function {@link #declarator(DtorStrategy, DeclarationOptions)}
// so it needs to be protected too.
@ -200,6 +201,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
protected ICPPASTTranslationUnit translationUnit;
private int functionBodyCount;
private int templateArgumentNestingDepth = 0;
private char[] currentClassName;
private char[] additionalNumericalSuffixes;
@ -281,6 +283,9 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
try {
return nameSpecifier(ctx, strat);
} catch (BacktrackException e) {
if (e.isFatal()) {
throw e;
}
if (strat.setNextAlternative(true /* previous alternative failed to parse */)) {
backup(m);
} else {
@ -705,33 +710,42 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
}
private List<IASTNode> templateArgumentList(ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException {
int startingOffset = LA(1).getOffset();
int endOffset = 0;
List<IASTNode> list= null;
boolean needComma= false;
int lt1= LT(1);
while (lt1 != IToken.tGT && lt1 != IToken.tGT_in_SHIFTR && lt1 != IToken.tEOC) {
if (needComma) {
if (lt1 != IToken.tCOMMA) {
throwBacktrack(startingOffset, endOffset - startingOffset);
if (templateArgumentNestingDepth >= TEMPLATE_ARGUMENT_NESTING_DEPTH_LIMIT) {
throwBacktrack(createProblem(IProblem.TEMPLATE_ARGUMENT_NESTING_DEPTH_LIMIT_EXCEEDED,
LA(1).getOffset(), 1));
}
++templateArgumentNestingDepth;
try {
int startingOffset = LA(1).getOffset();
int endOffset = 0;
List<IASTNode> list= null;
boolean needComma= false;
int lt1= LT(1);
while (lt1 != IToken.tGT && lt1 != IToken.tGT_in_SHIFTR && lt1 != IToken.tEOC) {
if (needComma) {
if (lt1 != IToken.tCOMMA) {
throwBacktrack(startingOffset, endOffset - startingOffset);
}
consume();
} else {
needComma= true;
}
consume();
} else {
needComma= true;
IASTNode node= templateArgument(strat);
if (list == null) {
list= new ArrayList<>();
}
list.add(node);
lt1= LT(1);
}
IASTNode node= templateArgument(strat);
if (list == null) {
list= new ArrayList<>();
return Collections.emptyList();
}
list.add(node);
lt1= LT(1);
return list;
} finally {
--templateArgumentNestingDepth;
}
if (list == null) {
return Collections.emptyList();
}
return list;
}
private IASTNode templateArgument(ITemplateIdStrategy strat) throws EndOfFileException, BacktrackException {
@ -742,6 +756,9 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
typeId= typeId(DeclarationOptions.TYPEID);
lt1 = LT(1);
} catch (BacktrackException e) {
if (e.isFatal()) {
throw e;
}
}
if (typeId != null
@ -800,6 +817,9 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
}
// The type-id is longer, use it.
} catch (BacktrackException e) {
if (e.isFatal()) {
throw e;
}
// Failed to parse an expression, use the type id.
}

View file

@ -48,6 +48,7 @@ ScannerProblemFactory.error.scanner.floatWithBadPrefix=Floating constant "{0}" h
ParserProblemFactory.error.syntax.syntaxError=Syntax error
ParserProblemFactory.error.syntax.missingSemicolon=Missing ';'
ParserProblemFactory.error.syntax.templateArgumentNestingDepthLimitExceeded=Template argument nesting depth limit exceeded
BaseProblemFactory.problemPattern={0} in file: {1}:{2, number, integer}