Skip to content

Commit 12c55b1

Browse files
committed
gh-146333: Fix quadratic regex backtracking in configparser option parsing
1 parent 119fce7 commit 12c55b1

File tree

3 files changed

+30
-6
lines changed

3 files changed

+30
-6
lines changed

Lib/configparser.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -614,17 +614,14 @@ class RawConfigParser(MutableMapping):
614614
"""
615615
_OPT_TMPL = r"""
616616
(?P<option>.*?) # very permissive!
617-
\s*(?P<vi>{delim})\s* # any number of space/tab,
618-
# followed by any of the
619-
# allowed delimiters,
617+
(?P<vi>{delim})\s* # any of the allowed delimiters,
620618
# followed by any space/tab
621619
(?P<value>.*)$ # everything up to eol
622620
"""
623621
_OPT_NV_TMPL = r"""
624622
(?P<option>.*?) # very permissive!
625-
\s*(?: # any number of space/tab,
626-
(?P<vi>{delim})\s* # optionally followed by
627-
# any of the allowed
623+
(?: # optionally:
624+
(?P<vi>{delim})\s* # any of the allowed
628625
# delimiters, followed by any
629626
# space/tab
630627
(?P<value>.*))?$ # everything up to eol

Lib/test/test_configparser.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2270,6 +2270,30 @@ def test_section_bracket_in_key(self):
22702270
output.close()
22712271

22722272

2273+
class ReDoSTestCase(unittest.TestCase):
2274+
"""Regression tests for quadratic regex backtracking (gh-146333)."""
2275+
2276+
def test_option_regex_does_not_backtrack(self):
2277+
# A line with many spaces between non-delimiter characters
2278+
# should be parsed in linear time, not quadratic.
2279+
parser = configparser.RawConfigParser()
2280+
content = "[section]
2281+
" + "x" + " " * 40000 + "y" + "
2282+
"
2283+
# This should complete almost instantly. Before the fix,
2284+
# it would take over a minute due to catastrophic backtracking.
2285+
with self.assertRaises(configparser.ParsingError):
2286+
parser.read_string(content)
2287+
2288+
def test_option_regex_no_value_does_not_backtrack(self):
2289+
parser = configparser.RawConfigParser(allow_no_value=True)
2290+
content = "[section]
2291+
" + "x" + " " * 40000 + "y" + "
2292+
"
2293+
parser.read_string(content)
2294+
self.assertTrue(parser.has_option("section", "x" + " " * 40000 + "y"))
2295+
2296+
22732297
class MiscTestCase(unittest.TestCase):
22742298
def test__all__(self):
22752299
support.check__all__(self, configparser, not_exported={"Error"})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix quadratic backtracking in :class:`configparser.RawConfigParser` option
2+
parsing regexes (``OPTCRE`` and ``OPTCRE_NV``). A crafted configuration line
3+
with many whitespace characters could cause excessive CPU usage.

0 commit comments

Comments
 (0)