Re: Panic: file charset-iconv.c: line 83 (charset_to_utf8_try): assertion failed: (srcleft <= CHARSET_MAX_PENDING_BUF_SIZE)

John Fawcett via dovecot <[email protected]> Tue, 12 May 2026 04:03:47 +0200
Newsgroups gmane.mail.imap.dovecot
Message-ID <[email protected]>
On 09/05/2026 00:27, Alex Rosenberg via dovecot wrote:
> Attached is an LLM-reduced reproduction of the crash in the title. My particular setup is dovecot 2.3.21.1 (d492236fa0) in a FreeBSD jail (13.5). I realize that this is an older release but there is no FreeBSD port/pkg for dovecot 2.4.x yet.
>
> The message in the attachment is reduced from an old (2017!) email to one of the LLVM compiler mailing lists. The original malformed email had this header:
>    X-Mailer: Evolution 3.22.5 (3.22.5-1.fc25)
>
> The bug occurs when dovecot's FTS indexer processes a MIME part that:
>    1. Declares charset="UTF-7"
>    2. Contains base64-encoded content that, when decoded, has bare '+' characters
>    3. Causes UTF-7 decoder buffer overflow in charset-iconv.c:83
>
> The base64 content decodes to C source code with expressions like:
>    - argc + 4
>    - state++
>    - state--
>
> These '+' characters in UTF-7 context cause the decoder's pending buffer to exceed CHARSET_MAX_PENDING_BUF_SIZE, triggering the assertion failure.
>
> Alex
>
> _______________________________________________
> dovecot mailing list -- [email protected]
> To unsubscribe send an email to [email protected]

Hi Alex

looks like the same code is present also in 2.4 so I guess this is not 
just an issue with older versions of Dovecot.

Basically there is an assumed limit of 10 for the maximum number of 
unprocessed bytes that iconv library function can return when it gives 
an EINVAL error. Logically this value could be larger, the upper bound 
for unprocessed bytes could even be the number of input bytes. In 
practice it seems it hasn't been an issue so far to have an upper bound 
of 10.

The Dovecot header file charset-utf8.h itself reports

/* Max number of bytes that iconv can require for a single character.
    UTF-8 takes max 6 bytes per character. Not sure about others, but 
I'd think
    10 is more than enough for everyone.. */
#define CHARSET_MAX_PENDING_BUF_SIZE 10

It wasn't 100% clear to me why this limit needs to be imposed in 
charset_to_utf8_try (and also in iconv_charset_to_utf8) but I am 
guessing that it is there for a reason, to prevent buffer overflows 
elsewhere in the calling code. It would be interesting to know what 
value was returned in your case, by adding some debugging code just 
before the assert:

if (inleft > CHARSET_MAX_PENDING_BUF_SIZE) {
     i_warning("charset-iconv: pending buffer size %zu exceeds limit %d",
               inleft, CHARSET_MAX_PENDING_BUF_SIZE);

Ultimately, the simplest and safest fix, without refactoring calliing 
code, may just boil down to selecting a better limit, e.g.

#define CHARSET_MAX_PENDING_BUF_SIZE 20

John


_______________________________________________
dovecot mailing list -- [email protected]
To unsubscribe send an email to [email protected]