Skip to content

Fix infinite loop and memory exhaustion when folding RFC 2047 params - #32

Merged
alecpl merged 2 commits into
pear:masterfrom
sebastka:fix/rfc2047-param-fold-infinite-loop
Jul 28, 2026
Merged

Fix infinite loop and memory exhaustion when folding RFC 2047 params#32
alecpl merged 2 commits into
pear:masterfrom
sebastka:fix/rfc2047-param-fold-infinite-loop

Conversation

@sebastka

@sebastka sebastka commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Hello,

I noticed a lot of these errors on our production Roundcube 1.7.2 instances...

PHP Warning: Undefined variable $_quote in vendor/pear/mail_mime/Mail/mimePart.php on line 808

... and eventually:

PHP Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 31457296 bytes) in vendor/pear/mail_mime/Mail/mimePart.php on line 808

AI use disclosure: Anthropic Claude Opus 4.8 helped me review and improve my fix for the quoted-printable branch and extended it to cover the base64 branch as well. It also generated the detailed comment below.


buildRFC2047Param() could loop forever and exhaust memory when an encoded parameter (e.g. a long or non-ASCII attachment filename) can't be folded within the wrap width, i.e. when the parameter name makes $add_len exceed $maxLength and the available $length goes negative.

  • Base64 branch: $real_len becomes <= 0, so $_quote is an empty string and $value is likewise never advanced.
  • Quoted-printable branch: the "{0,-N}" quantifier makes preg_match() fail, so $_quote is never set ("Undefined variable $_quote" warning on PHP 8) and substr($value, strlen($_quote)) never advances.

In both cases the while() condition stays true and $quoted grows until the PHP memory_limit is hit (fatal error): a single crafted header is enough to DoS the process.

Break out of both loops when nothing more can be folded; the trailing "$quoted .= $prefix . $value . $suffix" then emits the remainder as a single (over-long but valid) encoded word.


@till till left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test case?

@till
till requested review from alecpl and ashnazg July 25, 2026 15:24
@alecpl

alecpl commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

I confirm the issue and the fix. However, with this change the attachment headers (from the test) look like this:

Content-Transfer-Encoding: base64
Content-Type: application/pdf;
 charset=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
 name="=?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX?Q?=C3=A9=C3=A9=C3=A9=C3=A9=C3=A9=2Epdf?="
Content-Disposition: attachment;
 filename="=?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX?Q?=C3=A9=C3=A9=C3=A9=C3=A9=C3=A9=2Epdf?=";
 size=4

RFC2047 says: " An 'encoded-word' may not be more than 75 characters long, including 'charset', 'encoding', 'encoded-text', and delimiters. If it is desirable to encode more text than will fit in an 'encoded-word' of 75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may be used."

So, we're not RFC compliant when the charset is that long. I would consider truncating the charset to a sane length (something like 48 or 64 characters).

sebastka added 2 commits July 28, 2026 10:14
buildRFC2047Param() could loop until PHP ran out of memory when an
encoded parameter value could not be folded within the wrap width: the
quoted-printable branch left $_quote undefined and the base64 branch cut
a zero-length chunk, so the value was never advanced. A single crafted
header (e.g. an over-long charset name) was enough to trigger it.

Break out of both loops when no further chunk can be extracted, and cap
over-long charset names so every emitted encoded-word stays within the
RFC 2047 75-character limit.
@sebastka
sebastka force-pushed the fix/rfc2047-param-fold-infinite-loop branch from 23a4ea5 to e7646bb Compare July 28, 2026 08:15
@sebastka

Copy link
Copy Markdown
Contributor Author

@alecpl Encoded words are now capped to 75 characters.

@alecpl
alecpl merged commit 2001739 into pear:master Jul 28, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants