diff --git a/src/main/java/org/apache/xmlbeans/impl/regex/RegexParser.java b/src/main/java/org/apache/xmlbeans/impl/regex/RegexParser.java index 0ef108a97..9054f3226 100644 --- a/src/main/java/org/apache/xmlbeans/impl/regex/RegexParser.java +++ b/src/main/java/org/apache/xmlbeans/impl/regex/RegexParser.java @@ -604,9 +604,9 @@ Token parseFactor() throws ParseException { min = ch -'0'; while (off < this.regexlen && (ch = this.regex.charAt(off++)) >= '0' && ch <= '9') { - min = min*10 +ch-'0'; - if (min < 0) + if (min > (Integer.MAX_VALUE - (ch-'0')) / 10) throw ex("parser.quantifier.5", this.offset); + min = min*10 +ch-'0'; } } else { @@ -625,9 +625,9 @@ else if ((ch = this.regex.charAt(off++)) >= '0' && ch <= '9') { while (off < this.regexlen && (ch = this.regex.charAt(off++)) >= '0' && ch <= '9') { - max = max*10 +ch-'0'; - if (max < 0) + if (max > (Integer.MAX_VALUE - (ch-'0')) / 10) throw ex("parser.quantifier.5", this.offset); + max = max*10 +ch-'0'; } if (min > max) diff --git a/src/test/java/misc/checkin/RegularExpressionTest.java b/src/test/java/misc/checkin/RegularExpressionTest.java index c6f54b9de..38861c585 100644 --- a/src/test/java/misc/checkin/RegularExpressionTest.java +++ b/src/test/java/misc/checkin/RegularExpressionTest.java @@ -15,12 +15,14 @@ package misc.checkin; +import org.apache.xmlbeans.impl.regex.ParseException; import org.apache.xmlbeans.impl.regex.RegularExpression; import org.junit.jupiter.api.Test; import java.util.Random; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class RegularExpressionTest { @@ -44,6 +46,23 @@ void testLookbehindRangeAtInputEnd() { assertFalse(new RegularExpression("x(?<=[a-c])").matches("xc")); } + @Test + void testQuantifierOverflow() { + // a {min,max} count larger than Integer.MAX_VALUE overflowed the int + // accumulator. the only guard was a post-multiply min<0/max<0 check, so + // counts that wrapped to a non-negative value slipped through: "a{4294967296}" + // parsed as "a{0}" (matched the empty string) and "a{1,4294967298}" as "a{1,2}", + // while bigger ones such as "a{99999999999}" blew the heap at match time. + assertThrows(ParseException.class, () -> new RegularExpression("a{4294967296}")); + assertThrows(ParseException.class, () -> new RegularExpression("a{4294967297}")); + assertThrows(ParseException.class, () -> new RegularExpression("a{99999999999}")); + assertThrows(ParseException.class, () -> new RegularExpression("a{1,4294967298}")); + // counts up to Integer.MAX_VALUE are representable and must still parse + new RegularExpression("a{2147483647}"); + new RegularExpression("a{0,2147483647}"); + assertTrue(new RegularExpression("a{2,4}").matches("aaa")); + } + private static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static final Random rnd = new Random();