From 749893763728479337038a95f9812f1ac8c3c998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller?= Date: Fri, 29 Nov 2024 19:21:43 +0100 Subject: [PATCH 1/6] ``: Always reject character ranges with set limits --- stl/inc/regex | 51 ++++++++++--------- tests/libcxx/expected_results.txt | 1 - .../std/tests/VSO_0000000_regex_use/test.cpp | 14 +++++ 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/stl/inc/regex b/stl/inc/regex index f5f8e75bf46..05dd934d583 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -4086,37 +4086,38 @@ void _Parser<_FwdIt, _Elem, _RxTraits>::_ClassRanges() { // check for valid clas return; } - if (_Ret != _Prs_set) { - if (_Val == 0 && !(_L_flags & _L_bzr_chr)) { - _Error(regex_constants::error_escape); - } - - if (_Mchar == _Meta_dash) { // check for valid range - _Next(); - _Elem _Chr1 = static_cast<_Elem>(_Val); - if ((_Ret = _ClassAtom()) == _Prs_none) { // treat - as ordinary character - _Nfa._Add_char_to_class(static_cast<_Elem>(_Val)); - _Nfa._Add_char_to_class(_Meta_dash); - return; - } + if (_Ret == _Prs_chr && _Val == 0 && !(_L_flags & _L_bzr_chr)) { + _Error(regex_constants::error_escape); + } - if (_Ret == _Prs_set) { - _Error(regex_constants::error_range); // set follows dash + if (_Mchar == _Meta_dash) { // check for valid range + _Next(); + _Elem _Chr1 = static_cast<_Elem>(_Val); + bool _Set_preceding = _Ret == _Prs_set; + if ((_Ret = _ClassAtom()) == _Prs_none) { // treat - as ordinary character + if (!_Set_preceding) { + _Nfa._Add_char_to_class(_Chr1); } + _Nfa._Add_char_to_class(_Meta_dash); + return; + } - if (_Flags & regex_constants::collate) { // translate ends of range - _Val = _Traits.translate(static_cast<_Elem>(_Val)); - _Chr1 = _Traits.translate(_Chr1); - } + if (_Set_preceding || _Ret == _Prs_set) { + _Error(regex_constants::error_range); // set precedes or follows dash + } - if (static_cast(_Val) < static_cast(_Chr1)) { - _Error(regex_constants::error_range); - } + if (_Flags & regex_constants::collate) { // translate ends of range + _Val = _Traits.translate(static_cast<_Elem>(_Val)); + _Chr1 = _Traits.translate(_Chr1); + } - _Nfa._Add_range(_Chr1, static_cast<_Elem>(_Val)); - } else { - _Nfa._Add_char_to_class(static_cast<_Elem>(_Val)); + if (static_cast(_Val) < static_cast(_Chr1)) { + _Error(regex_constants::error_range); } + + _Nfa._Add_range(_Chr1, static_cast<_Elem>(_Val)); + } else if (_Ret == _Prs_chr) { + _Nfa._Add_char_to_class(static_cast<_Elem>(_Val)); } } } diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 28d2fc987aa..17260deb1b5 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -558,7 +558,6 @@ std/re/re.alg/re.alg.search/no_update_pos.pass.cpp FAIL std/re/re.const/re.synopt/syntax_option_type.pass.cpp FAIL std/re/re.regex/re.regex.construct/bad_backref.pass.cpp FAIL std/re/re.regex/re.regex.construct/bad_escape.pass.cpp FAIL -std/re/re.regex/re.regex.construct/bad_range.pass.cpp FAIL std/re/re.regex/re.regex.construct/default.pass.cpp FAIL std/re/re.regex/re.regex.nonmemb/re.regex.nmswap/swap.pass.cpp FAIL std/re/re.regex/re.regex.swap/swap.pass.cpp FAIL diff --git a/tests/std/tests/VSO_0000000_regex_use/test.cpp b/tests/std/tests/VSO_0000000_regex_use/test.cpp index fcd27f0beba..6f7a13b2e7d 100644 --- a/tests/std/tests/VSO_0000000_regex_use/test.cpp +++ b/tests/std/tests/VSO_0000000_regex_use/test.cpp @@ -582,6 +582,19 @@ void test_gh_993() { } } +void test_gh_4995() { + // GH-4995: R"([\d-e])" should be rejected + g_regexTester.should_throw("[\\d-e]", error_range); + g_regexTester.should_throw("[e-\\d]", error_range); + g_regexTester.should_throw("[\\w-\\d]", error_range); + g_regexTester.should_throw("[[:digit:]-e]", error_range); + g_regexTester.should_throw("[e-[:digit:]]", error_range); + g_regexTester.should_throw("[[:alpha:]-[:digit:]]", error_range); + g_regexTester.should_throw("[[=a=]-e]", error_range, ECMAScript | regex::collate); + g_regexTester.should_throw("[e-[=a=]]", error_range, ECMAScript | regex::collate); + g_regexTester.should_throw("[[=a=]-[=b=]]", error_range, ECMAScript | regex::collate); +} + void test_gh_5058() { // GH-5058 ": Small cleanups" changed some default constructors to be defaulted. // Verify that types are still const-default-constructible (N4993 [dcl.init.general]/8). @@ -656,6 +669,7 @@ int main() { test_VSO_225160_match_eol_flag(); test_VSO_226914_word_boundaries(); test_gh_993(); + test_gh_4995(); test_gh_5058(); return g_regexTester.result(); From 22ede5383c543c4369ea725a8d1ea6a287249a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller?= Date: Fri, 29 Nov 2024 20:52:43 +0100 Subject: [PATCH 2/6] missing header changes --- tests/std/include/test_regex_support.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/std/include/test_regex_support.hpp b/tests/std/include/test_regex_support.hpp index fc11c74929b..7e6e020522f 100644 --- a/tests/std/include/test_regex_support.hpp +++ b/tests/std/include/test_regex_support.hpp @@ -160,9 +160,10 @@ class regex_fixture { } } - void should_throw(const std::string& pattern, const std::regex_constants::error_type expectedCode) { + void should_throw(const std::string& pattern, const std::regex_constants::error_type expectedCode, + const std::regex_constants::syntax_option_type syntax = std::regex_constants::ECMAScript) { try { - const std::regex r(pattern); + const std::regex r(pattern, syntax); printf(R"(regex r("%s") succeeded (which is bad).)" "\n", pattern.c_str()); From da68543c5e90f9cd8d951e1e91fd0aa11d5c8035 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 2 Dec 2024 04:53:36 -0800 Subject: [PATCH 3/6] Use raw string literals. --- tests/std/tests/VSO_0000000_regex_use/test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/VSO_0000000_regex_use/test.cpp b/tests/std/tests/VSO_0000000_regex_use/test.cpp index 6f7a13b2e7d..1af7454adf7 100644 --- a/tests/std/tests/VSO_0000000_regex_use/test.cpp +++ b/tests/std/tests/VSO_0000000_regex_use/test.cpp @@ -584,9 +584,9 @@ void test_gh_993() { void test_gh_4995() { // GH-4995: R"([\d-e])" should be rejected - g_regexTester.should_throw("[\\d-e]", error_range); - g_regexTester.should_throw("[e-\\d]", error_range); - g_regexTester.should_throw("[\\w-\\d]", error_range); + g_regexTester.should_throw(R"([\d-e])", error_range); + g_regexTester.should_throw(R"([e-\d])", error_range); + g_regexTester.should_throw(R"([\w-\d])", error_range); g_regexTester.should_throw("[[:digit:]-e]", error_range); g_regexTester.should_throw("[e-[:digit:]]", error_range); g_regexTester.should_throw("[[:alpha:]-[:digit:]]", error_range); From c777bc6d2b5de2b284a057b633cf2cb9f8c58a5c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 2 Dec 2024 04:55:16 -0800 Subject: [PATCH 4/6] Add const to `_Set_preceding`. --- stl/inc/regex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/regex b/stl/inc/regex index 05dd934d583..0d487ec2ea4 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -4092,8 +4092,8 @@ void _Parser<_FwdIt, _Elem, _RxTraits>::_ClassRanges() { // check for valid clas if (_Mchar == _Meta_dash) { // check for valid range _Next(); - _Elem _Chr1 = static_cast<_Elem>(_Val); - bool _Set_preceding = _Ret == _Prs_set; + _Elem _Chr1 = static_cast<_Elem>(_Val); + const bool _Set_preceding = _Ret == _Prs_set; if ((_Ret = _ClassAtom()) == _Prs_none) { // treat - as ordinary character if (!_Set_preceding) { _Nfa._Add_char_to_class(_Chr1); From df40854230c460c31fa9153019fc253d9a499845 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 2 Dec 2024 05:09:21 -0800 Subject: [PATCH 5/6] Print `syntax` during test failure. --- tests/std/include/test_regex_support.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/std/include/test_regex_support.hpp b/tests/std/include/test_regex_support.hpp index 7e6e020522f..d45b77dec8a 100644 --- a/tests/std/include/test_regex_support.hpp +++ b/tests/std/include/test_regex_support.hpp @@ -164,15 +164,16 @@ class regex_fixture { const std::regex_constants::syntax_option_type syntax = std::regex_constants::ECMAScript) { try { const std::regex r(pattern, syntax); - printf(R"(regex r("%s") succeeded (which is bad).)" + printf(R"(regex r("%s", 0x%X) succeeded (which is bad).)" "\n", - pattern.c_str()); + pattern.c_str(), static_cast(syntax)); fail_regex(); } catch (const std::regex_error& e) { if (e.code() != expectedCode) { - printf(R"(regex r("%s") threw 0x%X; expected 0x%X)" + printf(R"(regex r("%s", 0x%X) threw 0x%X; expected 0x%X)" "\n", - pattern.c_str(), static_cast(e.code()), static_cast(expectedCode)); + pattern.c_str(), static_cast(syntax), static_cast(e.code()), + static_cast(expectedCode)); fail_regex(); } } From b0d22e303b4270466efb35cd969d130a5f3a4693 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 2 Dec 2024 06:48:38 -0800 Subject: [PATCH 6/6] Test valid cases. --- tests/std/tests/VSO_0000000_regex_use/test.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/std/tests/VSO_0000000_regex_use/test.cpp b/tests/std/tests/VSO_0000000_regex_use/test.cpp index 1af7454adf7..823190f9837 100644 --- a/tests/std/tests/VSO_0000000_regex_use/test.cpp +++ b/tests/std/tests/VSO_0000000_regex_use/test.cpp @@ -593,6 +593,23 @@ void test_gh_4995() { g_regexTester.should_throw("[[=a=]-e]", error_range, ECMAScript | regex::collate); g_regexTester.should_throw("[e-[=a=]]", error_range, ECMAScript | regex::collate); g_regexTester.should_throw("[[=a=]-[=b=]]", error_range, ECMAScript | regex::collate); + + // Test valid cases: + g_regexTester.should_not_match("b", R"([\d-])"); + g_regexTester.should_match("5", R"([\d-])"); + g_regexTester.should_match("-", R"([\d-])"); + + g_regexTester.should_not_match("b", R"([-\d])"); + g_regexTester.should_match("5", R"([-\d])"); + g_regexTester.should_match("-", R"([-\d])"); + + g_regexTester.should_match("b", R"([a-c\d])"); + g_regexTester.should_match("5", R"([a-c\d])"); + g_regexTester.should_not_match("-", R"([a-c\d])"); + + g_regexTester.should_match("b", R"([\da-c])"); + g_regexTester.should_match("5", R"([\da-c])"); + g_regexTester.should_not_match("-", R"([\da-c])"); } void test_gh_5058() {