Bug #69618 Set @@global.gtid_purged='UUID:1-1000000000000000000' Asserts
Submitted: 28 Jun 2013 22:12 Modified: 21 Oct 2013 11:42
Reporter: Santosh Praneeth Banda Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Row Based Replication ( RBR ) Severity:S2 (Serious)
Version:5.6.12 OS:Any
Assigned to: CPU Architecture:Any
Tags: GTID REPLICATION

[28 Jun 2013 22:12] Santosh Praneeth Banda
Description:
While executing the command
set @@global.gtid_purged = '$uuid:1-1000000000000000000'
server asserts in debug build in get_string_length() function. gdb back trace

#0  0x00007ffff6847deb in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:67
#1  0x00007ffff684973f in __GI_abort () at abort.c:93
#2  0x00007ffff684064f in __assert_fail_base (fmt=0x7ffff697f1c8 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", 
    assertion=assertion@entry=0xf65200 "snprintf(buf, 22, \"%lld\", gno) == len", 
    file=file@entry=0xf648f0 "sql/rpl_gtid_set.cc", line=line@entry=871, 
    function=function@entry=0xf65b50 "int get_string_length(rpl_gno)") at assert.c:96
#3  0x00007ffff68406f3 in __GI___assert_fail (assertion=0xf65200 "snprintf(buf, 22, \"%lld\", gno) == len", 
    file=0xf648f0 "sql/rpl_gtid_set.cc", line=871, 
    function=0xf65b50 "int get_string_length(rpl_gno)") at assert.c:105
#4  0x00000000009bc7bd in get_string_length (gno=1000000000000000000)
    at sql/rpl_gtid_set.cc:871
#5  0x00000000009bc90b in Gtid_set::get_string_length (this=0x7ffff5c3a4b0, sf=0x13a9f80)
    at sql/rpl_gtid_set.cc:903
#6  0x000000000085631b in Gtid_set::to_string (this=0x7ffff5c3a4b0) at sql/rpl_gtid.h:1067
#7  0x000000000085c424 in Sys_var_gtid_purged::global_update (this=0x1776e00, thd=0x7ffff3679000, var=0x7fffe68140f0)
    at sql/sys_vars.h:2177
#8  0x00000000006fad77 in sys_var::update (this=0x1776e00, thd=0x7ffff3679000, var=0x7fffe68140f0)
    at sql/set_var.cc:193
#9  0x00000000006fbe72 in set_var::update (this=0x7fffe68140f0, thd=0x7ffff3679000)
    at sql/set_var.cc:670
#10 0x00000000006fba4b in sql_set_variables (thd=0x7ffff3679000, var_list=0x7ffff367ba68)
    at sql/set_var.cc:573
#11 0x00000000007aad31 in mysql_execute_command (thd=0x7ffff3679000, statement_start_time=0x7fffe77fd808, post_parse=0x7fffe77fd9b8)
    at sql/sql_parse.cc:3972
#12 0x00000000007b1ac9 in mysql_parse (thd=0x7ffff3679000, 
    rawbuf=0x7fffe6810210 "set @@global.gtid_purged = 'bcaaf9e8-e03e-11e2-b3ca-0002c9526dc0:1-1", '0' <repeats 18 times>, "'", 
    length=87, parser_state=0x7fffe77fe1c0, last_timer=0x7fffe77fd9b8, async_commit=0x7fffe77fd98c "")
    at sql/sql_parse.cc:6595
#13 0x00000000007a428e in dispatch_command (command=COM_QUERY, thd=0x7ffff3679000, 
    packet=0x7ffff367d001 "set @@global.gtid_purged = 'bcaaf9e8-e03e-11e2-b3ca-0002c9526dc0:1-1", '0' <repeats 18 times>, "'", 
    packet_length=87) at sql/sql_parse.cc:1393
#14 0x00000000007a3052 in do_command (thd=0x7ffff3679000) at sql/sql_parse.cc:1038
#15 0x000000000076b28a in do_handle_one_connection (thd_arg=0x7ffff3679000)
    at sql/sql_connect.cc:1002
#16 0x000000000076ad65 in handle_one_connection (arg=0x7ffff3679000)
    at sql/sql_connect.cc:918
#17 0x00007ffff7bc510d in start_thread (arg=0x7fffe77ff700) at pthread_create.c:301
#18 0x00007ffff68ff11d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:115

How to repeat:
Run set @@global.gtid_purged = '$uuid:1-1000000000000000000';

Suggested fix:
--- a/sql/rpl_gtid_set.cc
+++ b/sql/rpl_gtid_set.cc
@@ -860,9 +860,12 @@ static int get_string_length(rpl_gno gno)

-  cmp2= cmp * 10LL;
-  if (gno >= cmp2)
-    len++;
+  if (cmp < 1000000000000000000LL)
+  {
+    cmp2= cmp * 10LL; // This is causing LongLongInt overflow
+    if (gno >= cmp2)
+      len++;
+  }
[29 Jun 2013 3:57] Shane Bester
Verfiied as described.

Testcase:

set @@global.gtid_purged = 'bcaaf9e8-e03e-11e2-b3ca-0002c9526dc0:1-1000000000000000000';
[29 Jun 2013 3:59] Shane Bester
Version: '5.7.2-m12-debug-log'  MySQL Community Server (GPL)
Assertion failed: _snprintf(buf, 22, "%lld", gno) == len, file rpl_gtid_set.cc, line 868

mysqld-debug.exe!my_sigabrt_handler()[my_thr_init.c:451]
mysqld-debug.exe!raise()[winsig.c:593]
mysqld-debug.exe!abort()[abort.c:81]
mysqld-debug.exe!_wassert()[assert.c:153]
mysqld-debug.exe!get_string_length()[rpl_gtid_set.cc:868]
mysqld-debug.exe!Gtid_set::get_string_length()[rpl_gtid_set.cc:900]
mysqld-debug.exe!Gtid_set::to_string()[rpl_gtid.h:1067]
mysqld-debug.exe!Sys_var_gtid_purged::global_update()[sys_vars.h:2185]
mysqld-debug.exe!sys_var::update()[set_var.cc:194]
mysqld-debug.exe!set_var::update()[set_var.cc:670]
mysqld-debug.exe!sql_set_variables()[set_var.cc:573]
mysqld-debug.exe!mysql_execute_command()[sql_parse.cc:3515]
mysqld-debug.exe!mysql_parse()[sql_parse.cc:5228]
mysqld-debug.exe!dispatch_command()[sql_parse.cc:1330]
mysqld-debug.exe!do_command()[sql_parse.cc:1026]
mysqld-debug.exe!do_handle_one_connection()[sql_connect.cc:983]
mysqld-debug.exe!handle_one_connection()[sql_connect.cc:900]
mysqld-debug.exe!pfs_spawn_thread()[pfs.cc:1929]
mysqld-debug.exe!pthread_start()[my_winthread.c:60]
mysqld-debug.exe!_callthreadstartex()[threadex.c:314]
mysqld-debug.exe!_threadstartex()[threadex.c:297]
[21 Oct 2013 11:42] Jon Stephens
Fixed in 5.6+. Documented as follows in the 5.6.15 and 5.7.3 changelogs:

      An internal function used for storing GTID values could sometimes 
      try to handle them as strings of the wrong length.

Closed.
[4 Dec 2013 11:54] Laurynas Biveinis
5.6$ bzr log -r 5510
------------------------------------------------------------
revno: 5510
committer: Joao Gramacho <joao.gramacho@oracle.com>
branch nick: mysql-5.6
timestamp: Thu 2013-10-10 18:15:38 +0100
message:
  Bug#17032712 SET @@GLOBAL.GTID_PURGED='UUID:1-1000000000000000000' ASSERTS
  
  Problem:
  =======
  The get_string_length() function is failing to assert the correct string
  length of numbers with 19 digits.
  
  Analysis:
  ========
  The get_string_length() function returns the length a string must have in
  order to store a given number. It is used to calculate the string length
  of a GTID set.
  
  Because this function is static at rpl_gtid_set.cc, it is not trivial to
  make unit tests to verify its functionality. So, there is an assert
  executed only in debug enabled versions that checks if the value to be
  returned is equal to the length of printing the number in a string with
  snprintf().
  
  Because of the precision of the rpl_gno data type (int64), an auxiliary
  variable may overflow when the number to be evaluated has 19 digits. The
  currently implemented code doesn't take into account this overflow 
  possibility, making that every 19 digit number be evaluated as having 20
  digits in length. This divergence cause the assert to fail.
  
  Fix:
  ===
  Changed the function code replacing the length calculation algorithm with
  one that is simple, easy to read and overflow free.