Bug #55932 handle_sigint -> mysql_get_server_version crashes if no connection
Submitted: 12 Aug 2010 1:22 Modified: 11 Jul 2014 4:14
Reporter: [ name withheld ] Email Updates:
Status: Can't repeat Impact on me:
None 
Category:MySQL Server: Command-line Clients Severity:S3 (Non-critical)
Version:5.1.48, 5.1-bzr OS:Any
Assigned to: CPU Architecture:Any

[12 Aug 2010 1:22] [ name withheld ]
Description:
According to the report here:

https://bugzilla.redhat.com/show_bug.cgi?id=623475

it's possible to crash the mysql client if one hits control-C at the wrong time while dealing with loss of connection.  I've not been able to reproduce this myself; the timing is probably too sensitive to hit it except by bad luck.  However, the stack trace seems to make it clear what happened: handle_sigint tried to call mysql_get_server_version on a dead connection object.  I think that call needs some guards.

How to repeat:
You might be able to reproduce it reliably after adding some sleep calls in the right places, but I've not tried.

Suggested fix:
Maybe test "connected" first, as is done for the mysql_get_server_version call in com_help?
[15 Aug 2010 9:52] Sveta Smirnova
Thank you for the report.

Please check if it is duplicate of bug #50008 or bug #47655: does client connects to older server?
[15 Aug 2010 15:32] [ name withheld ]
Based on the stack trace (see attachment in Red Hat bugzilla) I don't believe this is a duplicate of either of those bugs.  However, it could be that there is a server version mismatch contributing to the problem --- the original reporter didn't say whether he was connecting to a local or remote server.  I've asked him to clarify that.
[15 Aug 2010 15:39] Sveta Smirnova
Thank you for the feedback.

Please ask: I was not able to repeat using gdb and having breakpoints in locations which stack trace is showing.
[15 Aug 2010 17:13] Damien Grassart
Yes, I was connecting to an older MySQL server: 4.1.25-pro-gpl-log but I am not sure if those other bugs are related. I can't reproduce this crash either and that was the first and only time it's happened to me, so it seems pretty rare. Let me know if there's any other info I can provide.

Thanks,
-Damien
[17 Aug 2010 20:02] Sveta Smirnova
Thank you for the feedback.

I still can not repeat described behavior.

One thing confuses me. In the trace provided I see my_connect call, then mysql_get_server_version. But there is no mysql_get_server_version in my_connect call. Which binaries do you use?
[18 Aug 2010 14:46] [ name withheld ]
What the trace looks like to me is that my_connect was interrupted by SIGINT (control-C).  It's the signal handler handle_sigint that's calling mysql_get_server_version.
[18 Aug 2010 19:03] Sveta Smirnova
Thank you for the feedback.

But it is not referenced in handle_sigint either. Regular MySQL command line client gets version # from TCP/IP headers and don't ask for server version when reconnects. So is still interesting which client/binaries Damien uses.

Part from doxygen output:

ulong STDCALL mysql_get_server_version  	(  	MYSQL *   	 mysql  	 )   	

Definition at line 3229 of file client.c.

References mysql, pos(), st_mysql::server_version, strtoul(), and version().
[18 Aug 2010 22:29] [ name withheld ]
Um ... please note this bug report is against 5.1 branch.  I have not looked at the newer branches, but in 5.1.49 there is such a call, line 1309 of client/mysql.cc.

[ looks at the code some more ... ]  I wonder whether the problem isn't just that that call is referencing &mysql, rather than the new connection kill_mysql that was just set up above that?
[19 Aug 2010 18:53] Sveta Smirnova
Thank you for the feedback.

Verified as described.

To repeat modify sources as follow:

$bzr diff

=== modified file 'sql-common/client.c'
--- sql-common/client.c 2010-05-10 03:02:05 +0000
+++ sql-common/client.c 2010-08-19 18:33:59 +0000
@@ -3230,7 +3230,10 @@
 {
   uint major, minor, version;
   char *pos= mysql->server_version, *end_pos;
-  major=   (uint) strtoul(pos, &end_pos, 10);  pos=end_pos+1;
+  pos=0;
+  fprintf(stdout, "Disconnect now\n");
+  major=   (uint) strtoul(pos, &end_pos, sleep(10) + 10);      pos=end_pos+1;
+  fprintf(stdout, "stop disconnecting\n");
   minor=   (uint) strtoul(pos, &end_pos, 10);  pos=end_pos+1;
   version= (uint) strtoul(pos, &end_pos, 10);
   return (ulong) major*10000L+(ulong) (minor*100+version);

Then connect to 4.1 server, create large table named t1 there, then run query `create temporary table tt1(f1 int) select * from t1;`

In other window connect to 4.1 server and run query SHOW PROCESSLIST

In window one press Ctrl-C, then after "Disconnect here" in second window kill process running CREATE TEMPORARY TABLE

Wait when client crashes.
[19 Aug 2010 18:59] Sveta Smirnova
Sorry: please ignore previous comment. It was wrong test.
[21 Aug 2010 9:40] Sveta Smirnova
Correct test to repeat the problem.

Diff:

$bzr diff
=== modified file 'client/mysql.cc'
--- client/mysql.cc     2010-07-20 18:07:36 +0000
+++ client/mysql.cc     2010-08-21 09:32:41 +0000
@@ -1285,6 +1285,8 @@
 {
   char kill_buffer[40];
   MYSQL *kill_mysql= NULL;
+  
+  tee_fprintf(stdout, " Ctrl-C -- exit!\n");
 
   /* terminate if no query being executed, or we already tried interrupting */
   /* terminate if no query being executed, or we already tried interrupting */
@@ -2760,6 +2762,7 @@
     int error;
     if (!mysql_real_query(&mysql,buf,length))
       return 0;
+
     error= put_error(&mysql);
     if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 ||
         !opt_reconnect)
@@ -3988,7 +3991,13 @@
   }
   else
     opt_rehash= 0;
+  fprintf(stdout, "Disconnect now\n");
+  sleep(10);
+  fprintf(stdout, "stop disconnecting\n");
   error=sql_connect(current_host,current_db,current_user,opt_password,0);
+  fprintf(stdout, "Connect now\n");
+  sleep(10);
+  fprintf(stdout, "connecting\n");
   opt_rehash= save_rehash;
 
   if (connected)

Then start MySQL Proxy connected to 4.1 backend: mysql-proxy --proxy-backend-addresses=127.0.0.1:3341

Connect to Proxy port using modified 5.1 client: ./client/mysql -h127.0.0.1 -P4040 test 

Run set global interactive_timeout=1;,  set global wait_timeout=1;, reconnect.

Run query SHOW TABLES, you get:

ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Disconnect now

Then go to another terminal and kill MySQL Proxy

Then back to client terminal and see:

stop disconnecting
ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1' (61)
Connect now

Then restart MySQL Proxy with same parameters.

Then back to the client and hit Ctrl^C, you get error:

mysql> show tables;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Disconnect now
stop disconnecting
ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1' (61)
Connect now
^C Ctrl-C -- exit!
Bus error
[21 Aug 2010 9:44] Sveta Smirnova
Problem with test case not repeatable every time. You need to be *not* very fast to repeat it.

Suggested fix: in handle_sigint check existence of mysql instance before calling mysql_get_server_version.
[11 Jul 2014 4:14] Erlend Dahl
This is no longer repeatable on latest versions of 5.5 and 5.6.