Skip to content

Commit e025466

Browse files
committed
ICU-22983 Fix DecimalQuantity::shiftLeft ubsan issue
ICU-22983 Add java tests and comments
1 parent 2ba362f commit e025466

File tree

5 files changed

+20
-2
lines changed

5 files changed

+20
-2
lines changed

icu4c/source/i18n/number_decimalquantity.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1133,7 +1133,7 @@ void DecimalQuantity::setDigitPos(int32_t position, int8_t value) {
11331133
}
11341134

11351135
void DecimalQuantity::shiftLeft(int32_t numDigits) {
1136-
if (!usingBytes && precision + numDigits > 16) {
1136+
if (!usingBytes && precision + numDigits >= 16) {
11371137
switchStorage();
11381138
}
11391139
if (usingBytes) {

icu4c/source/test/intltest/numfmtst.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
253253
TESTCASE_AUTO(Test10997_FormatCurrency);
254254
TESTCASE_AUTO(Test21556_CurrencyAsDecimal);
255255
TESTCASE_AUTO(Test22088_Ethiopic);
256+
TESTCASE_AUTO(Test22983_LongFraction);
256257
TESTCASE_AUTO_END;
257258
}
258259

@@ -10161,5 +10162,9 @@ void NumberFormatTest::Test22088_Ethiopic() {
1016110162
assertEquals("Wrong result with UNUM_NUMBERING_SYSTEM and English", u"123", nf3->format(123, result));
1016210163
}
1016310164
}
10165+
void NumberFormatTest::Test22983_LongFraction() {
10166+
IcuTestErrorCode status(*this, "Test22983_LongFraction");
10167+
DecimalFormat df(u"0.0000000000000001", status);
10168+
}
1016410169

1016510170
#endif /* #if !UCONFIG_NO_FORMATTING */

icu4c/source/test/intltest/numfmtst.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ class NumberFormatTest: public CalendarTimeZoneTest {
309309
void Test10997_FormatCurrency();
310310
void Test21556_CurrencyAsDecimal();
311311
void Test22088_Ethiopic();
312+
void Test22983_LongFraction();
312313

313314
private:
314315
UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f);

icu4j/main/common_tests/src/test/java/com/ibm/icu/dev/test/format/NumberRegressionTests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1835,6 +1835,11 @@ public void test4241880() {
18351835
}
18361836
Locale.setDefault(savedLocale);
18371837
}
1838+
1839+
@Test
1840+
public void test22983LongFraction() {
1841+
DecimalFormat format = new DecimalFormat("0.0000000000000001");
1842+
}
18381843
}
18391844

18401845
class myformat implements Serializable

icu4j/main/core/src/main/java/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,14 @@ protected void setDigitPos(int position, byte value) {
167167

168168
@Override
169169
protected void shiftLeft(int numDigits) {
170-
if (!usingBytes && precision + numDigits > 16) {
170+
// https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.19
171+
// If the promoted type of the left-hand operand is long, then only the
172+
// six lowest-order bits of the right-hand operand are used as the shift
173+
// distance. It is as if the right-hand operand were subjected to a bitwise
174+
// logical AND operator & (§15.22.1) with the mask value 0x3f (0b111111).
175+
// The shift distance actually used is therefore always in the range 0 to
176+
// 63, inclusive.
177+
if (!usingBytes && precision + numDigits >= 16) {
171178
switchStorage();
172179
}
173180
if (usingBytes) {

0 commit comments

Comments
 (0)