From c0fea17e2a05d636bbea980be73ba8e67361ee07 Mon Sep 17 00:00:00 2001 From: "hyunghwan.chung" Date: Tue, 21 Jun 2022 17:49:45 +0900 Subject: [PATCH] enhanced mysql_close() and other related parts to prevent memory leaks when terminating an initiated but unestablished connection --- include/ma_context.h | 8 ++++++++ libmariadb/mariadb_lib.c | 25 +++++++++++++++++++++++++ plugins/pvio/pvio_socket.c | 27 +++++++++++++++++++++++---- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/include/ma_context.h b/include/ma_context.h index 2cc40d25..d7743654 100644 --- a/include/ma_context.h +++ b/include/ma_context.h @@ -223,6 +223,14 @@ struct mysql_async_context { struct st_ma_pvio *pvio; void (*suspend_resume_hook)(my_bool suspend, void *user_data); void *suspend_resume_hook_user_data; + + /* If non-NULL, this is a poitner to the result of getaddrinfo() currently + * under traversal in pvio_socket_connect(). It gets reset to NULL when a + * connection has been established to a server. The main objective is to + * free this memory resource in mysql_close() while an initiated connection + * has not been established. */ + struct addrinfo* pending_gai_res; + /* This is used to save the execution contexts so that we can suspend an operation and switch back to the application context, to resume the diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 5006e02b..43ee0ed5 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -1737,7 +1737,18 @@ restart: } if (ma_net_init(net, pvio)) + { + ma_pvio_close(pvio); goto error; + } + + if (mysql->options.extension && mysql->options.extension->async_context && mysql->options.extension->async_context->pvio) + { + /* pvio delegated to mysql->net.pvio by ma_net_init(). + * invalidate the pvio pointer in the async context */ + mysql->options.extension->async_context->pvio = NULL; + } + if (mysql->options.max_allowed_packet) net->max_packet_size= mysql->options.max_allowed_packet; @@ -2343,6 +2354,20 @@ void mysql_close_slow_part(MYSQL *mysql) ma_simple_command(mysql, COM_QUIT,NullS,0,1,0); end_server(mysql); } + /* there is an ongoing async operation */ + else if (mysql->options.extension && mysql->options.extension->async_context) + { + if (mysql->options.extension->async_context->pending_gai_res) + { + freeaddrinfo(mysql->options.extension->async_context->pending_gai_res); + mysql->options.extension->async_context->pending_gai_res = 0; + } + if (mysql->options.extension->async_context->pvio) + { + ma_pvio_close(mysql->options.extension->async_context->pvio); + mysql->options.extension->async_context->pvio = 0; + } + } } static void ma_clear_session_state(MYSQL *mysql) diff --git a/plugins/pvio/pvio_socket.c b/plugins/pvio/pvio_socket.c index c4ca16c2..68f3e32a 100644 --- a/plugins/pvio/pvio_socket.c +++ b/plugins/pvio/pvio_socket.c @@ -732,6 +732,16 @@ int pvio_socket_fast_send(MARIADB_PVIO *pvio) return r; } +static int +pvio_socket_connect_async(MARIADB_PVIO *pvio, + const struct sockaddr *name, uint namelen) +{ + MYSQL *mysql= pvio->mysql; + mysql->options.extension->async_context->pvio= pvio; + pvio_socket_blocking(pvio, 0, 0); + return my_connect_async(pvio, name, namelen, pvio->timeout[PVIO_CONNECT_TIMEOUT]); +} + static int pvio_socket_connect_sync_or_async(MARIADB_PVIO *pvio, const struct sockaddr *name, uint namelen) @@ -742,9 +752,7 @@ pvio_socket_connect_sync_or_async(MARIADB_PVIO *pvio, { /* even if we are not connected yet, application needs to check socket * via mysql_get_socket api call, so we need to assign pvio */ - mysql->options.extension->async_context->pvio= pvio; - pvio_socket_blocking(pvio, 0, 0); - return my_connect_async(pvio, name, namelen, pvio->timeout[PVIO_CONNECT_TIMEOUT]); + return pvio_socket_connect_async(pvio, name, namelen); } return pvio_socket_internal_connect(pvio, name, namelen); @@ -916,7 +924,18 @@ my_bool pvio_socket_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo) } } - rc= pvio_socket_connect_sync_or_async(pvio, save_res->ai_addr, (uint)save_res->ai_addrlen); + if (mysql->options.extension && mysql->options.extension->async_context && + mysql->options.extension->async_context->active) + { + mysql->options.extension->async_context->pending_gai_res = res; + rc= pvio_socket_connect_async(pvio, save_res->ai_addr, (uint)save_res->ai_addrlen); + mysql->options.extension->async_context->pending_gai_res = NULL; + } + else + { + rc= pvio_socket_connect_sync_or_async(pvio, save_res->ai_addr, (uint)save_res->ai_addrlen); + } + if (!rc) { MYSQL *mysql= pvio->mysql;