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-jdkim.git
commit 01e841d8b96234228113328bec7a3b37e9f04094 Author: Benoit TELLIER <[email protected]> AuthorDate: Tue Nov 3 08:38:54 2026 +0100 [FIX] SignatureRecordImpl: do not overflow the stack on long i=, q= and z= The patterns checking i=, q= and z= repeated an alternation greedily, which java.util.regex matches recursively, one frame per character: a z= copy or a q= value of a few KB threw a StackOverflowError. Being an Error, it got past the RuntimeException handling of the verifier and out of verify(). The repetitions are now possessive, as in TagValue: their alternatives are disjoint, so no backtracking is lost. Co-Authored-By: Claude Opus 5.5 <[email protected]> --- .../james/jdkim/tagvalue/SignatureRecordImpl.java | 16 +++++++++------- .../org/apache/james/jdkim/SignatureRecordImplTest.java | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java index 64bfd1a..2d62e51 100644 --- a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java +++ b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java @@ -109,10 +109,11 @@ public class SignatureRecordImpl extends TagValue implements SignatureRecord { } // SPEC (RFC 5321): Dot-string = Atom *("." Atom) ; Quoted-string = DQUOTE *QcontentSMTP DQUOTE, - // with the non-ASCII characters RFC 6531 adds to atext and qtextSMTP - private static final String ATEXT = "[A-Za-z0-9!#$%&'*+/=?^_`{|}~\\u0080-\\uFFFF-]+"; + // with the non-ASCII characters RFC 6531 adds to atext and qtextSMTP. + // Possessive: a repeated group otherwise recurses once per character, overflowing the stack on long values. + private static final String ATEXT = "[A-Za-z0-9!#$%&'*+/=?^_`{|}~\\u0080-\\uFFFF-]++"; private static final Pattern LOCAL_PART = Pattern.compile( - "|" + ATEXT + "(?:\\." + ATEXT + ")*|\"(?:[\\x20\\x21\\x23-\\x5B\\x5D-\\x7E\\u0080-\\uFFFF]|\\\\[\\x20-\\x7E])*\""); + "|" + ATEXT + "(?:\\." + ATEXT + ")*+|\"(?:[\\x20\\x21\\x23-\\x5B\\x5D-\\x7E\\u0080-\\uFFFF]|\\\\[\\x20-\\x7E])*+\""); /** * RFC 6376 section 3.5: checks the decoded i= value as [ Local-part ] "@" domain-name. @@ -138,13 +139,14 @@ public class SignatureRecordImpl extends TagValue implements SignatureRecord { } // SPEC: sig-q-tag-method = "dns/txt" / x-sig-q-tag-type ["/" x-sig-q-tag-args], the type a - // hyphenated-word and the args a qp-hdr-value (dkim-safe-char but "|", or "=" HEX HEX) + // hyphenated-word and the args a qp-hdr-value (dkim-safe-char but "|", or "=" HEX HEX). Possessive, see LOCAL_PART. static final Pattern QUERY_METHOD = Pattern.compile( - "[A-Za-z](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?:/(?:[\\x21-\\x3A\\x3C\\x3E-\\x7B\\x7D\\x7E]|=[0-9A-F]{2})*)?"); + "[A-Za-z](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?:/(?:[\\x21-\\x3A\\x3C\\x3E-\\x7B\\x7D\\x7E]|=[0-9A-F]{2})*+)?"); - // SPEC: sig-z-tag-copy = hdr-name [FWS] ":" qp-hdr-value ; hdr-name = 1*ftext (%d33-57 / %d59-126) + // SPEC: sig-z-tag-copy = hdr-name [FWS] ":" qp-hdr-value ; hdr-name = 1*ftext (%d33-57 / %d59-126). + // Possessive, see LOCAL_PART. private static final Pattern COPIED_HEADER_FIELD = Pattern.compile( - "[\\x21-\\x39\\x3B-\\x7E]+[ \\t\\r\\n]*:(?:[\\x21-\\x3A\\x3C\\x3E-\\x7B\\x7D\\x7E]|=[0-9A-F]{2}|[ \\t\\r\\n])*"); + "[\\x21-\\x39\\x3B-\\x7E]++[ \\t\\r\\n]*+:(?:[\\x21-\\x3A\\x3C\\x3E-\\x7B\\x7D\\x7E]|=[0-9A-F]{2}|[ \\t\\r\\n])*+"); /** * RFC 6376 section 3.5: checks z= as sig-z-tag-copy *( "|" [FWS] sig-z-tag-copy ). diff --git a/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java b/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java index 0614da7..8f59319 100644 --- a/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java +++ b/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java @@ -449,4 +449,21 @@ public class SignatureRecordImplTest { System.clearProperty(TagValue.STRICT_VALIDATION_PROPERTY); } } + + private static String repeat(String s, int times) { + StringBuilder sb = new StringBuilder(s.length() * times); + for (int k = 0; k < times; k++) { + sb.append(s); + } + return sb.toString(); + } + + @Test + public void longValuesDoNotOverflowTheStack() { + recordWithCopiedFields("Subject:" + repeat("a=20", 16384)); + recordWithCopiedFields("Subject:" + repeat("a ", 32768) + "a"); + recordWithQuery("x-foo/" + repeat("a=20", 16384)); + recordWithIdentity(repeat("a.", 32768) + "[email protected]"); + recordWithIdentity("\"" + repeat("a=20", 16384) + "\"@example.com"); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
