[MDEV-13972] crash in Item_func_sec_to_time::get_date Created: 2017-10-01  Updated: 2017-10-10  Resolved: 2017-10-10

Status: Closed
Project: MariaDB Server
Component/s: Temporal Types
Affects Version/s: 5.5, 10.0, 10.1, 10.2.9, 10.2
Fix Version/s: 5.5.58, 10.0.33, 10.1.29, 10.2.10, 10.3.3

Type: Bug Priority: Major
Reporter: sbester1 Assignee: Sergei Golubchik
Resolution: Fixed Votes: 0
Labels: None
Environment:

Win64


Issue Links:
Relates
relates to MDEV-14032 SEC_TO_TIME executes side effect two ... Closed

 Description   

Version: '10.2.9-MariaDB'  socket: ''  port: 3306  mariadb.org binary distribution
mysqld.exe!Item_func_sec_to_time::get_date()[item_timefunc.cc:1855]
mysqld.exe!Item::get_date_with_conversion()[item.cc:151]
mysqld.exe!Item_func_to_days::val_int()[item_timefunc.cc:783]
mysqld.exe!Item::update_null_value()[item.h:1454]
mysqld.exe!Item_avg_field::is_null()[item_sum.h:1244]
mysqld.exe!mysql_do()[sql_do.cc:34]
mysqld.exe!mysql_execute_command()[sql_parse.cc:3495]
mysqld.exe!mysql_parse()[sql_parse.cc:7880]
mysqld.exe!dispatch_command()[sql_parse.cc:1814]
mysqld.exe!do_command()[sql_parse.cc:1359]
mysqld.exe!threadpool_process_request()[threadpool_common.cc:366]
mysqld.exe!tp_callback()[threadpool_common.cc:192]

How to repeat

do to_days(sec_to_time(time(ceiling(uuid())))); #sporadic
do to_days(sec_to_time(makedate('',rand(~('')))));



 Comments   
Comment by Elena Stepanova [ 2017-10-01 ]

Thanks for the report and test case.

Comment by Alexander Barkov [ 2017-10-05 ]

These simplified queries also crashes the server:

SELECT TO_DAYS(SEC_TO_TIME(MAKEDATE(0,RAND(~0))));
SELECT SEC_TO_TIME(MAKEDATE(0,RAND(~0)));

Comment by Alexander Barkov [ 2017-10-09 ]

The problem happens because Item_func_sec_to_time::get_date() fetches the value for the second time in case of overflow, using val_str(), which can return NULL when the argument is not deterministic (e.g. uses RAND() directly or indirectly). The code calling make_truncated_value_warning() does not expect this.

Also, Item_func_sec_to_time::get_date() abuses make_truncate_value_warning() in another way:
it passes err->ptr() and err->length() directly, without using ErrConvString. This causes unreadable warnings in case of "tricky" charsets such as UCS2:

SELECT SEC_TO_TIME(CONVERT(900*24*60*60 USING ucs2));
SHOW WARNINGS;

+---------+------+----------------------------------------------------------------------------+
| Level   | Code | Message                                                                    |
+---------+------+----------------------------------------------------------------------------+
| Warning | 1292 | Truncated incorrect time value: '\x007\x007\x007\x006\x000\x000\x000\x000' |
+---------+------+----------------------------------------------------------------------------+

The expected warning would be:

+---------+------+--------------------------------------------+
| Level   | Code | Message                                    |
+---------+------+--------------------------------------------+
| Warning | 1292 | Truncated incorrect time value: '77760000' |
+---------+------+--------------------------------------------+

Comment by Alexander Barkov [ 2017-10-10 ]

Opps, forgot to add the supposed commit comment when pushing:

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)".

Generated at Thu Feb 08 08:09:50 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.