diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexMatcher.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexMatcher.cs index 72cffa72013eff..e96525cf07a519 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexMatcher.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexMatcher.cs @@ -784,11 +784,17 @@ private int FindFinalStatePosition(ReadOnlySpan input, int i, int timeoutO } } - // Now run the DFA or NFA traversal from the current point using the current state. + // Now run the DFA or NFA traversal from the current point using the current state. If timeouts are being checked, + // we need to pop out of the inner loop every now and then to do the timeout check in this outer loop. + const int CharsPerTimeoutCheck = 10_000; + ReadOnlySpan inputForInnerLoop = _checkTimeout && input.Length - i > CharsPerTimeoutCheck ? + input.Slice(0, i + CharsPerTimeoutCheck) : + input; + int finalStatePosition; int findResult = currentState.NfaState is not null ? - FindFinalStatePositionDeltas(builder, input, ref i, ref currentState, ref matchLength, out finalStatePosition) : - FindFinalStatePositionDeltas(builder, input, ref i, ref currentState, ref matchLength, out finalStatePosition); + FindFinalStatePositionDeltas(builder, inputForInnerLoop, ref i, ref currentState, ref matchLength, out finalStatePosition) : + FindFinalStatePositionDeltas(builder, inputForInnerLoop, ref i, ref currentState, ref matchLength, out finalStatePosition); // If we reached a final or deadend state, we're done. if (findResult > 0) @@ -802,17 +808,20 @@ private int FindFinalStatePosition(ReadOnlySpan input, int i, int timeoutO // find result will be 0, otherwise negative. if (findResult < 0) { - if ((uint)i >= (uint)input.Length) + if (i >= input.Length) { // We ran out of input. No match. break; } - // We failed to transition. Upgrade to DFA mode. - Debug.Assert(currentState.DfaState is not null); - NfaMatchingState nfaState = perThreadData.NfaState; - nfaState.InitializeFrom(currentState.DfaState); - currentState = new CurrentState(nfaState); + if (i < inputForInnerLoop.Length) + { + // We failed to transition. Upgrade to DFA mode. + Debug.Assert(currentState.DfaState is not null); + NfaMatchingState nfaState = perThreadData.NfaState; + nfaState.InitializeFrom(currentState.DfaState); + currentState = new CurrentState(nfaState); + } } // Check for a timeout before continuing. diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Match.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Match.Tests.cs index 8bdcf61aacfcf6..d43057fc9caf12 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Match.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Match.Tests.cs @@ -1120,12 +1120,6 @@ public async Task Match_Timeout_Loop_Throws(RegexEngine engine) [MemberData(nameof(RegexHelpers.AvailableEngines_MemberData), MemberType = typeof(RegexHelpers))] public async Task Match_Timeout_Repetition_Throws(RegexEngine engine) { - if (engine == RegexEngine.NonBacktracking) - { - // [ActiveIssue("https://github.com/dotnet/runtime/issues/65991")] - return; - } - int repetitionCount = 800_000_000; Regex regex = await RegexHelpers.GetRegexAsync(engine, @"a\s{" + repetitionCount + "}", RegexOptions.None, TimeSpan.FromSeconds(1)); string input = @"a" + new string(' ', repetitionCount) + @"b";