ICU-22633 Fix Integer-overflow in icu_75::Calendar::add

See #2805
This commit is contained in:
Frank Tang 2024-01-30 23:55:26 +00:00 committed by Frank Yung-Fong Tang
parent ae9cc8cbd1
commit 9515e82741
4 changed files with 70 additions and 4 deletions

View file

@ -2129,9 +2129,19 @@ void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status
switch (field) {
case UCAL_ERA:
set(field, get(field, status) + amount);
{
int32_t era = get(UCAL_ERA, status);
if (U_FAILURE(status)) {
return;
}
if (uprv_add32_overflow(era, amount, &era)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
set(UCAL_ERA, era);
pinField(UCAL_ERA, status);
return;
}
case UCAL_YEAR:
case UCAL_YEAR_WOY:
@ -2147,7 +2157,10 @@ void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status
if (era == 0) {
const char * calType = getType();
if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) {
amount = -amount;
if (uprv_mul32_overflow(amount, -1, &amount)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
}
}
}
@ -2159,7 +2172,16 @@ void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status
{
UBool oldLenient = isLenient();
setLenient(true);
set(field, get(field, status) + amount);
int32_t value = get(field, status);
if (U_FAILURE(status)) {
return;
}
if (uprv_add32_overflow(value, amount, &value)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
set(field, value);
pinField(UCAL_DAY_OF_MONTH, status);
if(oldLenient==false) {
complete(status); /* force recalculate */

View file

@ -34,7 +34,7 @@ int64_t ClockMath::floorDivide(int64_t numerator, int64_t denominator) {
int32_t ClockMath::floorDivide(int32_t numerator, int32_t denominator,
int32_t* remainder) {
auto quotient = floorDivide(numerator, denominator);
int64_t quotient = floorDivide(numerator, denominator);
if (remainder != nullptr) {
*remainder = numerator - (quotient * denominator);
}

View file

@ -190,6 +190,7 @@ void CalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &name,
TESTCASE_AUTO(TestDangiOverflowIsLeapMonthBetween22507);
TESTCASE_AUTO(TestRollWeekOfYear);
TESTCASE_AUTO(TestFirstDayOfWeek);
TESTCASE_AUTO(TestAddOverflow);
TESTCASE_AUTO(TestChineseCalendarComputeMonthStart);
@ -5659,6 +5660,48 @@ void CalendarTest::TestChineseCalendarComputeMonthStart() { // ICU-22639
chinese.hasLeapMonthBetweenWinterSolstices);
}
void CalendarTest::TestAddOverflow() {
UErrorCode status = U_ZERO_ERROR;
LocalPointer<Calendar> calendar(
Calendar::createInstance(Locale("en"), status),
status);
if (failure(status, "Calendar::createInstance")) return;
for (int32_t i = 0; i < UCAL_FIELD_COUNT; i++) {
status = U_ZERO_ERROR;
calendar->setTime(0, status);
calendar->add(static_cast<UCalendarDateFields>(i), INT32_MAX / 2, status);
calendar->add(static_cast<UCalendarDateFields>(i), INT32_MAX, status);
if ((i == UCAL_ERA) ||
(i == UCAL_YEAR) ||
(i == UCAL_YEAR_WOY) ||
(i == UCAL_EXTENDED_YEAR) ||
(i == UCAL_IS_LEAP_MONTH) ||
(i == UCAL_MONTH) ||
(i == UCAL_ORDINAL_MONTH) ||
(i == UCAL_ZONE_OFFSET) ||
(i == UCAL_DST_OFFSET)) {
assertTrue("add INT32_MAX should fail", U_FAILURE(status));
} else {
assertTrue("add INT32_MAX should still success", U_SUCCESS(status));
}
status = U_ZERO_ERROR;
calendar->setTime(0, status);
calendar->add(static_cast<UCalendarDateFields>(i), INT32_MIN / 2, status);
calendar->add(static_cast<UCalendarDateFields>(i), INT32_MIN, status);
if ((i == UCAL_YEAR) ||
(i == UCAL_YEAR_WOY) ||
(i == UCAL_EXTENDED_YEAR) ||
(i == UCAL_IS_LEAP_MONTH) ||
(i == UCAL_ZONE_OFFSET) ||
(i == UCAL_DST_OFFSET)) {
assertTrue("add INT32_MIN should fail", U_FAILURE(status));
} else {
assertTrue("add INT32_MIN should still success", U_SUCCESS(status));
}
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */
//eof

View file

@ -332,6 +332,7 @@ public: // package
void TestDangiOverflowIsLeapMonthBetween22507();
void TestFWWithISO8601();
void TestAddOverflow();
void TestRollWeekOfYear();
void verifyFirstDayOfWeek(const char* locale, UCalendarDaysOfWeek expected);