This is an automated email from the ASF dual-hosted git repository. chibenwa pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-mime4j.git
commit d4d13f467ffacc323c1c8af9e7b9fc92c2a71055 Author: Benoit TELLIER <[email protected]> AuthorDate: Wed Nov 4 14:53:28 2026 +0100 [PERF] DecoderUtil: stop at the first "=?" that cannot complete an encoded word When the search for the charset '?', the encoding '?' or the closing "?=" failed, decodeEncodedWords advanced by two characters to the next "=?" and searched to the end of the value again: O(k*n) for k "=?" with no "?=", in Subject, display names and parameter values. The three searches only move forward with the start of the pattern, so once one fails no later "=?" can complete an encoded word. Copy the rest of the value and stop. The output is unchanged. Found by twake-fuzz, scenario 33-header-value-cost-scaling. --- .../java/org/apache/james/mime4j/codec/DecoderUtil.java | 8 +++++--- .../org/apache/james/mime4j/codec/DecoderUtilTest.java | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/apache/james/mime4j/codec/DecoderUtil.java b/core/src/main/java/org/apache/james/mime4j/codec/DecoderUtil.java index 7f94540a..feffa60e 100644 --- a/core/src/main/java/org/apache/james/mime4j/codec/DecoderUtil.java +++ b/core/src/main/java/org/apache/james/mime4j/codec/DecoderUtil.java @@ -238,10 +238,12 @@ public class DecoderUtil { int encodedTextEnd = body.indexOf("?=", encodingEnd + 1); if (charsetEnd < 0 || encodingEnd < 0 || encodedTextEnd < 0) { - // Invalid pattern + // Invalid pattern. The three searches only move forward with startPattern, so a later + // "=?" cannot complete an encoded word either: copy the rest instead of searching again + // from each "=?", which is quadratic. appendStringBuffer(fallback, charsetOverrides, out, sb, mimeCharset); - sb.append(body, position, startPattern + 2); - position = startPattern + 2; + sb.append(body, position, body.length()); + break; } else if (encodingEnd == encodedTextEnd) { appendStringBuffer(fallback, charsetOverrides, out, sb, mimeCharset); sb.append(body, position, Math.min(encodedTextEnd + 2, body.length())); diff --git a/core/src/test/java/org/apache/james/mime4j/codec/DecoderUtilTest.java b/core/src/test/java/org/apache/james/mime4j/codec/DecoderUtilTest.java index 7e239cdd..c65574e0 100644 --- a/core/src/test/java/org/apache/james/mime4j/codec/DecoderUtilTest.java +++ b/core/src/test/java/org/apache/james/mime4j/codec/DecoderUtilTest.java @@ -193,4 +193,20 @@ public class DecoderUtilTest { public void strictMonitorShouldReportNonStandardSoftLineBreakAsIllegalArgument() { DecoderUtil.decodeEncodedWords("=?utf-8?Q?a=\tb?=", DecodeMonitor.STRICT); } + + @Test(timeout = 2000) + public void decodeEncodedWordsShouldBeLinearOnUnterminatedEncodedWords() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 200000; i++) { + sb.append("=?x"); + } + String value = sb.toString(); + Assert.assertEquals(value, DecoderUtil.decodeEncodedWords(value, DecodeMonitor.SILENT)); + } + + @Test + public void anUnterminatedEncodedWordShouldKeepTheWordsDecodedBeforeIt() { + Assert.assertEquals("ab =?x?q?c =?y", + DecoderUtil.decodeEncodedWords("=?utf-8?q?a?= =?utf-8?q?b?= =?x?q?c =?y", DecodeMonitor.SILENT)); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
