From 15fb73551b95b116310bd9d30f1d102b24a8e6aa Mon Sep 17 00:00:00 2001 From: Neil Henderson Date: Mon, 28 Jun 2021 15:02:03 +1000 Subject: [PATCH] Fixes #485. Added a timeout to NegatableTypeOrMemberReferenceRegex and MemberReferenceRegex to prevent CPU hang when given input that can cause expensive regex backtracking. --- .../CommonInterest.cs | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/CommonInterest.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/CommonInterest.cs index eaa836aff..d70298300 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/CommonInterest.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/CommonInterest.cs @@ -63,9 +63,11 @@ internal static class CommonInterest private const RegexOptions FileNamePatternRegexOptions = RegexOptions.IgnoreCase | RegexOptions.Singleline; - private static readonly Regex NegatableTypeOrMemberReferenceRegex = new Regex(@"^(?!)?\[(?[^\[\]\:]+)+\](?:\:\:(?\S+))?\s*$", RegexOptions.Singleline | RegexOptions.CultureInvariant); + private static readonly TimeSpan RegexMatchTimeout = TimeSpan.FromMilliseconds(100); // Prevent expensive CPU hang in Regex.Match if backtracking occurs due to pathological input (see #485). - private static readonly Regex MemberReferenceRegex = new Regex(@"^\[(?[^\[\]\:]+)+\]::(?\S+)\s*$", RegexOptions.Singleline | RegexOptions.CultureInvariant); + private static readonly Regex NegatableTypeOrMemberReferenceRegex = new Regex(@"^(?!)?\[(?[^\[\]\:]+)+\](?:\:\:(?\S+))?\s*$", RegexOptions.Singleline | RegexOptions.CultureInvariant, RegexMatchTimeout); + + private static readonly Regex MemberReferenceRegex = new Regex(@"^\[(?[^\[\]\:]+)+\]::(?\S+)\s*$", RegexOptions.Singleline | RegexOptions.CultureInvariant, RegexMatchTimeout); /// /// An array with '.' as its only element. @@ -84,8 +86,16 @@ internal static IEnumerable ReadTypesAndMembers(AnalyzerOptions a { foreach (string line in ReadAdditionalFiles(analyzerOptions, fileNamePattern, cancellationToken)) { - Match match = NegatableTypeOrMemberReferenceRegex.Match(line); - if (!match.Success) + Match? match = null; + try + { + match = NegatableTypeOrMemberReferenceRegex.Match(line); + } + catch (RegexMatchTimeoutException) + { + } + + if (match == null || !match.Success) { throw new InvalidOperationException($"Parsing error on line: {line}"); } @@ -175,8 +185,16 @@ internal static IEnumerable ReadLinesFromAdditionalFile(SourceText text) internal static QualifiedMember ParseAdditionalFileMethodLine(string line) { - Match match = MemberReferenceRegex.Match(line); - if (!match.Success) + Match? match = null; + try + { + match = MemberReferenceRegex.Match(line); + } + catch (RegexMatchTimeoutException) + { + } + + if (match == null || !match.Success) { throw new InvalidOperationException($"Parsing error on line: {line}"); }