Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-36564: Fix infinite loop in email folding logic #12732

Merged
merged 1 commit into from Jul 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -2846,15 +2846,22 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset):
trailing_wsp = to_encode[-1]
to_encode = to_encode[:-1]
new_last_ew = len(lines[-1]) if last_ew is None else last_ew

encode_as = 'utf-8' if charset == 'us-ascii' else charset

# The RFC2047 chrome takes up 7 characters plus the length
# of the charset name.
chrome_len = len(encode_as) + 7

if (chrome_len + 1) >= maxlen:
raise errors.HeaderParseError(
"max_line_length is too small to fit an encoded word")
warsaw marked this conversation as resolved.
Show resolved Hide resolved

while to_encode:
remaining_space = maxlen - len(lines[-1])
# The RFC2047 chrome takes up 7 characters plus the length
# of the charset name.
encode_as = 'utf-8' if charset == 'us-ascii' else charset
text_space = remaining_space - len(encode_as) - 7
text_space = remaining_space - chrome_len
if text_space <= 0:
lines.append(' ')
# XXX We'll get an infinite loop here if maxlen is <= 7
continue

to_encode_word = to_encode[:text_space]
@@ -13,7 +13,6 @@
from email._policybase import compat32



class Parser:
def __init__(self, _class=None, *, policy=compat32):
"""Parser of RFC 2822 and MIME email messages.
@@ -2,6 +2,7 @@
import types
import textwrap
import unittest
import email.errors
import email.policy
import email.parser
import email.generator
@@ -257,6 +258,25 @@ def test_non_ascii_chars_do_not_cause_inf_loop(self):
'Subject: \n' +
12 * ' =?utf-8?q?=C4=85?=\n')

def test_short_maxlen_error(self):
# RFC 2047 chrome takes up 7 characters, plus the length of the charset
# name, so folding should fail if maxlen is lower than the minimum
# required length for a line.

# Note: This is only triggered when there is a single word longer than
# max_line_length, hence the 1234567890 at the end of this whimsical
# subject. This is because when we encounter a word longer than
# max_line_length, it is broken down into encoded words to fit
# max_line_length. If the max_line_length isn't large enough to even
# contain the RFC 2047 chrome (`?=<charset>?q??=`), we fail.
subject = "Melt away the pounds with this one simple trick! 1234567890"

for maxlen in [3, 7, 9]:
with self.subTest(maxlen=maxlen):
policy = email.policy.default.clone(max_line_length=maxlen)
with self.assertRaises(email.errors.HeaderParseError):
policy.fold("Subject", subject)

# XXX: Need subclassing tests.
# For adding subclassed objects, make sure the usual rules apply (subclass
# wins), but that the order still works (right overrides left).
@@ -0,0 +1,3 @@
Fix infinite loop in email header folding logic that would be triggered when
an email policy's max_line_length is not long enough to include the required
markup and any values in the message. Patch by Paul Ganssle