Skip to content

Commit 1ec9465

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 cf23b91 commit 1ec9465

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
@@ -568,15 +568,19 @@ class RawConfigParser(MutableMapping):
568568
\] # ]
569569
"""
570570
_OPT_TMPL = r"""
571-
(?P<option>.*?) # very permissive!
571+
(?P<option> # very permissive!
572+
(?:(?!{delim})\S)* # non-delimiter non-whitespace
573+
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
572574
\s*(?P<vi>{delim})\s* # any number of space/tab,
573575
# followed by any of the
574576
# allowed delimiters,
575577
# followed by any space/tab
576578
(?P<value>.*)$ # everything up to eol
577579
"""
578580
_OPT_NV_TMPL = r"""
579-
(?P<option>.*?) # very permissive!
581+
(?P<option> # very permissive!
582+
(?:(?!{delim})\S)* # non-delimiter non-whitespace
583+
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
580584
\s*(?: # any number of space/tab,
581585
(?P<vi>{delim})\s* # optionally followed by
582586
# any of the allowed

Lib/test/test_configparser.py

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

21352135

2136+
class ReDoSTestCase(unittest.TestCase):
2137+
"""Regression tests for quadratic regex backtracking (gh-146333)."""
2138+
2139+
def test_option_regex_does_not_backtrack(self):
2140+
# A line with many spaces between non-delimiter characters
2141+
# should be parsed in linear time, not quadratic.
2142+
parser = configparser.RawConfigParser()
2143+
content = "[section]\n" + "x" + " " * 40000 + "y" + "\n"
2144+
# This should complete almost instantly. Before the fix,
2145+
# it would take over a minute due to catastrophic backtracking.
2146+
with self.assertRaises(configparser.ParsingError):
2147+
parser.read_string(content)
2148+
2149+
def test_option_regex_no_value_does_not_backtrack(self):
2150+
parser = configparser.RawConfigParser(allow_no_value=True)
2151+
content = "[section]\n" + "x" + " " * 40000 + "y" + "\n"
2152+
parser.read_string(content)
2153+
self.assertTrue(parser.has_option("section", "x" + " " * 40000 + "y"))
2154+
2155+
21362156
class MiscTestCase(unittest.TestCase):
21372157
def test__all__(self):
21382158
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)