diff --git a/stl/inc/regex b/stl/inc/regex index fd255b3254f..37621a24271 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -47,10 +47,6 @@ _STL_DISABLE_CLANG_WARNINGS #define _REGEX_MAX_COMPLEXITY_COUNT 10000000L // set to 0 to disable #endif // !defined(_REGEX_MAX_COMPLEXITY_COUNT) -#ifndef _REGEX_MAX_STACK_COUNT -#define _REGEX_MAX_STACK_COUNT 1000L // set to 0 to disable -#endif // !defined(_REGEX_MAX_STACK_COUNT) - #ifndef _ENHANCED_REGEX_VISUALIZER #ifdef _DEBUG #define _ENHANCED_REGEX_VISUALIZER 1 @@ -1725,6 +1721,9 @@ public: _Char_class_d = _Lookup_char_class(static_cast<_Elem>('D')); } + _Input_length = _STD distance(_Pfirst, _Plast); + _Frames_limit = _Calculate_frames_limit(); + // sanitize multiline mode setting #if _REGEX_LEGACY_MULTILINE_MODE _Sflags |= regex_constants::multiline; // old matcher applied multiline mode for all grammars @@ -1765,7 +1764,6 @@ public: } _Full = _Full_match; _Max_complexity_count = _REGEX_MAX_COMPLEXITY_COUNT; - _Max_stack_count = _REGEX_MAX_STACK_COUNT; _Frames_count = 0; _Matched = false; @@ -1819,12 +1817,13 @@ private: vector<_Loop_vals_v3_t<_Iter_diff_t<_It>>> _Loop_vals; vector<_Rx_state_frame_t<_It>> _Frames; size_t _Frames_count; + size_t _Frames_limit; + _Iter_diff_t<_It> _Input_length; size_t _Push_frame(_Rx_unwind_ops _Code, _Node_base* _Node); void _Pop_frame(size_t); - void _Increase_stack_usage_count(); - void _Decrease_stack_usage_count(); + size_t _Calculate_frames_limit(); void _Increase_complexity_count(); void _Prepare_rep(_Node_rep*); @@ -1847,7 +1846,6 @@ private: const _RxTraits& _Traits; bool _Full; long _Max_complexity_count; - long _Max_stack_count; typename _RxTraits::char_class_type _Char_class_w{}; typename _RxTraits::char_class_type _Char_class_s{}; typename _RxTraits::char_class_type _Char_class_d{}; @@ -3389,6 +3387,10 @@ void _Builder2<_FwdIt, _Elem, _RxTraits>::_Tidy() noexcept { // free memory template size_t _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Push_frame(_Rx_unwind_ops _Code, _Node_base* _Node) { if (_Frames_count >= _Frames.size()) { + if (_Frames_count >= _Frames_limit) { + _Xregex_error(regex_constants::error_stack); + } + _Frames.push_back({_Code, {0}, _Node, _Tgt_state._Cur, size_t{}}); } else { auto& _Frame = _Frames[_Frames_count]; @@ -3406,21 +3408,17 @@ void _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Pop_frame(size_t _Idx) { } template -void _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Increase_stack_usage_count() { - if (0 < _Max_stack_count && --_Max_stack_count <= 0) { - _Xregex_error(regex_constants::error_stack); - } +size_t _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Calculate_frames_limit() { + constexpr size_t _Fixed_part = 10000000U / sizeof(_Rx_state_frame_t<_It>); + constexpr size_t _Divisor = sizeof(_Rx_state_frame_t<_It>) / 10U; + const auto _Variable_part = _Input_length / static_cast<_Iter_diff_t<_It>>(_Divisor); + const size_t _Max_frames_size = _Frames.max_size(); - if (0 < _Max_complexity_count && --_Max_complexity_count <= 0) { - _Xregex_error(regex_constants::error_complexity); + if (PTRDIFF_MAX < _Variable_part) { + return _Max_frames_size; } -} -template -void _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Decrease_stack_usage_count() { - if (0 < _Max_stack_count) { - ++_Max_stack_count; - } + return (_STD min) (_Max_frames_size, static_cast(_Variable_part) + _Fixed_part); } template @@ -3447,9 +3445,6 @@ void _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Prepare_rep(_Node_rep* _ template bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Find_first_inner_capture_group( _Node_base* _Nx, _Loop_vals_v3_t<_Iter_diff_t<_It>>* _Loop_state) { - if (0 < _Max_stack_count && --_Max_stack_count <= 0) { - _Xregex_error(regex_constants::error_stack); - } bool _Found_group = false; while (_Nx) { @@ -3529,10 +3524,6 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Find_first_inner_capture } } - if (0 < _Max_stack_count) { - ++_Max_stack_count; - } - return _Found_group; } @@ -3841,7 +3832,7 @@ bool _Is_ecmascript_line_terminator(_Elem _Ch) { template bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _Nx) { // check for match - _Increase_stack_usage_count(); + _Increase_complexity_count(); bool _Failed = false; @@ -3934,7 +3925,7 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N _Push_frame(_Rx_unwind_ops::_After_assert, _Node); _Next = _Node->_Child; - _Increase_stack_usage_count(); + _Increase_complexity_count(); break; } @@ -3944,7 +3935,7 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N _Push_frame(_Rx_unwind_ops::_After_neg_assert, _Node); _Next = _Node->_Child; - _Increase_stack_usage_count(); + _Increase_complexity_count(); break; } @@ -3958,7 +3949,6 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N const auto& _Frame = _Frames[_Frames_count]; const auto _Code = _Frame._Code; if (_Code == _Rx_unwind_ops::_After_assert || _Code == _Rx_unwind_ops::_After_neg_assert) { - _Decrease_stack_usage_count(); if (_Code == _Rx_unwind_ops::_After_assert) { _Tgt_state._Cur = _Frame._Pos; _Next = _Frame._Node->_Next; @@ -3982,12 +3972,6 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N } } break; - } else if (_Code == _Rx_unwind_ops::_Disjunction_eval_alt_on_failure - || _Code == _Rx_unwind_ops::_Disjunction_eval_alt_always - || _Code == _Rx_unwind_ops::_Loop_greedy // - || _Code == _Rx_unwind_ops::_Loop_nongreedy - || _Code == _Rx_unwind_ops::_Loop_restore_vals) { - _Decrease_stack_usage_count(); } else if (_Code == _Rx_unwind_ops::_Capture_restore_unmatched_end) { auto _Node = static_cast<_Node_capture*>(_Frame._Node); auto _Capture_idx = _Node->_Idx; @@ -4072,7 +4056,7 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N _Push_frame(_Longest ? _Rx_unwind_ops::_Disjunction_eval_alt_always : _Rx_unwind_ops::_Disjunction_eval_alt_on_failure, _Node->_Child); - _Increase_stack_usage_count(); + _Increase_complexity_count(); } break; } @@ -4119,10 +4103,11 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N _Frame._Loop_frame_idx_sav = _Sav._Loop_frame_idx; _Sav._Loop_idx = 1; _Sav._Loop_frame_idx = _Frame_idx; - _Increase_stack_usage_count(); + _Increase_complexity_count(); // _Next is already assigned correctly for matching a rep } else { // try tail first _Next = _Node->_End_rep->_Next; + _Increase_complexity_count(); // set up stack unwinding for non-greedy matching if at least one rep is allowed if (_Node->_Max != 0) { auto _Frame_idx = _Push_frame(_Rx_unwind_ops::_Loop_nongreedy, _Node); @@ -4131,9 +4116,6 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N _Frame._Loop_frame_idx_sav = _Sav._Loop_frame_idx; _Sav._Loop_idx = 0; _Sav._Loop_frame_idx = _Frame_idx; - _Increase_stack_usage_count(); - } else { - _Increase_complexity_count(); } } } @@ -4239,7 +4221,7 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N _Reset_capture_groups(_Sav._Group_first); _Next = _Nr->_Next; - _Increase_stack_usage_count(); + _Increase_complexity_count(); } else if (!_Progress) { // latest rep match empty // An empty match is allowed if it is needed to reach the minimum number of reps. // Moreover, POSIX allows an empty repetition if the subexpression is matched only once. @@ -4264,18 +4246,16 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N _Reset_capture_groups(_Sav._Group_first); _Next = _Nr->_Next; - _Increase_stack_usage_count(); + _Increase_complexity_count(); } else { // non-greedy matching or greedy matching with maximum reached // set up stack unwinding for non-greedy matching if one more rep is allowed + _Increase_complexity_count(); if (_Sav._Loop_idx != _Nr->_Max) { auto _Frame_idx = _Push_frame(_Rx_unwind_ops::_Loop_nongreedy, _Nr); auto& _Frame = _Frames[_Frame_idx]; _Frame._Loop_idx_sav = _Sav._Loop_idx; _Frame._Loop_frame_idx_sav = _Sav._Loop_frame_idx; _Sav._Loop_frame_idx = _Frame_idx; - _Increase_stack_usage_count(); - } else { - _Increase_complexity_count(); } // _Next is already assigned correctly for matching tail } @@ -4322,14 +4302,12 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N case _Rx_unwind_ops::_After_assert: { // matching pattern of positive assert failed _STL_INTERNAL_CHECK(_Failed); - _Decrease_stack_usage_count(); break; } case _Rx_unwind_ops::_After_neg_assert: { // matching pattern of negative assert failed _STL_INTERNAL_CHECK(_Failed); - _Decrease_stack_usage_count(); _Tgt_state._Cur = _Frame._Pos; _Nx = _Frame._Node->_Next; _Failed = false; @@ -4339,7 +4317,6 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N case _Rx_unwind_ops::_Disjunction_eval_alt_on_failure: // evaluate next alternative if matching prior alternatives failed if (!_Failed) { - _Decrease_stack_usage_count(); break; } _FALLTHROUGH; @@ -4355,8 +4332,6 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N if (_Node->_Child) { _Frame._Node = _Node->_Child; ++_Frames_count; - } else { - _Decrease_stack_usage_count(); } break; } @@ -4455,8 +4430,6 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N _Sav._Loop_idx = _Frame._Loop_idx_sav; _Sav._Loop_frame_idx = _Frame._Loop_frame_idx_sav; - - _Decrease_stack_usage_count(); } break; @@ -4513,7 +4486,6 @@ bool _Matcher3<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Match_pat(_Node_base* _N } } - _Decrease_stack_usage_count(); _STL_INTERNAL_CHECK(_Frames_count == 0); return !_Failed; diff --git a/tests/std/tests/VSO_0000000_regex_use/test.cpp b/tests/std/tests/VSO_0000000_regex_use/test.cpp index d97178a6bc5..7b9ee69b694 100644 --- a/tests/std/tests/VSO_0000000_regex_use/test.cpp +++ b/tests/std/tests/VSO_0000000_regex_use/test.cpp @@ -902,7 +902,8 @@ void test_gh_993() { void test_gh_997() { // GH-997: : Grouping within repetition causes regex stack error // GH-1528: : regex_match gets caught in recursive loop until stack overflow occurs - g_regexTester.should_match(string(1025, 'a'), "(?:a)+"); + g_regexTester.should_match(string(2000, 'a'), "(?:a)+"); + g_regexTester.should_match(string(2000, 'a'), "(?:a|bc)+"); { test_wregex rgx(&g_regexTester, LR"(^http[s]?://([^.]+\.)*example\.com/.*$)", icase);