https://bugs.exim.org/show_bug.cgi?id=3155
Bug ID: 3155
Summary: memory corruption in internal_transport_write_message
& transport_headers_send
Product: Exim
Version: 4.98.2
Hardware: x86-64
OS: Linux
Status: NEW
Severity: bug
Priority: medium
Component: Transports
Assignee: [email protected]
Reporter: [email protected]
CC: [email protected]
Created attachment 1512
--> https://bugs.exim.org/attachment.cgi?id=1512&action=edit
script to reproduce the bug(s)
Hi Exim developers,
I discovered a possible memory corruption in the functions
internal_transport_write_message() and transport_headers_send() in
transports.c.
In both cases, the function write_chunk() is called in between store_mark() and
corresponding store_reset() calls. In transport_headers_send() this call is a
bit hidden as the function pointer sendfn() is called and write_chunk() is
assigned to this pointer.
write_chunk() does call transport_write_block() in case the header buffer needs
flushing. transport_write_block() will allocate tctx->u.msg if not yet
allocated.
When store_reset() is called afterwards, the memory u.msg is pointing to can
get overwritten (or even freed?) anytime later although u.msg is still being
used.
I have prepared a small script that demonstrates the behaviour:
exim-mem-corruption.py (see attachment)
To reproduce the bug you must run the script on a machine with exim listening
on localhost:25. Use a suitable sender address (first param) that is accepted
by your mail setup.
For the bug in transport_headers_send() you must use a recipient address
(second param) that is routed through a transport with headers_rewrite to
rewrite the From: header. The rewrite does not need to actually change the
header, a rewrite to the same value is enough to trigger the bug.
To trigger the bug in internal_transport_write_message(), you must have a
transport, that has envelope_to_add enabled. You then have to call the script
with so many recipients, that the char length exceeds DELIVER_OUT_BUFFER_SIZE
(default: 8192).
I did not get a SIGSEGV in my tests, but the resulting mail contained corrupted
data including non-ASCII control sequences and overwritten header names (e.g.
Subject).
I also developed a small patch to workaround the headers_rewrite bug:
--- exim-4.98.2.orig/src/transport.c
+++ exim-4.98.2/src/transport.c
@@ -770,10 +770,27 @@ for (header_line * h = header_list; h; h
if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules,
tblock->rewrite_existflags, FALSE)))
{
+ uschar * saved_hh_text;
+ BOOL result;
len = hh->slen;
if (tctx->options & topt_truncate_headers && len > 998) len = 998;
- if (!sendfn(tctx, hh->text, len)) return FALSE;
+ /* save hh->text in newly allocated memory to allow invalidating
+ current storage location with store_reset() BEFORE calling
+ sendfn() which might allocate memory from store that MUST NOT
+ be reused after store_reset() */
+ saved_hh_text = malloc(len);
+ if (!saved_hh_text) {
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to malloc %d bytes of "
+ "memory in line %d in %s", len, __LINE__, __FUNCTION__);
+ return FALSE;
+ }
+ memcpy(saved_hh_text, hh->text, len);
store_reset(reset_point);
+ /* save the result of sendfn() to free saved_hh_text
+ before returning on failure */
+ result = sendfn(tctx, saved_hh_text, len);
+ free(saved_hh_text);
+ if (!result) return FALSE;
continue; /* With the next header line */
}
}
This patch is obviously just a workaround as it introduces a malloc() call
outside of store.c! I'm not familiar enough with exim's store management to
provide a better solution.
For the envelope_to_add bug I did not search a workaround yet as we do not use
this option (GDPR), Also a similar approach like above is not possible here
without changes in write_env_to() as well.
After using the workaround, I discovered ANOTHER BUG! It is triggered, when
such a mail is sent through an smtp transport with chunking enabled, which is
the default. I assume the headers must be larger than DELIVER_OUT_BUFFER_SIZE
but I did not have time yet to debug this in more detail. As a workaround I
temporarily disabled chunking to send out the problematic mail in my production
system.
If you need a real-world example for a mail with large headers, the German mail
provider GMX is including a header "UI-OutboundReport". This header seems to
include a very long base64-like encoded data, the length seems to be depending
on the size of the whole mail. In my case, this header alone had >4kB due to a
2MB PDF attachment. The From: header was close to the end because mailman
removed the original header and added the new one at the end. All together,
this triggered the bug in transport_headers_send() as I have set
headers_rewrite to fix the From: header for SRS compliant forwards of bounces.
If needed, I can provide an anonymized version of that mail, but only
non-public, please.
--
You are receiving this mail because:
You are on the CC list for the bug.
--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-dev.lists.exim.org/
## unsubscribe (doesn't require an account):
## [email protected]
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/