提交 bcacaed4 编写于 作者: C Cyrus Najmabadi

Prevent stack overflows when parsing regexes.

上级 c37bc38f
......@@ -103,7 +103,12 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
return;
}
var tree = RegexParser.Parse(virtualChars, options);
var tree = RegexParser.TryParse(virtualChars, options);
if (tree == null)
{
return;
}
foreach (var diag in tree.Diagnostics)
{
context.ReportDiagnostic(Diagnostic.Create(
......
......@@ -51,7 +51,12 @@ public override void AddClassifications(Workspace workspace, SyntaxToken token,
return;
}
var tree = RegexParser.Parse(chars, options);
var tree = RegexParser.TryParse(chars, options);
if (tree == null)
{
return;
}
var visitor = new Visitor(result);
AddClassifications(tree.Root, visitor, result);
}
......
......@@ -61,17 +61,30 @@ private void TryParseSubTrees(string stringText, RegexOptions options)
}
}
private RegexTree TryParseTree(string stringText, RegexOptions options, bool conversionFailureOk)
private (SyntaxToken, RegexTree, ImmutableArray<VirtualChar>) JustParseTree(
string stringText, RegexOptions options, bool conversionFailureOk)
{
var token = GetStringToken(stringText);
var allChars = _service.TryConvertToVirtualChars(token);
if (allChars.IsDefault)
{
Assert.True(conversionFailureOk, "Failed to convert text to token.");
return (token, null, allChars);
}
var tree = RegexParser.TryParse(allChars, options);
return (token, tree, allChars);
}
private RegexTree TryParseTree(string stringText, RegexOptions options, bool conversionFailureOk)
{
var (token, tree, allChars) = JustParseTree(stringText, options, conversionFailureOk);
if (tree == null)
{
Assert.True(conversionFailureOk);
return null;
}
var tree = RegexParser.Parse(allChars, options);
Assert.NotNull(tree);
CheckInvariants(tree, allChars);
try
......@@ -10341,5 +10354,27 @@ public void TestNegatedCharacterClass1()
}
#endregion
[Fact]
public void TestDeepRecursion()
{
var (token, tree, chars) =
JustParseTree(
@"@""((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((""", RegexOptions.None, conversionFailureOk: false);
Assert.False(token.IsMissing);
Assert.False(chars.IsDefaultOrEmpty);
Assert.Null(tree);
}
}
}
......@@ -21,6 +21,7 @@ internal struct RegexParser
private RegexToken _currentToken;
private readonly List<string> _captureNames;
private readonly List<int> _captureNumbers;
private int _recursionDepth;
private RegexParser(
ImmutableArray<VirtualChar> text, RegexOptions options,
......@@ -42,25 +43,32 @@ private RegexToken ScanNextToken(bool allowTrivia)
return _currentToken;
}
public static RegexTree Parse(ImmutableArray<VirtualChar> text, RegexOptions options)
public static RegexTree TryParse(ImmutableArray<VirtualChar> text, RegexOptions options)
{
// Parse the tree once, to figure out the capture groups. These are needed
// to then parse the tree again, as the captures will affect how we interpret
// certain things (i.e. escape references) and what errors will be reported.
//
// This is necessary as .net regexes allow references to *future* captures.
// As such, we don't know when we're seeing a reference if it's to something
// that exists or not.
var tree1 = new RegexParser(text, options, null, null).ParseTree();
try
{
// Parse the tree once, to figure out the capture groups. These are needed
// to then parse the tree again, as the captures will affect how we interpret
// certain things (i.e. escape references) and what errors will be reported.
//
// This is necessary as .net regexes allow references to *future* captures.
// As such, we don't know when we're seeing a reference if it's to something
// that exists or not.
var tree1 = new RegexParser(text, options, null, null).ParseTree();
var captureNames = new List<string>();
var captureNumbers = new List<int> { 0 };
var autoNumber = 1;
CollectCaptures(tree1.Root, captureNames, captureNumbers, ref autoNumber);
AssignNumbersToCaptureNames(captureNames, captureNumbers, autoNumber);
var captureNames = new List<string>();
var captureNumbers = new List<int> { 0 };
var autoNumber = 1;
CollectCaptures(tree1.Root, captureNames, captureNumbers, ref autoNumber);
AssignNumbersToCaptureNames(captureNames, captureNumbers, autoNumber);
var tree2 = new RegexParser(text, options, captureNames, captureNumbers).ParseTree();
return tree2;
var tree2 = new RegexParser(text, options, captureNames, captureNumbers).ParseTree();
return tree2;
}
catch (Exception e) when (StackGuard.IsInsufficientExecutionStackException(e))
{
return null;
}
}
private static void AssignNumbersToCaptureNames(List<string> captureNames, List<int> captureNumbers, int autoNumber)
......@@ -192,6 +200,20 @@ private void CollectDiagnostics(RegexToken token)
}
private RegexExpressionNode ParseExpression(bool consumeCloseParen)
{
try
{
_recursionDepth++;
StackGuard.EnsureSufficientExecutionStack(_recursionDepth);
return ParseExpressionWorker(consumeCloseParen);
}
finally
{
_recursionDepth--;
}
}
private RegexExpressionNode ParseExpressionWorker(bool consumeCloseParen)
{
RegexExpressionNode current = ParseSequence(consumeCloseParen);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册