Skip to content

Commit c4fcd9c

Browse files
joshuaswansonencukou
authored andcommitted
[3.13] gh-146333: Fix quadratic regex backtracking in configparser option parsing (GH-146399)
Use negative lookahead in option regex to prevent backtracking, and to avoid changing logic outside the regexes (since people could use the regex directly). (cherry picked from commit 7e0a0be) Co-authored-by: Joshua Swanson <22283299+joshuaswanson@users.noreply.github.com>
1 parent 63c9aa6 commit c4fcd9c

File tree

3 files changed

+29
-2
lines changed

3 files changed

+29
-2
lines changed

Lib/configparser.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,15 +600,19 @@ class RawConfigParser(MutableMapping):
600600
\] # ]
601601
"""
602602
_OPT_TMPL = r"""
603-
(?P<option>.*?) # very permissive!
603+
(?P<option> # very permissive!
604+
(?:(?!{delim})\S)* # non-delimiter non-whitespace
605+
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
604606
\s*(?P<vi>{delim})\s* # any number of space/tab,
605607
# followed by any of the
606608
# allowed delimiters,
607609
# followed by any space/tab
608610
(?P<value>.*)$ # everything up to eol
609611
"""
610612
_OPT_NV_TMPL = r"""
611-
(?P<option>.*?) # very permissive!
613+
(?P<option> # very permissive!
614+
(?:(?!{delim})\S)* # non-delimiter non-whitespace
615+
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
612616
\s*(?: # any number of space/tab,
613617
(?P<vi>{delim})\s* # optionally followed by
614618
# any of the allowed

Lib/test/test_configparser.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2225,6 +2225,26 @@ def test_multiple_configs(self):
22252225
self.assertEqual('2', cfg[configparser.UNNAMED_SECTION]['b'])
22262226

22272227

2228+
class ReDoSTestCase(unittest.TestCase):
2229+
"""Regression tests for quadratic regex backtracking (gh-146333)."""
2230+
2231+
def test_option_regex_does_not_backtrack(self):
2232+
# A line with many spaces between non-delimiter characters
2233+
# should be parsed in linear time, not quadratic.
2234+
parser = configparser.RawConfigParser()
2235+
content = "[section]\n" + "x" + " " * 40000 + "y" + "\n"
2236+
# This should complete almost instantly. Before the fix,
2237+
# it would take over a minute due to catastrophic backtracking.
2238+
with self.assertRaises(configparser.ParsingError):
2239+
parser.read_string(content)
2240+
2241+
def test_option_regex_no_value_does_not_backtrack(self):
2242+
parser = configparser.RawConfigParser(allow_no_value=True)
2243+
content = "[section]\n" + "x" + " " * 40000 + "y" + "\n"
2244+
parser.read_string(content)
2245+
self.assertTrue(parser.has_option("section", "x" + " " * 40000 + "y"))
2246+
2247+
22282248
class MiscTestCase(unittest.TestCase):
22292249
def test__all__(self):
22302250
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)