commit c9f47cdd83e546674a400f2ea64a4495b8185a1c
Author: Alexander Barkov <bar@mariadb.org>
Date:   Mon Oct 9 15:00:35 2017 +0400

    MDEV-13972 crash in Item_func_sec_to_time::get_date
    
    Item_func_sec_to_time::get_date() calls an args[0]'s value method two times:
    - once val_int() or val_decimal() inside Item::get_seconds()
    - then val_str() to generate a warning, in case an out-of-range value
    
    This approach was wrong for two reasons:
    1. val_str() in some cases can return NULL even if val_int()/val_decimal()
       previously returned a not-NULL value.
       This is possible when args[0] is not deterministic
       (for instance, uses RAND() directly or indirectly).
       This caused the crash reported in the bug:
       the warning generation code in Item_func_sec_to_time::get_date()
       did not expect val_str() to return NULL.
    
       This patch fixes this problem by checking the result of val_str().
       In case of a non-NULL, the result of val_str() is used as before.
       In case of NULL, the "ulonglong seconds" value is used for the warning.
       Note, "ulong sec_part" value is ignored in the warning for simplicity.
    
    2. Calling val_str() to generate warnings fires the function side effect again.
       For example, consider SEC_TO_TIME(f1()), where f1() is a stored function
       doing some INSERTs as a side effect. If f1() returns an out-of-range value,
       the call for val_str() to generate the warning forces the INSERTs to happen
       again, which is wrong. The side effect must happen only once per call.
       This problem is NOT fixed by this patch to avoid big changes in 5.5.
       This will be fixed separately in 10.3 or 10.4.
    
    Also, passing err->ptr() and err->length() directly to
    make_truncated_value_warning() was wrong, because it did not take
    into account err->charset(). This caused unreadable warnings in case
    of tricky character sets, such as ucs2. This patch fixes this additional
    problem by using "ErrConvStr err2(err)".

diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result
index 7a93c55..d55d308 100644
--- a/mysql-test/r/ctype_ucs.result
+++ b/mysql-test/r/ctype_ucs.result
@@ -4367,5 +4367,13 @@ NO_ENGINE_SUBSTITUTION
 SET sql_mode=DEFAULT;
 SET NAMES utf8;
 #
+# MDEV-13972 crash in Item_func_sec_to_time::get_date
+#
+SELECT SEC_TO_TIME(CONVERT(900*24*60*60 USING ucs2));
+SEC_TO_TIME(CONVERT(900*24*60*60 USING ucs2))
+838:59:59.999999
+Warnings:
+Warning	1292	Truncated incorrect time value: '77760000'
+#
 # End of 5.5 tests
 #
diff --git a/mysql-test/t/ctype_ucs.test b/mysql-test/t/ctype_ucs.test
index d94c9ae..62890d0 100644
--- a/mysql-test/t/ctype_ucs.test
+++ b/mysql-test/t/ctype_ucs.test
@@ -863,5 +863,12 @@ SET sql_mode=DEFAULT;
 SET NAMES utf8;
 
 --echo #
+--echo # MDEV-13972 crash in Item_func_sec_to_time::get_date
+--echo #
+
+SELECT SEC_TO_TIME(CONVERT(900*24*60*60 USING ucs2));
+
+
+--echo #
 --echo # End of 5.5 tests
 --echo #
diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test
index 7544f9e..8323bd3 100644
--- a/mysql-test/t/func_time.test
+++ b/mysql-test/t/func_time.test
@@ -1615,6 +1615,21 @@ SELECT * FROM t1;
 DROP TABLE t1;
 SET sql_mode=DEFAULT;
 
+
+--echo #
+--echo # MDEV-13972 crash in Item_func_sec_to_time::get_date
+--echo #
+
+# The below query can return warning sporadically
+--disable_warnings
+DO TO_DAYS(SEC_TO_TIME(TIME(CEILING(UUID()))));
+--enable_warnings
+
+DO TO_DAYS(SEC_TO_TIME(MAKEDATE('',RAND(~('')))));
+SELECT TO_DAYS(SEC_TO_TIME(MAKEDATE(0,RAND(~0))));
+SELECT SEC_TO_TIME(MAKEDATE(0,RAND(~0)));
+
+
 --echo #
 --echo # End of 5.5 tests
 --echo #
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 0ed1506..eeb373a 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1733,9 +1733,18 @@ bool Item_func_sec_to_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
 
   ltime->hour= TIME_MAX_HOUR+1;
   check_time_range(ltime, decimals, &unused);
-  make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
-                               err->ptr(), err->length(),
-                               MYSQL_TIMESTAMP_TIME, NullS);
+  if (!err)
+  {
+    ErrConvInteger err2(sec, unsigned_flag);
+    make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                                 &err2, MYSQL_TIMESTAMP_TIME, NullS);
+  }
+  else
+  {
+    ErrConvString err2(err);
+    make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                                 &err2, MYSQL_TIMESTAMP_TIME, NullS);
+  }
   return 0;
 }
 
