Skip to content

Commit 6786f73

Browse files
committed
fix quadratic-complexity parsing in email.message._parseparam
1 parent c419af9 commit 6786f73

File tree

3 files changed

+26
-9
lines changed

3 files changed

+26
-9
lines changed

Lib/email/message.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,23 @@ def _parseparam(s):
7474
# RDM This might be a Header, so for now stringify it.
7575
s = ';' + str(s)
7676
plist = []
77-
while s[:1] == ';':
78-
s = s[1:]
79-
end = s.find(';')
80-
while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2:
77+
start = 0
78+
while s.find(';', start) == start:
79+
start += 1
80+
end = s.find(';', start)
81+
while end > 0 and (
82+
s.count('"', start, end) - s.count('\\"', start, end)
83+
) % 2:
8184
end = s.find(';', end + 1)
8285
if end < 0:
8386
end = len(s)
84-
f = s[:end]
85-
if '=' in f:
86-
i = f.index('=')
87-
f = f[:i].strip().lower() + '=' + f[i+1:].strip()
87+
i = s.find('=', start, end)
88+
if i == -1:
89+
f = s[start:end]
90+
else:
91+
f = s[start:i].rstrip().lower() + '=' + s[i+1:end].lstrip()
8892
plist.append(f.strip())
89-
s = s[end:]
93+
start = end
9094
return plist
9195

9296

Lib/test/test_email/test_email.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,17 @@ def test_get_param_with_quotes(self):
481481
"Content-Type: foo; bar*0=\"baz\\\"foobar\"; bar*1=\"\\\"baz\"")
482482
self.assertEqual(msg.get_param('bar'), 'baz"foobar"baz')
483483

484+
def test_get_param_linear_complexity(self):
485+
# Ensure that email.message._parseparam() is fast.
486+
# See https://github.com/python/cpython/issues/136063.
487+
N = 100_000
488+
res = email.message._parseparam(';' * N)
489+
self.assertEqual(res, [''] * N)
490+
res = email.message._parseparam('foo=bar;' * N)
491+
self.assertEqual(res, ['foo=bar'] * N)
492+
res = email.message._parseparam(' FOO = bar ;' * N)
493+
self.assertEqual(res, ['foo=bar'] * N)
494+
484495
def test_field_containment(self):
485496
msg = email.message_from_string('Header: exists')
486497
self.assertIn('header', msg)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`email.message`: ensure linear complexity for legacy HTTP parameters
2+
parsing. Patch by Bénédikt Tran.

0 commit comments

Comments
 (0)