mirror of
https://github.com/python/cpython.git
synced 2026-05-06 04:37:33 -04:00
[3.13] gh-146333: Fix quadratic regex backtracking in configparser option parsing (GH-146399) (GH-148559)
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 7e0a0be409)
Co-authored-by: Joshua Swanson <22283299+joshuaswanson@users.noreply.github.com>
This commit is contained in:
+6
-2
@@ -600,7 +600,9 @@ class RawConfigParser(MutableMapping):
|
||||
\] # ]
|
||||
"""
|
||||
_OPT_TMPL = r"""
|
||||
(?P<option>.*?) # very permissive!
|
||||
(?P<option> # very permissive!
|
||||
(?:(?!{delim})\S)* # non-delimiter non-whitespace
|
||||
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
|
||||
\s*(?P<vi>{delim})\s* # any number of space/tab,
|
||||
# followed by any of the
|
||||
# allowed delimiters,
|
||||
@@ -608,7 +610,9 @@ class RawConfigParser(MutableMapping):
|
||||
(?P<value>.*)$ # everything up to eol
|
||||
"""
|
||||
_OPT_NV_TMPL = r"""
|
||||
(?P<option>.*?) # very permissive!
|
||||
(?P<option> # very permissive!
|
||||
(?:(?!{delim})\S)* # non-delimiter non-whitespace
|
||||
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
|
||||
\s*(?: # any number of space/tab,
|
||||
(?P<vi>{delim})\s* # optionally followed by
|
||||
# any of the allowed
|
||||
|
||||
@@ -2225,6 +2225,26 @@ class SectionlessTestCase(unittest.TestCase):
|
||||
self.assertEqual('2', cfg[configparser.UNNAMED_SECTION]['b'])
|
||||
|
||||
|
||||
class ReDoSTestCase(unittest.TestCase):
|
||||
"""Regression tests for quadratic regex backtracking (gh-146333)."""
|
||||
|
||||
def test_option_regex_does_not_backtrack(self):
|
||||
# A line with many spaces between non-delimiter characters
|
||||
# should be parsed in linear time, not quadratic.
|
||||
parser = configparser.RawConfigParser()
|
||||
content = "[section]\n" + "x" + " " * 40000 + "y" + "\n"
|
||||
# This should complete almost instantly. Before the fix,
|
||||
# it would take over a minute due to catastrophic backtracking.
|
||||
with self.assertRaises(configparser.ParsingError):
|
||||
parser.read_string(content)
|
||||
|
||||
def test_option_regex_no_value_does_not_backtrack(self):
|
||||
parser = configparser.RawConfigParser(allow_no_value=True)
|
||||
content = "[section]\n" + "x" + " " * 40000 + "y" + "\n"
|
||||
parser.read_string(content)
|
||||
self.assertTrue(parser.has_option("section", "x" + " " * 40000 + "y"))
|
||||
|
||||
|
||||
class MiscTestCase(unittest.TestCase):
|
||||
def test__all__(self):
|
||||
support.check__all__(self, configparser, not_exported={"Error"})
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
Fix quadratic backtracking in :class:`configparser.RawConfigParser` option
|
||||
parsing regexes (``OPTCRE`` and ``OPTCRE_NV``). A crafted configuration line
|
||||
with many whitespace characters could cause excessive CPU usage.
|
||||
Reference in New Issue
Block a user