[CONC-129] Bug in Async flow Created: 2015-06-09  Updated: 2015-09-08  Resolved: 2015-09-08

Status: Closed
Project: MariaDB Connector/C
Component/s: None
Affects Version/s: 2.1
Fix Version/s: 2.2.0

Type: Bug Priority: Major
Reporter: Matt Fagan Assignee: Georg Richter
Resolution: Fixed Votes: 0
Labels: None
Environment:

All



 Description   

When using async, there is a logic error in the connect/close processing when using async but an error occurs during connection. Sample code:

MYSQL* pTest, *pRes;
int nRV, nRes;

// Create for async:
pTest = mysql_init(nullptr);
mysql_options(pTest, MYSQL_OPT_NONBLOCK, 0);

// Async connect:
nRV = mysql_real_connect_start(&pRes, pTest, "nonexist.example.com", "", "", "", 0, nullptr, 0);
while (nRV != 0)

{ nRV = mysql_real_connect_cont(&pRes, pTest, MYSQL_WAIT_WRITE | MYSQL_WAIT_READ); };
if (pRes == nullptr) { printf("FAIL\n"); } else { printf("SUCC\n"); };

// Async close:
nRV = mysql_close_start(pTest);
while (nRV != 0) { nRV = mysql_close_cont(pTest, MYSQL_WAIT_WRITE | MYSQL_WAIT_READ); };

This will segfault in mysql_close_start. The problem is that when real_connect fails, it deletes the options object that indicates that the client is using async. This happens on Line 1989 of libmariadb.c. This does not occur if there is a connection/network failure in any other async function. For comparison, here is modified sample code:

MYSQL* pTest, *pRes;
int nRV, nRes;

// Create for async:
pTest = mysql_init(nullptr);
mysql_options(pTest, MYSQL_OPT_NONBLOCK, 0);

// Async connect:
nRV = mysql_real_connect_start(&pRes, pTest, nullptr, "", "", "", 0, nullptr, 0);
while (nRV != 0) { nRV = mysql_real_connect_cont(&pRes, pTest, MYSQL_WAIT_WRITE | MYSQL_WAIT_READ); }

;
if (pRes == nullptr)

{ printf("FAIL\n"); } else { printf("SUCC\n"); };

// Async query:
nRV = mysql_query_start(&nRes, pTest, "show databases");
while (nRV != 0) { closesocket(mysql_get_socket(pTest)); nRV = mysql_query_cont(&nRes, pTest, MYSQL_WAIT_WRITE | MYSQL_WAIT_READ); };
if (nRes == 0) { printf("SUCCn"); } else { printf("FAILn"); }

;

// Async close:
nRV = mysql_close_start(pTest);
while (nRV != 0)

{ nRV = mysql_close_cont(pTest, MYSQL_WAIT_WRITE | MYSQL_WAIT_READ); }

;

This will run successfully without any segfault, because when the mysql_query_cont() call fails, it does not delete the options object.

There are a few possible fixes:
1. Add note to the documentation that mysql_close_start cannot be used if an error occurs in mysql_real_connect_start/cont.
2. Remove the call to mysql_close_options() at the bottom of the mthd_my_real_connect() function. This way, the MYSQL* object will be in the same state regardless of whether a connection error occurs in mysql_connect or mysql_query.
3. Modify mysql_close_start() to first check if the socket is actually running. If it is not running, then it can divert to run the synchronous mysql_close() instead. This avoids the problem under all circumstances without modifying the current behavior. For this, change part of mysql_async.c from:

int STDCALL
mysql_close_start(MYSQL *sock)
{
int res;

/* It is legitimate to have NULL sock argument, which will do nothing. */
if (sock)

{ res= mysql_close_slow_part_start(sock); /* If we need to block, return now and do the rest in mysql_close_cont(). */ if (res) return res; }
mysql_close(sock);
return 0;
}

To:

mysql_close_start(MYSQL *sock)
{
int res;

/* It is legitimate to have NULL sock argument, which will do nothing. */
if (sock && sock->net.vio)
{ res= mysql_close_slow_part_start(sock); /* If we need to block, return now and do the rest in mysql_close_cont(). */ if (res) return res; }

mysql_close(sock);
return 0;
}

Note the only change is the if conditional, from:
if (sock)
To:
if (sock && sock->net.vio)

An alternative that would work is:
if (sock && sock->options.extension)



 Comments   
Comment by Georg Richter [ 2015-09-08 ]

Thanks for your bug report and provided fix!

Fixed in C/C 2.2 rev. abf0080bfa0a4255713034eb60245f2d890652da

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