diff --git a/mysql-test/main/func_date_add.result b/mysql-test/main/func_date_add.result index a6201a3c23f..1c2d9ab5eb1 100644 --- a/mysql-test/main/func_date_add.result +++ b/mysql-test/main/func_date_add.result @@ -200,3 +200,34 @@ select 30 + (20010101 + interval 2 day), x from v1; 20010133 20010133 drop view v1; End of 10.2 tests +# +# Start of 10.5 tests +# +# +# MDEV-30931 UBSAN: negation of -X cannot be represented in type 'long long int'; cast to an unsigned type to negate this value to itself in get_interval_value on SELECT +# +SELECT DATE_ADD('01-01-23',INTERVAL '9223372036854775808-02' WEEK); +DATE_ADD('01-01-23',INTERVAL '9223372036854775808-02' WEEK) +NULL +Warnings: +Warning 1292 Truncated incorrect INTEGER value: '9223372036854775808-02' +Warning 1441 Datetime function: datetime field overflow +SELECT DATE_ADD('01-01-23',INTERVAL -9223372036854775807 WEEK); +DATE_ADD('01-01-23',INTERVAL -9223372036854775807 WEEK) +NULL +Warnings: +Warning 1441 Datetime function: datetime field overflow +SELECT DATE_ADD('01-01-23',INTERVAL -9223372036854775808 WEEK); +DATE_ADD('01-01-23',INTERVAL -9223372036854775808 WEEK) +NULL +Warnings: +Warning 1441 Datetime function: datetime field overflow +SELECT DATE_ADD('01-01-23',INTERVAL -9223372036854775809 WEEK); +DATE_ADD('01-01-23',INTERVAL -9223372036854775809 WEEK) +NULL +Warnings: +Warning 1916 Got overflow when converting '-9223372036854775809' to INT. Value truncated +Warning 1441 Datetime function: datetime field overflow +# +# End of 10.5 tests +# diff --git a/mysql-test/main/func_date_add.test b/mysql-test/main/func_date_add.test index f9287b952d2..aa00fd144e2 100644 --- a/mysql-test/main/func_date_add.test +++ b/mysql-test/main/func_date_add.test @@ -169,3 +169,20 @@ select 30 + (20010101 + interval 2 day), x from v1; drop view v1; --echo End of 10.2 tests + +--echo # +--echo # Start of 10.5 tests +--echo # + +--echo # +--echo # MDEV-30931 UBSAN: negation of -X cannot be represented in type 'long long int'; cast to an unsigned type to negate this value to itself in get_interval_value on SELECT +--echo # + +SELECT DATE_ADD('01-01-23',INTERVAL '9223372036854775808-02' WEEK); +SELECT DATE_ADD('01-01-23',INTERVAL -9223372036854775807 WEEK); +SELECT DATE_ADD('01-01-23',INTERVAL -9223372036854775808 WEEK); +SELECT DATE_ADD('01-01-23',INTERVAL -9223372036854775809 WEEK); + +--echo # +--echo # End of 10.5 tests +--echo # diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 2f689acc54c..cd0e88544e0 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1306,6 +1306,15 @@ my_decimal *Item_func_time_to_sec::decimal_op(my_decimal* buf) } +static inline +uint32 adjust_interval_field_uint32(ulonglong value, int32 multiplier) +{ + return value > ((ulonglong) (uint32) (UINT_MAX32)) / multiplier ? + (uint32) UINT_MAX32 : + (uint32) (value * multiplier); +} + + /** Convert a string to a interval value. @@ -1316,7 +1325,7 @@ bool get_interval_value(THD *thd, Item *args, interval_type int_type, INTERVAL *interval) { ulonglong array[5]; - longlong UNINIT_VAR(value); + ulonglong UNINIT_VAR(value); const char *UNINIT_VAR(str); size_t UNINIT_VAR(length); CHARSET_INFO *UNINIT_VAR(cs); @@ -1343,14 +1352,17 @@ bool get_interval_value(THD *thd, Item *args, } else if ((int) int_type <= INTERVAL_MICROSECOND) { - value= args->val_int(); - if (args->null_value) - return 1; - if (value < 0) - { - interval->neg=1; - value= -value; - } + /* + Let's use Longlong_hybrid_null to handle correctly: + - signed and unsigned values + - the corner case with LONGLONG_MIN + (avoid undefined behavior with its negation) + */ + const Longlong_hybrid_null nr= args->to_longlong_hybrid_null(); + if (nr.is_null()) + return true; + value= nr.abs(); + interval->neg= nr.neg() ? 1 : 0; } else { @@ -1377,13 +1389,13 @@ bool get_interval_value(THD *thd, Item *args, interval->year= (ulong) value; break; case INTERVAL_QUARTER: - interval->month= (ulong)(value*3); + interval->month= adjust_interval_field_uint32(value, 3); break; case INTERVAL_MONTH: interval->month= (ulong) value; break; case INTERVAL_WEEK: - interval->day= (ulong)(value*7); + interval->day= adjust_interval_field_uint32(value, 7); break; case INTERVAL_DAY: interval->day= (ulong) value; diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 12e4460ed25..90bf701fadc 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -930,7 +930,7 @@ void make_truncated_value_warning(THD *thd, bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, const INTERVAL &interval, bool push_warn) { - long period, sign; + long sign; sign= (interval.neg == (bool)ltime->neg ? 1 : -1); @@ -1001,13 +1001,17 @@ bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, break; } case INTERVAL_WEEK: - period= (calc_daynr(ltime->year,ltime->month,ltime->day) + - sign * (long) interval.day); + { + longlong period= calc_daynr(ltime->year, ltime->month, ltime->day) + + (longlong) sign * (longlong) interval.day; + if (period < 0 || period > 0x7FFFFFFF) + goto invalid_date; /* Daynumber from year 0 to 9999-12-31 */ if (get_date_from_daynr((long) period,<ime->year,<ime->month, <ime->day)) goto invalid_date; break; + } case INTERVAL_YEAR: ltime->year+= sign * (long) interval.year; if ((ulong) ltime->year >= 10000L) @@ -1019,8 +1023,9 @@ bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, case INTERVAL_YEAR_MONTH: case INTERVAL_QUARTER: case INTERVAL_MONTH: - period= (ltime->year*12 + sign * (long) interval.year*12 + - ltime->month-1 + sign * (long) interval.month); + { + long period= (ltime->year*12 + sign * (long) interval.year*12 + + ltime->month-1 + sign * (long) interval.month); if ((ulong) period >= 120000L) goto invalid_date; ltime->year= (uint) (period / 12); @@ -1033,6 +1038,7 @@ bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, ltime->day++; // Leap-year } break; + } default: goto null_date; } diff --git a/strings/my_strtoll10.c b/strings/my_strtoll10.c index 8144be7a1da..74baef080eb 100644 --- a/strings/my_strtoll10.c +++ b/strings/my_strtoll10.c @@ -241,7 +241,7 @@ end4: *endptr= (char*) s; if (negative) { - if (li >= MAX_NEGATIVE_NUMBER) + if (li >= MAX_NEGATIVE_NUMBER) // Avoid undefined behavior goto overflow; return -((longlong) li); }