Skip to content

Commit 44bb21e

Browse files
encukoujoshuaswanson
authored andcommitted
[3.12] gh-146333: Fix quadratic regex backtracking in configparser option parsing (GH-146399) (GH-148559) (GH-154081)
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) (cherry picked from commit a5969e8) (cherry picked from commit 5755830) Co-authored-by: Petr Viktorin <encukou@gmail.com> Co-authored-by: Joshua Swanson <22283299+joshuaswanson@users.noreply.github.com>
1 parent d2b2f5e commit 44bb21e

3 files changed

Lines changed: 29 additions & 2 deletions

File tree

Lib/configparser.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -577,15 +577,19 @@ class RawConfigParser(MutableMapping):
577577
\] # ]
578578
"""
579579
_OPT_TMPL = r"""
580-
(?P<option>.*?) # very permissive!
580+
(?P<option> # very permissive!
581+
(?:(?!{delim})\S)* # non-delimiter non-whitespace
582+
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
581583
\s*(?P<vi>{delim})\s* # any number of space/tab,
582584
# followed by any of the
583585
# allowed delimiters,
584586
# followed by any space/tab
585587
(?P<value>.*)$ # everything up to eol
586588
"""
587589
_OPT_NV_TMPL = r"""
588-
(?P<option>.*?) # very permissive!
590+
(?P<option> # very permissive!
591+
(?:(?!{delim})\S)* # non-delimiter non-whitespace
592+
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
589593
\s*(?: # any number of space/tab,
590594
(?P<vi>{delim})\s* # optionally followed by
591595
# any of the allowed

Lib/test/test_configparser.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2166,6 +2166,26 @@ def test_instance_assignment(self):
21662166
self.assertEqual(cfg['two'].getlen('one'), 5)
21672167

21682168

2169+
class ReDoSTestCase(unittest.TestCase):
2170+
"""Regression tests for quadratic regex backtracking (gh-146333)."""
2171+
2172+
def test_option_regex_does_not_backtrack(self):
2173+
# A line with many spaces between non-delimiter characters
2174+
# should be parsed in linear time, not quadratic.
2175+
parser = configparser.RawConfigParser()
2176+
content = "[section]\n" + "x" + " " * 40000 + "y" + "\n"
2177+
# This should complete almost instantly. Before the fix,
2178+
# it would take over a minute due to catastrophic backtracking.
2179+
with self.assertRaises(configparser.ParsingError):
2180+
parser.read_string(content)
2181+
2182+
def test_option_regex_no_value_does_not_backtrack(self):
2183+
parser = configparser.RawConfigParser(allow_no_value=True)
2184+
content = "[section]\n" + "x" + " " * 40000 + "y" + "\n"
2185+
parser.read_string(content)
2186+
self.assertTrue(parser.has_option("section", "x" + " " * 40000 + "y"))
2187+
2188+
21692189
class MiscTestCase(unittest.TestCase):
21702190
def test__all__(self):
21712191
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)