Bug #98177 CR_CONN_HOST_ERROR never returned by async client

Submitted: 10 Jan 2020 3:14 Modified: 20 Jul 2020 8:43
Reporter: Manuel Ung Email Updates:
Status: Can't repeat Impact on me:
None 
Category:MySQL Server: C API (client library) Severity:S3 (Non-critical)
Version:8.0.18 OS:Any
Assigned to: CPU Architecture:Any

[10 Jan 2020 3:14] Manuel Ung
Description:
During connection, the initial tcp connect is started by calling vio_socket_connect in csm_begin_connect.

In blocking mode, vio_socket_connect blocks until the connection finishes (either with success or failure), and its return code can be checked immediately in csm_begin_connect (and CR_CONN_HOST_ERROR is returned if a error was hit).

However, in nonblocking mode, vio_socket_connect returns right away, and would not necessarily return an error if the network was not ready yet. There is no call to vio_io_wait to check for connection status, so instead, we error when attempt to write to an disconnected socket.

This is the reason why the mtr test main.ipv4_as_ipv6 fails in the async client.

How to repeat:
./mtr --async-client main.ipv4_as_ipv6

Suggested fix:
Call vio_io_wait in csm_complete_connect to verify if connection has succeeded or not, and return not ready if needed.

Additionally, there is error checking code in vio_socket_connect that is not executed in nonblocking mode (ie. the call to mysql_socket_getsockopt to collect errno). This code will have to run after vio_io_wait indicates that network is ready.
[10 Jan 2020 5:43] MySQL Verification Team
Hello Manuel Ung,

Thank you for the bug report and feedback.

regards,
Umesh
[25 Mar 2020 18:40] Jay Edgar
In addition, once #98574 and #98980 are fixed, you may need to add a check in cli_advanced_command_nonblocking() to make sure that net_async is not null.  This is because with this check added, it is possible to never get net_async assigned (via my_net_init).  If that happens then when mysql_close() is called, it will call cli_advanced_command_nonblocking() with net_async (via NET_ASYNC_DATA(mysql)) having never been initialized:

diff --git a/sql-common/client.cc b/sql-common/client.cc
index a44e9f4ec17..8d66cbdbb0c 100644
--- a/sql-common/client.cc
+++ b/sql-common/client.cc
@@ -1445,7 +1445,7 @@ net_async_status cli_advanced_command_nonblocking(
     DBUG_DUMP("sending arg", arg, arg_length);
   }

-  if (mysql->net.vio == 0) {
+  if (mysql->net.vio == 0 || !net_async) {
     set_mysql_error(mysql, CR_SERVER_GONE_ERROR, unknown_sqlstate);
     goto end;
   }

I was able to reproduce the problem by attempting to connect to an invalid IP address, fail (because mysql_real_connect_nonblocking will always return NET_ASYNC_NOT_READY) and the call mysql_close():

diff --git a/testclients/mysql_client_test.cc b/testclients/mysql_client_test.cc
index 9e558664abf..c36b79b03db 100644
--- a/testclients/mysql_client_test.cc
+++ b/testclients/mysql_client_test.cc
@@ -20165,6 +20165,26 @@ static void test_bug27443252() {

 void perform_arithmatic() { fprintf(stdout, "\n Do some other stuff."); }

+static void test_crash() {
+  MYSQL *mysql_local;
+  net_async_status status;
+
+  /*make new non blocking connection to do asynchronous operations */
+  if (!(mysql_local = mysql_client_init(NULL))) {
+    myerror("mysql_client_init() failed");
+    exit(1);
+  }
+
+  status = mysql_real_connect_nonblocking(mysql_local, "0100::", opt_user, opt_password, current_db, opt_port, opt_unix_socket, CLIENT_MULTI_STATEMENTS);
+  if (status != NET_ASYNC_NOT_READY) {
+    myerror("Unexpected result from mysql_real_connect_nonblocking()");
+    exit(1);
+  }
+
+  // See if we can close without crashing
+  mysql_close(mysql_local);
+}
+
 static void test_wl11381() {
   MYSQL *mysql_local;
   MYSQL_RES *result;
@@ -20874,6 +20894,7 @@ static struct my_tests_st my_tests[] = {
     {"test_wl11772", test_wl11772},
     {"test_wl12475", test_wl12475},
     {"test_bug30032302", test_bug30032302},
+    {"test_crash", test_crash},
     {0, 0}};

 static struct my_tests_st *get_my_tests() { return my_tests; }
[20 Jul 2020 8:43] Erlend Dahl
We can't repeat this any more on 8.0.21.

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.053 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.