# search command can be quite large, so we now use 1M.
_MAXLINE = 1000000
+# Data larger than this will be read in chunks, to prevent extreme
+# overallocation.
+_SAFE_BUF_SIZE = 1 << 20
# Commands
def read(self, size):
"""Read 'size' bytes from remote."""
- return self.file.read(size)
+ cursize = min(size, _SAFE_BUF_SIZE)
+ data = self.file.read(cursize)
+ while cursize < size and len(data) == cursize:
+ delta = min(cursize, size - cursize)
+ data += self.file.read(delta)
+ cursize += delta
+ return data
def readline(self):
self.assertRaises(imaplib.IMAP4.error,
self.imap_class, *server.server_address)
+ def test_truncated_large_literal(self):
+ size = 0
+ class BadHandler(SimpleIMAPHandler):
+ def handle(self):
+ self._send_textline('* OK {%d}' % size)
+ self._send_textline('IMAP4rev1')
+
+ for exponent in range(15, 64):
+ size = 1 << exponent
+ with self.subTest(f"size=2e{size}"):
+ with self.reaped_server(BadHandler) as server:
+ with self.assertRaises(imaplib.IMAP4.abort):
+ self.imap_class(*server.server_address)
+
@threading_helper.reap_threads
def test_simple_with_statement(self):
# simplest call
--- /dev/null
+Fix a potential denial of service in the :mod:`imaplib` module. When connecting
+to a malicious server, it could cause an arbitrary amount of memory to be
+allocated. On many systems this is harmless as unused virtual memory is only a
+mapping, but if this hit a virtual address size limit it could lead to a
+:exc:`MemoryError` or other process crash. On unusual systems or builds where
+all allocated memory is touched and backed by actual ram or storage it could've
+consumed resources doing so until similarly crashing.