diff --git a/icu4c/source/i18n/nfrule.cpp b/icu4c/source/i18n/nfrule.cpp index 0078159af82b..dd67bcc2330b 100644 --- a/icu4c/source/i18n/nfrule.cpp +++ b/icu4c/source/i18n/nfrule.cpp @@ -504,6 +504,13 @@ NFRule::extractSubstitutions(const NFRuleSet* owner, } rulePatternFormat = formatter->createPluralFormat(pluralType, ruleText.tempSubString(endType + 1, pluralRuleEnd - endType - 1), status); + if (U_SUCCESS(status) && getDivisor() <= 0) { + // radix^exponent overflowed 64 bits, so doFormat() would divide the + // number by a zero (or wrapped-negative) divisor. Reject the rule, like + // the modulus and integral-part substitutions already do for a zero divisor. + status = U_PARSE_ERROR; + return; + } } } diff --git a/icu4c/source/test/intltest/itrbnf.cpp b/icu4c/source/test/intltest/itrbnf.cpp index ad7ad8f8d266..9a2a6b67e314 100644 --- a/icu4c/source/test/intltest/itrbnf.cpp +++ b/icu4c/source/test/intltest/itrbnf.cpp @@ -2834,6 +2834,15 @@ IntlTestRBNF::TestDividedByZero() { UErrorCode status = U_ZERO_ERROR; RuleBasedNumberFormat rbnf(u"7060920374060940374/4:[]", Locale::getUS(), perror, status); assertEquals("base is too large", U_NUMBER_ARG_OUTOFBOUNDS_ERROR, status); + + // A $(cardinal,...)$ rule whose radix^exponent overflows 64 bits: the + // divisor is zero, so doFormat() would divide the number by it. The rule + // must be rejected at construction instead of dividing by zero. + status = U_ZERO_ERROR; + RuleBasedNumberFormat pluralDivisor( + u"0: zero;\n4611686018427387904/16: big $(cardinal,one{a}other{b})$;", + Locale::getEnglish(), perror, status); + assertEquals("zero divisor from overflow", U_PARSE_ERROR, status); } void diff --git a/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/RbnfTest.java b/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/RbnfTest.java index 4b5535583286..e4c0161ac91e 100644 --- a/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/RbnfTest.java +++ b/icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/RbnfTest.java @@ -2202,6 +2202,20 @@ public void TestInfiniteRecursion() { } } + @Test + public void TestDivisorOverflow() { + // A $(cardinal,...)$ rule whose radix^exponent overflows 64 bits: the divisor is + // zero, so doFormat() would divide the number by it (ArithmeticException: / by zero). + // The rule must be rejected at construction instead, like a zero-divisor substitution. + try { + new RuleBasedNumberFormat( + "0: zero;\n4611686018427387904/16: big $(cardinal,one{a}other{b})$;"); + errln("Rule with a zero divisor from overflow should have been rejected"); + } catch (IllegalStateException e) { + // expected + } + } + /** * This test is a little contrived for English, but the grammar is relevant for several * languages, including: Latin, Germanic, Slavic and Indic. It's pretty common, especially for diff --git a/icu4j/main/core/src/main/java/com/ibm/icu/text/NFRule.java b/icu4j/main/core/src/main/java/com/ibm/icu/text/NFRule.java index 2d633503344a..f43d325d3cf4 100644 --- a/icu4j/main/core/src/main/java/com/ibm/icu/text/NFRule.java +++ b/icu4j/main/core/src/main/java/com/ibm/icu/text/NFRule.java @@ -412,6 +412,12 @@ private void extractSubstitutions(NFRuleSet owner, String sourceRuleText, NFRule rulePatternFormat = formatter.createPluralFormat( pluralType, ruleText.substring(endType + 1, pluralRuleEnd)); + if (getDivisor() <= 0) { + // radix^exponent overflowed 64 bits, so doFormat() would divide the + // number by a zero (or wrapped-negative) divisor. Reject the rule, like + // the modulus and integral-part substitutions already do for a zero divisor. + throw new IllegalStateException("Rule with divisor " + getDivisor()); + } } }