summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2019-07-21 07:01:43 -0700
committerNed Deily <nad@python.org>2019-07-21 16:01:43 +0200
commit79a47e2b9cff6c9facdbc022a752177ab89dc533 (patch)
tree7e48c830858be3e01d5f6180a378b133e8e0d030
parent317c33e67cb6076c5a87a66c75e8c35ac581398d (diff)
downloadcpython-git-79a47e2b9cff6c9facdbc022a752177ab89dc533.tar.gz
Fix infinite loop in email folding logic (GH-12732) (GH-14799)
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 (cherry picked from commit f69d5c61981ea97d251db515c7ff280fcc17182d) Co-authored-by: Paul Ganssle <pganssle@users.noreply.github.com>
-rw-r--r--Lib/email/_header_value_parser.py17
-rw-r--r--Lib/email/parser.py1
-rw-r--r--Lib/test/test_email/test_policy.py20
-rw-r--r--Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst3
4 files changed, 35 insertions, 6 deletions
diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py
index f42cde203c..e3c343dffb 100644
--- a/Lib/email/_header_value_parser.py
+++ b/Lib/email/_header_value_parser.py
@@ -2715,15 +2715,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")
+
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]
diff --git a/Lib/email/parser.py b/Lib/email/parser.py
index 555b172560..7db4da1ff0 100644
--- a/Lib/email/parser.py
+++ b/Lib/email/parser.py
@@ -13,7 +13,6 @@ from email.feedparser import FeedParser, BytesFeedParser
from email._policybase import compat32
-
class Parser:
def __init__(self, _class=None, *, policy=compat32):
"""Parser of RFC 2822 and MIME email messages.
diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py
index c2c437e6ac..6999e4af10 100644
--- a/Lib/test/test_email/test_policy.py
+++ b/Lib/test/test_email/test_policy.py
@@ -2,6 +2,7 @@ import io
import types
import textwrap
import unittest
+import email.errors
import email.policy
import email.parser
import email.generator
@@ -245,6 +246,25 @@ class PolicyAPITests(unittest.TestCase):
'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).
diff --git a/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst b/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst
new file mode 100644
index 0000000000..ddd17aec1d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst
@@ -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