Skip to content

Commit f69d5c6

Browse files
pgansslewarsaw
authored andcommitted
Fix infinite loop in email folding logic (GH-12732)
As far as I can tell, this infinite loop would be triggered if: 1. The value being folded contains a single word (no spaces) longer than max_line_length 2. The max_line_length is shorter than the encoding's name + 9 characters. bpo-36564: https://bugs.python.org/issue36564
1 parent 1d8b04e commit f69d5c6

File tree

4 files changed

+35
-6
lines changed

4 files changed

+35
-6
lines changed

Lib/email/_header_value_parser.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2846,15 +2846,22 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset):
28462846
trailing_wsp = to_encode[-1]
28472847
to_encode = to_encode[:-1]
28482848
new_last_ew = len(lines[-1]) if last_ew is None else last_ew
2849+
2850+
encode_as = 'utf-8' if charset == 'us-ascii' else charset
2851+
2852+
# The RFC2047 chrome takes up 7 characters plus the length
2853+
# of the charset name.
2854+
chrome_len = len(encode_as) + 7
2855+
2856+
if (chrome_len + 1) >= maxlen:
2857+
raise errors.HeaderParseError(
2858+
"max_line_length is too small to fit an encoded word")
2859+
28492860
while to_encode:
28502861
remaining_space = maxlen - len(lines[-1])
2851-
# The RFC2047 chrome takes up 7 characters plus the length
2852-
# of the charset name.
2853-
encode_as = 'utf-8' if charset == 'us-ascii' else charset
2854-
text_space = remaining_space - len(encode_as) - 7
2862+
text_space = remaining_space - chrome_len
28552863
if text_space <= 0:
28562864
lines.append(' ')
2857-
# XXX We'll get an infinite loop here if maxlen is <= 7
28582865
continue
28592866

28602867
to_encode_word = to_encode[:text_space]

Lib/email/parser.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from email._policybase import compat32
1414

1515

16-
1716
class Parser:
1817
def __init__(self, _class=None, *, policy=compat32):
1918
"""Parser of RFC 2822 and MIME email messages.

Lib/test/test_email/test_policy.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import types
33
import textwrap
44
import unittest
5+
import email.errors
56
import email.policy
67
import email.parser
78
import email.generator
@@ -257,6 +258,25 @@ def test_non_ascii_chars_do_not_cause_inf_loop(self):
257258
'Subject: \n' +
258259
12 * ' =?utf-8?q?=C4=85?=\n')
259260

261+
def test_short_maxlen_error(self):
262+
# RFC 2047 chrome takes up 7 characters, plus the length of the charset
263+
# name, so folding should fail if maxlen is lower than the minimum
264+
# required length for a line.
265+
266+
# Note: This is only triggered when there is a single word longer than
267+
# max_line_length, hence the 1234567890 at the end of this whimsical
268+
# subject. This is because when we encounter a word longer than
269+
# max_line_length, it is broken down into encoded words to fit
270+
# max_line_length. If the max_line_length isn't large enough to even
271+
# contain the RFC 2047 chrome (`?=<charset>?q??=`), we fail.
272+
subject = "Melt away the pounds with this one simple trick! 1234567890"
273+
274+
for maxlen in [3, 7, 9]:
275+
with self.subTest(maxlen=maxlen):
276+
policy = email.policy.default.clone(max_line_length=maxlen)
277+
with self.assertRaises(email.errors.HeaderParseError):
278+
policy.fold("Subject", subject)
279+
260280
# XXX: Need subclassing tests.
261281
# For adding subclassed objects, make sure the usual rules apply (subclass
262282
# wins), but that the order still works (right overrides left).
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix infinite loop in email header folding logic that would be triggered when
2+
an email policy's max_line_length is not long enough to include the required
3+
markup and any values in the message. Patch by Paul Ganssle

0 commit comments

Comments
 (0)