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]

Reply via email to