Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed unexpected blocking in async codepaths w/SSL and other issues #216

Open
wants to merge 9 commits into
base: 3.3
Choose a base branch
from
2 changes: 2 additions & 0 deletions include/ma_pvio.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ typedef struct st_ma_pvio_methods PVIO_METHODS;
#define IS_MYSQL_ASYNC_ACTIVE(a) \
(IS_MYSQL_ASYNC(a)&& (a)->options.extension->async_context->active)

#define MATCH_PVIO_SYNC_OR_ASYNC(a) !IS_PVIO_ASYNC(a)

enum enum_pvio_timeout {
PVIO_CONNECT_TIMEOUT= 0,
PVIO_READ_TIMEOUT,
Expand Down
2 changes: 1 addition & 1 deletion libmariadb/ma_net.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ int ma_net_init(NET *net, MARIADB_PVIO* pvio)
if (pvio != 0) /* If real connection */
{
ma_pvio_get_handle(pvio, &net->fd);
ma_pvio_blocking(pvio, 1, 0);
ma_pvio_blocking(pvio, MATCH_PVIO_SYNC_OR_ASYNC(pvio), 0);
ma_pvio_fast_send(pvio);
}
return 0;
Expand Down
12 changes: 9 additions & 3 deletions libmariadb/ma_pvio.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ static size_t ma_pvio_read_async(MARIADB_PVIO *pvio, uchar *buffer, size_t lengt
if (res >= 0 || IS_BLOCKING_ERROR())
return res;
b->events_to_wait_for= MYSQL_WAIT_READ;
if (timeout >= 0)
if (timeout > 0)
{
b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
b->timeout_value= timeout;
Expand Down Expand Up @@ -325,7 +325,7 @@ static ssize_t ma_pvio_write_async(MARIADB_PVIO *pvio, const uchar *buffer, size
if (res >= 0 || IS_BLOCKING_ERROR())
return res;
b->events_to_wait_for= MYSQL_WAIT_WRITE;
if (timeout >= 0)
if (timeout > 0)
{
b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
b->timeout_value= timeout;
Expand Down Expand Up @@ -402,9 +402,11 @@ ssize_t ma_pvio_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length)
/* {{{ void ma_pvio_close */
void ma_pvio_close(MARIADB_PVIO *pvio)
{
MYSQL *mysql;
/* free internal structures and close connection */
if (pvio)
{
mysql = pvio->mysql;
#ifdef HAVE_TLS
if (pvio->ctls)
{
Expand All @@ -418,6 +420,10 @@ void ma_pvio_close(MARIADB_PVIO *pvio)
if (pvio->cache)
free(pvio->cache);

/* if there is an ongoing async operation */
if (mysql->options.extension && mysql->options.extension->async_context && mysql->options.extension->async_context->pvio)
mysql->options.extension->async_context->pvio = 0; /* clear pvio about to be freed */

free(pvio);
}
}
Expand Down Expand Up @@ -450,7 +456,7 @@ ma_pvio_wait_async(struct mysql_async_context *b, enum enum_pvio_io_event event,
break;
}

if (timeout >= 0)
if (timeout > 0)
{
b->events_to_wait_for |= MYSQL_WAIT_TIMEOUT;
b->timeout_value= timeout;
Expand Down
23 changes: 18 additions & 5 deletions libmariadb/secure/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,8 @@ void *ma_tls_init(MYSQL *mysql)
return NULL;
}

extern int ma_pvio_wait_io_or_timeout(MARIADB_PVIO *pvio, my_bool is_read, int timeout);

my_bool ma_tls_connect(MARIADB_TLS *ctls)
{
SSL *ssl = (SSL *)ctls->ssl;
Expand Down Expand Up @@ -490,11 +492,11 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
{
switch((SSL_get_error(ssl, rc))) {
case SSL_ERROR_WANT_READ:
if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1)
if (ma_pvio_wait_io_or_timeout(pvio, TRUE, pvio->timeout[PVIO_CONNECT_TIMEOUT]) < 1) /* handle non-blocking read */
try_connect= 0;
break;
case SSL_ERROR_WANT_WRITE:
if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1)
if (ma_pvio_wait_io_or_timeout(pvio, FALSE, pvio->timeout[PVIO_CONNECT_TIMEOUT]) < 1) /* handle non-blocking write */
try_connect= 0;
break;
default:
Expand Down Expand Up @@ -530,7 +532,7 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
}

static my_bool
ma_tls_async_check_result(int res, struct mysql_async_context *b, SSL *ssl)
ma_tls_async_check_result(int res, struct mysql_async_context *b, SSL *ssl, int timeout)
{
int ssl_err;
b->events_to_wait_for= 0;
Expand All @@ -543,6 +545,11 @@ ma_tls_async_check_result(int res, struct mysql_async_context *b, SSL *ssl)
b->events_to_wait_for|= MYSQL_WAIT_WRITE;
else
return 1;
if (timeout > 0)
{
b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
b->timeout_value= timeout;
}
if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
my_context_yield(&b->async_context);
Expand All @@ -557,13 +564,16 @@ ssize_t ma_tls_read_async(MARIADB_PVIO *pvio,
{
int res;
struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
int timeout= pvio->timeout[PVIO_READ_TIMEOUT];
MARIADB_TLS *ctls= pvio->ctls;

for (;;)
{
res= SSL_read((SSL *)ctls->ssl, (void *)buffer, (int)length);
if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl))
if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl, timeout))
return res;
if (b->events_occurred & MYSQL_WAIT_TIMEOUT)
return -1;
}
}

Expand All @@ -573,13 +583,16 @@ ssize_t ma_tls_write_async(MARIADB_PVIO *pvio,
{
int res;
struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
int timeout= pvio->timeout[PVIO_WRITE_TIMEOUT];
MARIADB_TLS *ctls= pvio->ctls;

for (;;)
{
res= SSL_write((SSL *)ctls->ssl, (void *)buffer, (int)length);
if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl))
if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl, timeout))
return res;
if (b->events_occurred & MYSQL_WAIT_TIMEOUT)
return -1;
}
}

Expand Down
4 changes: 2 additions & 2 deletions plugins/pvio/pvio_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ my_bool pvio_socket_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo)
ER(CR_CONNECTION_ERROR), cinfo->unix_socket, socket_errno);
goto error;
}
if (pvio_socket_blocking(pvio, 1, 0) == SOCKET_ERROR)
if (pvio_socket_blocking(pvio, MATCH_PVIO_SYNC_OR_ASYNC(pvio), 0) == SOCKET_ERROR)
{
goto error;
}
Expand Down Expand Up @@ -992,7 +992,7 @@ my_bool pvio_socket_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo)
#endif
goto error;
}
if (pvio_socket_blocking(pvio, 1, 0) == SOCKET_ERROR)
if (pvio_socket_blocking(pvio, MATCH_PVIO_SYNC_OR_ASYNC(pvio), 0) == SOCKET_ERROR)
goto error;
}
/* apply timeouts */
Expand Down
6 changes: 3 additions & 3 deletions unittest/libmariadb/async.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ static int test_conc622(MYSQL *my __attribute__((unused)))
if (skip_async)
return SKIP;

for (i=0; i < 100; i++)
for (i=0; i < 10; i++)
{
mysql_init(&mysql);
rc= mysql_options(&mysql, MYSQL_OPT_NONBLOCK, 0);
Expand All @@ -273,7 +273,7 @@ static int test_conc622(MYSQL *my __attribute__((unused)))
mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "myapp");

/* Returns 0 when done, else flag for what to wait for when need to block. */
status= mysql_real_connect_start(&ret, &mysql, "0.0.0.0", username, password, schema, port, socketname, 0);
status= mysql_real_connect_start(&ret, &mysql, "1.2.3.4", username, password, schema, port, socketname, 0);
while (status)
{
status= wait_for_mysql(&mysql, status);
Expand All @@ -288,7 +288,7 @@ static int test_conc622(MYSQL *my __attribute__((unused)))
status= mysql_close_cont(&mysql, status);
}
} else {
diag("Expected error when connection to host '0.0.0.0'");
diag("Expected error when connection to host '1.2.3.4'");
return FAIL;
}
}
Expand Down