From 60bb5a65ae961ae8cddfead620193ac390a05b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Thu, 18 Mar 2021 16:19:52 +0000 Subject: [PATCH 1/2] Closes #3350: Free memory when allocated for specific 'stmt_execute_metadata_t' bind buffers types --- lib/MySQL_Session.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index fad92a13a3..d079cf48b3 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -3885,6 +3885,21 @@ void MySQL_Session::handler_rc0_PROCESSING_STMT_EXECUTE(MySQL_Data_Stream *myds) free(CurrentQuery.stmt_meta->pkt); CurrentQuery.stmt_meta->pkt=NULL; } + + // free for all the buffer types in which we allocate + for (int i = 0; i < CurrentQuery.stmt_meta->num_params; i++) { + enum enum_field_types buffer_type = + CurrentQuery.stmt_meta->binds[i].buffer_type; + + if ( + (buffer_type == MYSQL_TYPE_TIME) || + (buffer_type == MYSQL_TYPE_DATE) || + (buffer_type == MYSQL_TYPE_TIMESTAMP) || + (buffer_type == MYSQL_TYPE_DATETIME) + ) { + free(CurrentQuery.stmt_meta->binds[i].buffer); + } + } } CurrentQuery.mysql_stmt=NULL; } From 93c3fb911d58df9242e4be9744b4aa570db8b842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Thu, 18 Mar 2021 16:21:45 +0000 Subject: [PATCH 2/2] Added helper test file 'repro_test_leak_3350.cpp' used to reproduce issue #3350 --- test/tap/tests/repro_test_leak_3350.cpp | 101 ++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 test/tap/tests/repro_test_leak_3350.cpp diff --git a/test/tap/tests/repro_test_leak_3350.cpp b/test/tap/tests/repro_test_leak_3350.cpp new file mode 100644 index 0000000000..6d51b66367 --- /dev/null +++ b/test/tap/tests/repro_test_leak_3350.cpp @@ -0,0 +1,101 @@ +/** + * @file repro_test_leak_3350.cpp + * @brief Test to reproduce issue #3350. + * @details This test is not meant to be executed, it's just a left as DOC of how to + * reproduce issue #3350. + * @date 2021-03-18 + */ + +#include +#include +#include +#include +#include + +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +const int NUM_EXECUTIONS = 10000; + +int main(int argc, char** argv) { + CommandLine cl; + + plan(NUM_EXECUTIONS); + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return -1; + } + + MYSQL* mysql = mysql_init(NULL); + if (!mysql) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql)); + return exit_status(); + } + + if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, 6033, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql)); + return exit_status(); + } + + std::string select_query { "SELECT DAY(?)" }; + + // Initialize and prepare the statement + MYSQL_STMT* stmt = mysql_stmt_init(mysql); + if (!stmt) { + ok(false, "mysql_stmt_init(), out of memory\n"); + return exit_status(); + } + + MYSQL_TIME ts; + MYSQL_BIND bind; + + if (mysql_stmt_prepare(stmt, select_query.c_str(), strlen(select_query.c_str()))) { + diag("select_query: %s", select_query.c_str()); + ok(false, "mysql_stmt_prepare at line %d failed: %s\n", __LINE__ , mysql_error(mysql)); + mysql_close(mysql); + mysql_library_end(); + return exit_status(); + } + + /* set up input buffers for all 3 parameters */ + bind.buffer_type= MYSQL_TYPE_DATE; + bind.buffer= (char *)&ts; + bind.is_null= 0; + bind.length= 0; + + mysql_stmt_bind_param(stmt, &bind); + + ts.year= 2002; + ts.month= 2; + ts.day= 3; + + ts.hour= 10; + ts.minute= 45; + ts.second= 20; + + // Execute the prepared statement and check that the field count is correct after doing the execute + for (int i = 0; i < NUM_EXECUTIONS; i++) { + + if (mysql_stmt_execute(stmt)) { + ok(false, "mysql_stmt_execute at line %d failed: %s\n", __LINE__ , mysql_stmt_error(stmt)); + } + if (mysql_stmt_fetch(stmt)) { + ok(false, "mysql_stmt_fetch at line %d failed: %s\n", __LINE__ , mysql_stmt_error(stmt)); + } + + int field_count = mysql_stmt_field_count(stmt); + ok(field_count == 1, "Field count should be '1'"); + } + + if (mysql_stmt_close(stmt)) { + ok(false, "mysql_stmt_close at line %d failed: %s\n", __LINE__ , mysql_error(mysql)); + } + + mysql_close(mysql); + + return exit_status(); +}