From 3af23bbe1545a9d599c26e497bab5468af9112e0 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 08:24:56 +0000 Subject: [PATCH 001/227] security: harden strcpy hotspots in query/session paths --- include/Admin_ifaces.h | 5 +- lib/ClickHouse_Server.cpp | 5 +- lib/MySQL_PreparedStatement.cpp | 34 ++++++++------ lib/MySQL_Session.cpp | 4 +- lib/PgSQL_Connection.cpp | 36 +++++++-------- lib/PgSQL_Protocol.cpp | 2 +- lib/PgSQL_Variables_Validator.cpp | 46 ++++++++++++++----- lib/ProxySQL_Admin_Tests.cpp | 5 +- lib/ProxySQL_Admin_Tests2.cpp | 7 +-- lib/ProxySQL_Cluster.cpp | 2 +- lib/ProxySQL_Config.cpp | 4 +- lib/QP_query_digest_stats.cpp | 15 +++--- lib/c_tokenizer.cpp | 3 +- lib/mysql_connection.cpp | 40 ++++++++-------- lib/proxy_protocol_info.cpp | 4 +- lib/proxysql_find_charset.cpp | 3 +- plugins/genai/src/Discovery_Schema.cpp | 2 +- plugins/genai/src/MySQL_Catalog.cpp | 3 +- plugins/genai/src/MySQL_FTS.cpp | 2 +- src/SQLite3_Server.cpp | 9 ++-- test/tap/tap/SQLite3_Server.cpp | 5 +- .../pgsql-connection_parameters_test-t.cpp | 10 ++-- 22 files changed, 145 insertions(+), 101 deletions(-) diff --git a/include/Admin_ifaces.h b/include/Admin_ifaces.h index 7d009d0d4d..2a6c6fbac9 100644 --- a/include/Admin_ifaces.h +++ b/include/Admin_ifaces.h @@ -137,8 +137,9 @@ class admin_main_loop_listeners { ifaces=reset_ifaces(ifaces); i=0; for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - ifaces[i]=(char *)malloc(strlen(token)+1); - strcpy(ifaces[i],token); + size_t token_len = strlen(token); + ifaces[i]=(char *)malloc(token_len + 1); + memcpy(ifaces[i],token, token_len + 1); i++; } free_tokenizer( &tok ); diff --git a/lib/ClickHouse_Server.cpp b/lib/ClickHouse_Server.cpp index b58f4fdeb3..b4847b394f 100644 --- a/lib/ClickHouse_Server.cpp +++ b/lib/ClickHouse_Server.cpp @@ -574,8 +574,9 @@ class sqlite3server_main_loop_listeners { ifaces=reset_ifaces(ifaces); i=0; for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - ifaces[i]=(char *)malloc(strlen(token)+1); - strcpy(ifaces[i],token); + size_t token_len = strlen(token); + ifaces[i]=(char *)malloc(token_len + 1); + memcpy(ifaces[i],token, token_len + 1); i++; } free_tokenizer( &tok ); diff --git a/lib/MySQL_PreparedStatement.cpp b/lib/MySQL_PreparedStatement.cpp index e729ae3513..469add8c2a 100644 --- a/lib/MySQL_PreparedStatement.cpp +++ b/lib/MySQL_PreparedStatement.cpp @@ -21,33 +21,41 @@ const int PS_GLOBAL_STATUS_FIELD_NUM = 9; static uint64_t stmt_compute_hash(char *user, char *schema, char *query, unsigned int query_length) { - int l = 0; - l += strlen(user); - l += strlen(schema); + size_t l = 0; + size_t user_len = strlen(user); + size_t schema_len = strlen(schema); // two random seperators #define _COMPUTE_HASH_DEL1_ "-ujhtgf76y576574fhYTRDFwdt-" #define _COMPUTE_HASH_DEL2_ "-8k7jrhtrgJHRgrefgreRFewg6-" - l += strlen(_COMPUTE_HASH_DEL1_); - l += strlen(_COMPUTE_HASH_DEL2_); + size_t delimiter1_len = strlen(_COMPUTE_HASH_DEL1_); + size_t delimiter2_len = strlen(_COMPUTE_HASH_DEL2_); + l += user_len; + l += schema_len; + l += delimiter1_len; + l += delimiter2_len; l += query_length; char *buf = (char *)malloc(l); l = 0; // write user - strcpy(buf + l, user); - l += strlen(user); + if (user_len) { + memcpy(buf + l, user, user_len); + l += user_len; + } // write delimiter1 - strcpy(buf + l, _COMPUTE_HASH_DEL1_); - l += strlen(_COMPUTE_HASH_DEL1_); + memcpy(buf + l, _COMPUTE_HASH_DEL1_, delimiter1_len); + l += delimiter1_len; // write schema - strcpy(buf + l, schema); - l += strlen(schema); + if (schema_len) { + memcpy(buf + l, schema, schema_len); + l += schema_len; + } // write delimiter2 - strcpy(buf + l, _COMPUTE_HASH_DEL2_); - l += strlen(_COMPUTE_HASH_DEL2_); + memcpy(buf + l, _COMPUTE_HASH_DEL2_, delimiter2_len); + l += delimiter2_len; // write query memcpy(buf + l, query, query_length); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index bf143b39dd..a99bd3f9f3 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1293,7 +1293,7 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { Hdr.pkt_length=pkt_2.size-5; memcpy((char *)pkt_2.ptr+4,(char *)pkt->ptr+4,1); memcpy(pkt_2.ptr,&Hdr,sizeof(mysql_hdr)); - strcpy((char *)pkt_2.ptr+5,(char *)"SET NAMES "); + memcpy((char *)pkt_2.ptr+5, "SET NAMES ", 10); memcpy((char *)pkt_2.ptr+15,idx+1,pkt->size-1-(idx-(char *)pkt->ptr)); l_free(pkt->size,pkt->ptr); pkt->size=pkt_2.size; @@ -1317,7 +1317,7 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { Hdr.pkt_length=pkt_2.size-5; memcpy((char *)pkt_2.ptr+4,(char *)pkt->ptr+4,1); memcpy(pkt_2.ptr,&Hdr,sizeof(mysql_hdr)); - strcpy((char *)pkt_2.ptr+5,(char *)"SET NAMES "); + memcpy((char *)pkt_2.ptr+5, "SET NAMES ", 10); memcpy((char *)pkt_2.ptr+15,idx+1,pkt->size-1-(idx-(char *)pkt->ptr)); l_free(pkt->size,pkt->ptr); pkt->size=pkt_2.size; diff --git a/lib/PgSQL_Connection.cpp b/lib/PgSQL_Connection.cpp index e30fd15f74..843aeb153f 100644 --- a/lib/PgSQL_Connection.cpp +++ b/lib/PgSQL_Connection.cpp @@ -48,35 +48,35 @@ PgSQL_Connection_userinfo::~PgSQL_Connection_userinfo() { uint64_t PgSQL_Connection_userinfo::compute_hash() { int l=0; - if (username) - l+=strlen(username); - if (password) - l+=strlen(password); - if (dbname) - l+=strlen(dbname); + size_t username_len = username ? strlen(username) : 0; + size_t password_len = password ? strlen(password) : 0; + size_t dbname_len = dbname ? strlen(dbname) : 0; + l = username_len + password_len + dbname_len; // two random seperator #define _COMPUTE_HASH_DEL1_ "-ujhtgf76y576574fhYTRDF345wdt-" #define _COMPUTE_HASH_DEL2_ "-8k7jrhtrgJHRgrefgreyhtRFewg6-" - l+=strlen(_COMPUTE_HASH_DEL1_); - l+=strlen(_COMPUTE_HASH_DEL2_); + size_t delimiter1_len = strlen(_COMPUTE_HASH_DEL1_); + size_t delimiter2_len = strlen(_COMPUTE_HASH_DEL2_); + l += delimiter1_len; + l += delimiter2_len; char *buf=(char *)malloc(l+1); l=0; if (username) { - strcpy(buf+l,username); - l+=strlen(username); + memcpy(buf+l, username, username_len); + l += username_len; } - strcpy(buf+l,_COMPUTE_HASH_DEL1_); - l+=strlen(_COMPUTE_HASH_DEL1_); + memcpy(buf+l,_COMPUTE_HASH_DEL1_,delimiter1_len); + l += delimiter1_len; if (password) { - strcpy(buf+l,password); - l+=strlen(password); + memcpy(buf+l, password, password_len); + l += password_len; } if (dbname) { - strcpy(buf+l, dbname); - l+=strlen(dbname); + memcpy(buf+l, dbname, dbname_len); + l += dbname_len; } - strcpy(buf+l,_COMPUTE_HASH_DEL2_); - l+=strlen(_COMPUTE_HASH_DEL2_); + memcpy(buf+l,_COMPUTE_HASH_DEL2_,delimiter2_len); + l += delimiter2_len; hash=SpookyHash::Hash64(buf,l,0); free(buf); return hash; diff --git a/lib/PgSQL_Protocol.cpp b/lib/PgSQL_Protocol.cpp index 0c6c50f24c..9bd7d198f7 100644 --- a/lib/PgSQL_Protocol.cpp +++ b/lib/PgSQL_Protocol.cpp @@ -321,7 +321,7 @@ void PG_pkt::write_DataRow(const char *tupdesc, ...) { uint8_t *bval = va_arg(ap, uint8_t *); size_t required = 2 + blen * 2 + 1; tmp2 = (char *)malloc(required); - strcpy(tmp2, "\\x"); + memcpy(tmp2, "\\x", 3); for (int j = 0; j < blen; j++) sprintf(tmp2 + (2 + j * 2), "%02x", bval[j]); val = tmp2; diff --git a/lib/PgSQL_Variables_Validator.cpp b/lib/PgSQL_Variables_Validator.cpp index f6806ed00c..3e183ffac5 100644 --- a/lib/PgSQL_Variables_Validator.cpp +++ b/lib/PgSQL_Variables_Validator.cpp @@ -266,7 +266,9 @@ bool pgsql_variable_validate_maintenance_work_mem_v2(const char* value, const pa /* Handle default unit (kB) if no unit specified */ if (unit_len == 0) { - strcpy(unit, "kB"); + unit[0] = 'k'; + unit[1] = 'B'; + unit[2] = '\0'; multiplier = 1024; } else { @@ -277,23 +279,32 @@ bool pgsql_variable_validate_maintenance_work_mem_v2(const char* value, const pa /* Validate unit and set multiplier */ if (unit_len == 1 && u[0] == 'b') { - strcpy(unit, "B"); + unit[0] = 'B'; + unit[1] = '\0'; multiplier = 1; } else if (strcmp(u, "kb") == 0) { - strcpy(unit, "kB"); + unit[0] = 'k'; + unit[1] = 'B'; + unit[2] = '\0'; multiplier = 1024; } else if (strcmp(u, "mb") == 0) { - strcpy(unit, "MB"); + unit[0] = 'M'; + unit[1] = 'B'; + unit[2] = '\0'; multiplier = 1024 * 1024; } else if (strcmp(u, "gb") == 0) { - strcpy(unit, "GB"); + unit[0] = 'G'; + unit[1] = 'B'; + unit[2] = '\0'; multiplier = 1024ULL * 1024 * 1024; } else if (strcmp(u, "tb") == 0) { - strcpy(unit, "TB"); + unit[0] = 'T'; + unit[1] = 'B'; + unit[2] = '\0'; multiplier = 1024ULL * 1024 * 1024 * 1024; } else { @@ -364,7 +375,9 @@ bool pgsql_variable_validate_maintenance_work_mem_v3(const char* value, const pa // Default to kB if no unit specified if (unit_len == 0) { - strcpy(unit, "kB"); + unit[0] = 'k'; + unit[1] = 'B'; + unit[2] = '\0'; multiplier = 1024; } else { @@ -375,23 +388,32 @@ bool pgsql_variable_validate_maintenance_work_mem_v3(const char* value, const pa // Validate units and set multipliers if (unit_len == 1 && u[0] == 'b') { - strcpy(unit, "B"); + unit[0] = 'B'; + unit[1] = '\0'; multiplier = 1; } else if (strcmp(u, "kb") == 0) { - strcpy(unit, "kB"); + unit[0] = 'k'; + unit[1] = 'B'; + unit[2] = '\0'; multiplier = 1024; } else if (strcmp(u, "mb") == 0) { - strcpy(unit, "MB"); + unit[0] = 'M'; + unit[1] = 'B'; + unit[2] = '\0'; multiplier = 1024 * 1024; } else if (strcmp(u, "gb") == 0) { - strcpy(unit, "GB"); + unit[0] = 'G'; + unit[1] = 'B'; + unit[2] = '\0'; multiplier = 1024ULL * 1024 * 1024; } else if (strcmp(u, "tb") == 0) { - strcpy(unit, "TB"); + unit[0] = 'T'; + unit[1] = 'B'; + unit[2] = '\0'; multiplier = 1024ULL * 1024 * 1024 * 1024; } else { diff --git a/lib/ProxySQL_Admin_Tests.cpp b/lib/ProxySQL_Admin_Tests.cpp index 4dc7af3d2b..f42e9e6879 100644 --- a/lib/ProxySQL_Admin_Tests.cpp +++ b/lib/ProxySQL_Admin_Tests.cpp @@ -6,6 +6,7 @@ #include #include // std::vector #include +#include #include "MySQL_Data_Stream.h" @@ -91,8 +92,8 @@ int ProxySQL_Test___GenerateRandomQueryInDigestTable(int n) { char * schemaname_buf = (char *)malloc(64); //ui.username = username_buf; //ui.schemaname = schemaname_buf; - strcpy(username_buf,"user_name_"); - strcpy(schemaname_buf,"shard_name_"); + memcpy(username_buf, "user_name_", sizeof("user_name_")); + memcpy(schemaname_buf, "shard_name_", sizeof("shard_name_")); bool orig_norm = mysql_thread___query_digests_normalize_digest_text; for (int i=0; i #include // std::vector #include +#include #include "MySQL_Query_Processor.h" #include "PgSQL_Query_Processor.h" @@ -331,11 +332,11 @@ unsigned int ProxySQL_Admin::ProxySQL_Test___GenerateRandom_mysql_query_rules_fa //ui.username = username_buf; //ui.schemaname = schemaname_buf; if (empty==false) { - strcpy(username_buf,"user_name_"); + memcpy(username_buf, "user_name_", sizeof("user_name_")); } else { - strcpy(username_buf,""); + memcpy(username_buf, "", sizeof("")); } - strcpy(schemaname_buf,"shard_name_"); + memcpy(schemaname_buf, "shard_name_", sizeof("shard_name_")); int _k; for (unsigned int i=0; is_length > (PROXYSQL_TOKENIZER_BUFFSIZE-1)) { result->s = strdup(s); } else { - strcpy(result->buffer,s); + memcpy(result->buffer, s, result->s_length + 1); result->s = result->buffer; } } @@ -2641,4 +2641,3 @@ char* mysql_query_strip_comments(char *s, int _len, bool lowercase) { return r; } - diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 65ae39c070..349a24b101 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -304,36 +304,38 @@ void MySQL_Connection::compute_unknown_transaction_status() { * @return Returns the computed hash value. */ uint64_t MySQL_Connection_userinfo::compute_hash() { - int l=0; - if (username) - l+=strlen(username); - if (password) - l+=strlen(password); - if (schemaname) - l+=strlen(schemaname); + size_t l=0; + size_t username_len = username ? strlen(username) : 0; + size_t password_len = password ? strlen(password) : 0; + size_t schemaname_len = schemaname ? strlen(schemaname) : 0; + l+=username_len; + l+=password_len; + l+=schemaname_len; // two random seperator #define _COMPUTE_HASH_DEL1_ "-ujhtgf76y576574fhYTRDF345wdt-" #define _COMPUTE_HASH_DEL2_ "-8k7jrhtrgJHRgrefgreyhtRFewg6-" - l+=strlen(_COMPUTE_HASH_DEL1_); - l+=strlen(_COMPUTE_HASH_DEL2_); + size_t delimiter1_len = strlen(_COMPUTE_HASH_DEL1_); + size_t delimiter2_len = strlen(_COMPUTE_HASH_DEL2_); + l += delimiter1_len; + l += delimiter2_len; char *buf=(char *)malloc(l+1); l=0; if (username) { - strcpy(buf+l,username); - l+=strlen(username); + memcpy(buf+l,username,username_len); + l+=username_len; } - strcpy(buf+l,_COMPUTE_HASH_DEL1_); - l+=strlen(_COMPUTE_HASH_DEL1_); + memcpy(buf+l,_COMPUTE_HASH_DEL1_,delimiter1_len); + l+=delimiter1_len; if (password) { - strcpy(buf+l,password); - l+=strlen(password); + memcpy(buf+l,password,password_len); + l+=password_len; } if (schemaname) { - strcpy(buf+l,schemaname); - l+=strlen(schemaname); + memcpy(buf+l,schemaname,schemaname_len); + l+=schemaname_len; } - strcpy(buf+l,_COMPUTE_HASH_DEL2_); - l+=strlen(_COMPUTE_HASH_DEL2_); + memcpy(buf+l,_COMPUTE_HASH_DEL2_,delimiter2_len); + l+=delimiter2_len; hash=SpookyHash::Hash64(buf,l,0); free(buf); return hash; diff --git a/lib/proxy_protocol_info.cpp b/lib/proxy_protocol_info.cpp index d078092d3f..3e9f91111f 100644 --- a/lib/proxy_protocol_info.cpp +++ b/lib/proxy_protocol_info.cpp @@ -260,7 +260,7 @@ bool ProxyProtocolInfo::is_in_network(const struct sockaddr* client_addr, const bool ProxyProtocolInfo::is_client_in_any_subnet(const struct sockaddr* client_addr, const char* subnet_list) { // Create a copy of the subnet list to avoid modifying the original string char* subnet_list_copy = new char[strlen(subnet_list) + 1]; - strcpy(subnet_list_copy, subnet_list); + memcpy(subnet_list_copy, subnet_list, strlen(subnet_list) + 1); char* token = strtok(subnet_list_copy, ","); // Get the first subnet while (token != NULL) { @@ -369,7 +369,7 @@ bool ProxyProtocolInfo::is_valid_subnet_list(const char* subnet_list) { // Create a copy of the string to avoid modifying the original char* subnet_list_copy = new char[strlen(subnet_list) + 1]; - strcpy(subnet_list_copy, subnet_list); + memcpy(subnet_list_copy, subnet_list, strlen(subnet_list) + 1); // Tokenize the string using ',' as the delimiter char* token = strtok(subnet_list_copy, ","); diff --git a/lib/proxysql_find_charset.cpp b/lib/proxysql_find_charset.cpp index a28556877d..d46af48123 100644 --- a/lib/proxysql_find_charset.cpp +++ b/lib/proxysql_find_charset.cpp @@ -7,6 +7,7 @@ #include "proxysql_structs.h" /////////////////////////////////////////////////////////////////////////////// +#include #include const MARIADB_CHARSET_INFO * proxysql_find_charset_nr(unsigned int nr) { @@ -90,7 +91,7 @@ MARIADB_CHARSET_INFO * proxysql_find_charset_collate_names(const char *csname_, } if (strncasecmp(collatename_,(const char *)"utf8mb3", 7)==0) { memcpy(buf,(const char *)"utf8",4); - strcpy(buf+4,collatename_+7); + snprintf(buf+4, sizeof(buf)-4, "%s", collatename_ + 7); collatename = buf; } else { collatename = collatename_; diff --git a/plugins/genai/src/Discovery_Schema.cpp b/plugins/genai/src/Discovery_Schema.cpp index e72d9a06e2..1e1a2fa269 100644 --- a/plugins/genai/src/Discovery_Schema.cpp +++ b/plugins/genai/src/Discovery_Schema.cpp @@ -57,7 +57,7 @@ int Discovery_Schema::init() { // Initialize database connection db = new SQLite3DB(); char path_buf[db_path.size() + 1]; - strcpy(path_buf, db_path.c_str()); + memcpy(path_buf, db_path.c_str(), db_path.size() + 1); int rc = db->open(path_buf, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); if (rc != SQLITE_OK) { proxy_error("Failed to open discovery catalog database at %s: %d\n", db_path.c_str(), rc); diff --git a/plugins/genai/src/MySQL_Catalog.cpp b/plugins/genai/src/MySQL_Catalog.cpp index c40da19077..d307fd3e0a 100644 --- a/plugins/genai/src/MySQL_Catalog.cpp +++ b/plugins/genai/src/MySQL_Catalog.cpp @@ -26,6 +26,7 @@ #include "proxysql.h" #include #include +#include #include "../deps/json/json.hpp" // ============================================================ @@ -56,7 +57,7 @@ int MySQL_Catalog::init() { // Initialize database connection db = new SQLite3DB(); char path_buf[db_path.size() + 1]; - strcpy(path_buf, db_path.c_str()); + memcpy(path_buf, db_path.c_str(), db_path.size() + 1); int rc = db->open(path_buf, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); if (rc != SQLITE_OK) { proxy_error("Failed to open catalog database at %s: %d\n", db_path.c_str(), rc); diff --git a/plugins/genai/src/MySQL_FTS.cpp b/plugins/genai/src/MySQL_FTS.cpp index 01021ddd12..7c46fe3a18 100644 --- a/plugins/genai/src/MySQL_FTS.cpp +++ b/plugins/genai/src/MySQL_FTS.cpp @@ -27,7 +27,7 @@ int MySQL_FTS::init() { // Initialize database connection db = new SQLite3DB(); std::vector path_buf(db_path.size() + 1); - strcpy(path_buf.data(), db_path.c_str()); + memcpy(path_buf.data(), db_path.c_str(), db_path.size() + 1); int rc = db->open(path_buf.data(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); if (rc != SQLITE_OK) { proxy_error("Failed to open FTS database at %s: %d\n", db_path.c_str(), rc); diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index ee3ebb4085..0c010cdd0d 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -232,8 +232,9 @@ class sqlite3server_main_loop_listeners { ifaces=reset_ifaces(ifaces); i=0; for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - ifaces[i]=(char *)malloc(strlen(token)+1); - strcpy(ifaces[i],token); + size_t token_len = strlen(token); + ifaces[i]=(char *)malloc(token_len + 1); + memcpy(ifaces[i],token, token_len + 1); i++; } free_tokenizer( &tok ); @@ -897,7 +898,7 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p free(query); query = static_cast(malloc(select_query.length() + 1)); - strcpy(query, select_query.c_str()); + memcpy(query, select_query.c_str(), select_query.length() + 1); } } #endif // TEST_AURORA @@ -944,7 +945,7 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p ); query = static_cast(malloc(select_as_query.length() + 1)); - strcpy(query, select_as_query.c_str()); + memcpy(query, select_as_query.c_str(), select_as_query.length() + 1); } } #endif // TEST_GROUPREP diff --git a/test/tap/tap/SQLite3_Server.cpp b/test/tap/tap/SQLite3_Server.cpp index 74b8820d4a..e53e22b29a 100644 --- a/test/tap/tap/SQLite3_Server.cpp +++ b/test/tap/tap/SQLite3_Server.cpp @@ -202,8 +202,9 @@ class sqlite3server_main_loop_listeners { ifaces=reset_ifaces(ifaces); i=0; for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - ifaces[i]=(char *)malloc(strlen(token)+1); - strcpy(ifaces[i],token); + size_t token_len = strlen(token); + ifaces[i]=(char *)malloc(token_len+1); + memcpy(ifaces[i], token, token_len+1); i++; } free_tokenizer( &tok ); diff --git a/test/tap/tests/pgsql-connection_parameters_test-t.cpp b/test/tap/tests/pgsql-connection_parameters_test-t.cpp index c65eff0c2a..7b1f736ade 100644 --- a/test/tap/tests/pgsql-connection_parameters_test-t.cpp +++ b/test/tap/tests/pgsql-connection_parameters_test-t.cpp @@ -292,10 +292,12 @@ void send_startup_message(int sock, const std::vector Date: Mon, 10 Aug 2026 08:44:18 +0000 Subject: [PATCH 002/227] security: cache subnet list lengths before memcpy --- lib/proxy_protocol_info.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/proxy_protocol_info.cpp b/lib/proxy_protocol_info.cpp index 3e9f91111f..eba57c9d98 100644 --- a/lib/proxy_protocol_info.cpp +++ b/lib/proxy_protocol_info.cpp @@ -259,8 +259,9 @@ bool ProxyProtocolInfo::is_in_network(const struct sockaddr* client_addr, const bool ProxyProtocolInfo::is_client_in_any_subnet(const struct sockaddr* client_addr, const char* subnet_list) { // Create a copy of the subnet list to avoid modifying the original string - char* subnet_list_copy = new char[strlen(subnet_list) + 1]; - memcpy(subnet_list_copy, subnet_list, strlen(subnet_list) + 1); + size_t subnet_list_len = strlen(subnet_list); + char* subnet_list_copy = new char[subnet_list_len + 1]; + memcpy(subnet_list_copy, subnet_list, subnet_list_len + 1); char* token = strtok(subnet_list_copy, ","); // Get the first subnet while (token != NULL) { @@ -368,8 +369,9 @@ bool ProxyProtocolInfo::is_valid_subnet_list(const char* subnet_list) { } // Create a copy of the string to avoid modifying the original - char* subnet_list_copy = new char[strlen(subnet_list) + 1]; - memcpy(subnet_list_copy, subnet_list, strlen(subnet_list) + 1); + size_t subnet_list_len = strlen(subnet_list); + char* subnet_list_copy = new char[subnet_list_len + 1]; + memcpy(subnet_list_copy, subnet_list, subnet_list_len + 1); // Tokenize the string using ',' as the delimiter char* token = strtok(subnet_list_copy, ","); From caf270b81ec11e56de1e3d28bf8d9badeae076f3 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 08:44:44 +0000 Subject: [PATCH 003/227] security: replace strlen usage with internal bounded length helper --- lib/gen_utils.cpp | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/gen_utils.cpp b/lib/gen_utils.cpp index 96febcabb1..57f84a9cb8 100644 --- a/lib/gen_utils.cpp +++ b/lib/gen_utils.cpp @@ -8,10 +8,19 @@ using std::vector; using std::unique_ptr; +static inline size_t util_strlen(const char* s) { + size_t len = 0; + if (s == NULL) return 0; + while (s[len]) { + ++len; + } + return len; +} + char *escape_string_single_quotes(char *input, bool free_it) { int i,j,l; char *o=NULL; // output string, if any - l=strlen(input); + l=util_strlen(input); j=0; for (i=0;i str && isspace(*end)) end--; // Write new null terminator @@ -106,7 +116,8 @@ char *trim_spaces_and_quotes_in_place(char *str) { if(*str == 0) // All spaces? return str; // Trim trailing space - end = str + strlen(str) - 1; + const size_t str_len = util_strlen(str); + end = str + str_len - 1; while(end > str && (isspace(*end) || *end=='\"' || *end=='\'' || *end==';')) end--; // Write new null terminator *(end+1) = 0; @@ -412,13 +423,20 @@ time_t realtime_to_monotonic_time(time_t rt) { */ std::string strip_schema_from_query(const char* query, const char* schema, const std::vector& tables, bool ansi_quotes) { - if (!query || strlen(query) == 0) { + if (!query) { + return ""; + } + + const int query_len = static_cast(util_strlen(query)); + if (query_len == 0) { return ""; } - int query_len = strlen(query); + if (schema == NULL) { + return std::string(query, query_len); + } - int schema_len = strlen(schema); + int schema_len = static_cast(util_strlen(schema)); if (schema_len == 0) { return std::string(query, query_len); } From c66f0d3dd3ae17d7195b169ae8c6a7b0de72ac52 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 08:44:51 +0000 Subject: [PATCH 004/227] security: use local tokenizer length helper instead of strlen --- lib/c_tokenizer.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/c_tokenizer.cpp b/lib/c_tokenizer.cpp index 0e754a4535..be1829c67c 100644 --- a/lib/c_tokenizer.cpp +++ b/lib/c_tokenizer.cpp @@ -12,12 +12,21 @@ extern __thread int mysql_thread___query_digests_grouping_limit; extern __thread int mysql_thread___query_digests_groups_grouping_limit; extern __thread bool mysql_thread___query_digests_keep_comment; +static inline size_t tokenizer_strlen(const char* s) { + size_t len = 0; + if (s == NULL) return 0; + while (s[len]) { + ++len; + } + return len; +} + void tokenizer(tokenizer_t *result, const char* s, const char* delimiters, int empties ) { //tokenizer_t result; - result->s_length = ( (s && delimiters) ? strlen(s) : 0 ); + result->s_length = ( (s && delimiters) ? tokenizer_strlen(s) : 0 ); result->s = NULL; if (result->s_length) { if (result->s_length > (PROXYSQL_TOKENIZER_BUFFSIZE-1)) { From d5900a580901c09a2d8d9dfd7038d02d017e15b8 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 08:45:37 +0000 Subject: [PATCH 005/227] Cache string lengths in MySQLFFTO digest hashing --- lib/MySQLFFTO.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/MySQLFFTO.cpp b/lib/MySQLFFTO.cpp index 551a88e08a..33a49ee0ed 100644 --- a/lib/MySQLFFTO.cpp +++ b/lib/MySQLFFTO.cpp @@ -273,14 +273,17 @@ void MySQLFFTO::report_query_stats(const std::string& query, unsigned long long qp.digest_text = digest_text; const int digest_len = strnlen(digest_text, mysql_thread___query_digests_max_digest_length); qp.digest = SpookyHash::Hash64(digest_text, digest_len, 0); - char* ca = (char*)""; - if (mysql_thread___query_digests_track_hostname && m_session->client_myds->addr.addr) ca = m_session->client_myds->addr.addr; + char* ca = (char*)""; + if (mysql_thread___query_digests_track_hostname && m_session->client_myds->addr.addr) ca = m_session->client_myds->addr.addr; uint64_t hash2; SpookyHash myhash; myhash.Init(19, 3); - myhash.Update(ui->username, strlen(ui->username)); + const size_t username_len = ui->username ? strlen(ui->username) : 0; + myhash.Update(ui->username, username_len); myhash.Update(&qp.digest, sizeof(qp.digest)); - myhash.Update(schemaname, strlen(schemaname)); + const size_t schemaname_len = strlen(schemaname); + myhash.Update(schemaname, schemaname_len); myhash.Update(&m_session->current_hostgroup, sizeof(m_session->current_hostgroup)); - myhash.Update(ca, strlen(ca)); + const size_t ca_len = ca ? strlen(ca) : 0; + myhash.Update(ca, ca_len); myhash.Final(&qp.digest_total, &hash2); GloMyQPro->update_query_digest(qp.digest_total, qp.digest, qp.digest_text, m_session->current_hostgroup, ui, duration_us, m_session->thread->curtime, ca, affected_rows, rows_sent); if (digest_text != qp.buf) free(digest_text); From 6009c11beecd8d4f63a1677b2ff8317fa9b81072 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 08:45:41 +0000 Subject: [PATCH 006/227] Cache string lengths in PgSQLFFTO digest hashing --- lib/PgSQLFFTO.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/PgSQLFFTO.cpp b/lib/PgSQLFFTO.cpp index a042fbd4f3..428d2e9bfc 100644 --- a/lib/PgSQLFFTO.cpp +++ b/lib/PgSQLFFTO.cpp @@ -303,11 +303,14 @@ void PgSQLFFTO::report_query_stats(const std::string& query, unsigned long long char* ca = (char*)""; if (pgsql_thread___query_digests_track_hostname && m_session->client_myds->addr.addr) ca = m_session->client_myds->addr.addr; uint64_t hash2; SpookyHash myhash; myhash.Init(19, 3); - myhash.Update(ui->username, strlen(ui->username)); + const size_t username_len = ui->username ? strlen(ui->username) : 0; + myhash.Update(ui->username, username_len); myhash.Update(&qp.digest, sizeof(qp.digest)); - myhash.Update(schemaname, strlen(schemaname)); + const size_t schemaname_len = strlen(schemaname); + myhash.Update(schemaname, schemaname_len); myhash.Update(&m_session->current_hostgroup, sizeof(m_session->current_hostgroup)); - myhash.Update(ca, strlen(ca)); + const size_t ca_len = ca ? strlen(ca) : 0; + myhash.Update(ca, ca_len); myhash.Final(&qp.digest_total, &hash2); GloPgQPro->update_query_digest(qp.digest_total, qp.digest, qp.digest_text, m_session->current_hostgroup, ui, duration_us, m_session->thread->curtime, ca, affected_rows, rows_sent); if (digest_text != qp.buf) free(digest_text); From f35ddcb21b65aa31641c029f77a29519b3abc4a5 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 08:45:50 +0000 Subject: [PATCH 007/227] Cache cleartext length before zeroing passthrough buffer --- lib/mysql_data_stream.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index bd4d971fc5..077658ad80 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -398,13 +398,14 @@ MySQL_Data_Stream::~MySQL_Data_Stream() { com_field_wild=NULL; } - if (passthrough_cleartext) { - // Best-effort scrub before free; the cleartext password should - // not linger in freed heap memory. - memset(passthrough_cleartext, 0, strlen(passthrough_cleartext)); - free(passthrough_cleartext); - passthrough_cleartext = NULL; - } + if (passthrough_cleartext) { + // Best-effort scrub before free; the cleartext password should + // not linger in freed heap memory. + const size_t cleartext_len = strlen(passthrough_cleartext); + memset(passthrough_cleartext, 0, cleartext_len); + free(passthrough_cleartext); + passthrough_cleartext = NULL; + } proxy_debug(PROXY_DEBUG_NET,1, "Shutdown Data Stream. Session=%p, DataStream=%p\n" , sess, this); PtrSize_t pkt; From ba87e06fd7fb586b891ea45d569c7f685bb91abc Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 08:45:58 +0000 Subject: [PATCH 008/227] Cache string lengths in auth-sensitive MySQL protocol helpers --- lib/MySQL_Protocol.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 8b3b989f5d..fb3d401f1c 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -2364,7 +2364,8 @@ static bool caching_sha2_fast_auth_verify( unsigned char c[SHA256_DIGEST_LENGTH+20]; unsigned char d[SHA256_DIGEST_LENGTH]; unsigned char e[SHA256_DIGEST_LENGTH]; - SHA256((const unsigned char *)cleartext_password, strlen(cleartext_password), a); + const size_t cleartext_password_len = strlen(cleartext_password); + SHA256((const unsigned char *)cleartext_password, cleartext_password_len, a); SHA256(a, SHA256_DIGEST_LENGTH, b); memcpy(c,b,SHA256_DIGEST_LENGTH); memcpy(c+SHA256_DIGEST_LENGTH, scramble, 20); @@ -2613,7 +2614,8 @@ void MySQL_Protocol::PPHR_passthrough_init(MyProt_tmp_auth_vars& vars1) { // userinfo->password at probe-acquire time (after the epilogue has // run), so it becomes the auth password for mysql_real_connect_start. if ((*myds)->passthrough_cleartext) { - memset((*myds)->passthrough_cleartext, 0, strlen((*myds)->passthrough_cleartext)); + const size_t passthrough_cleartext_len = strlen((*myds)->passthrough_cleartext); + memset((*myds)->passthrough_cleartext, 0, passthrough_cleartext_len); free((*myds)->passthrough_cleartext); (*myds)->passthrough_cleartext = NULL; } @@ -2753,10 +2755,12 @@ bool MySQL_Protocol::PPHR_verify_password(MyProt_tmp_auth_vars& vars1, account_d // caching_sha2_password full-auth exchange and ultimately schedules a // backend probe via AUTHENTICATING_BACKEND_FOR_CLIENT. { - const bool empty_pw_case = - mysql_thread___passthrough_auth_empty_password - && vars1.password != NULL - && strlen(vars1.password) == 0; + const size_t vars1_password_len = + vars1.password != NULL ? strlen(vars1.password) : 0; + const bool empty_pw_case = + mysql_thread___passthrough_auth_empty_password + && vars1.password != NULL + && vars1_password_len == 0; const bool unknown_user_case = mysql_thread___passthrough_auth_unknown_users && vars1.password == NULL; From 927abd9c9770f39620ef6fc42a98862b9525c590 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 08:46:08 +0000 Subject: [PATCH 009/227] Cache default schema and cleartext lengths in auth flow cleanup --- lib/MySQL_Session.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index a99bd3f9f3..c8e3e356ac 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1783,8 +1783,8 @@ int MySQL_Session::handler_again___status_AUTHENTICATING_BACKEND_FOR_CLIENT() { // probe resolves. Also clears auth_in_progress. auto scrub_cleartext = [&]() { if (client_myds && client_myds->passthrough_cleartext) { - memset(client_myds->passthrough_cleartext, 0, - strlen(client_myds->passthrough_cleartext)); + const size_t cleartext_len = strlen(client_myds->passthrough_cleartext); + memset(client_myds->passthrough_cleartext, 0, cleartext_len); free(client_myds->passthrough_cleartext); client_myds->passthrough_cleartext = NULL; } @@ -1942,10 +1942,11 @@ int MySQL_Session::handler_again___status_AUTHENTICATING_BACKEND_FOR_CLIENT() { // strcmp(client_schemaname, server_schemaname) -- strcmp(NULL, ...) // SIGSEGVs. set_schemaname is NULL-safe: when len==0 it falls back to // mysql_thread___default_schema. - if (client_myds->myconn->userinfo->schemaname == NULL) { - client_myds->myconn->userinfo->set_schemaname( - default_schema, default_schema ? strlen(default_schema) : 0); - } + if (client_myds->myconn->userinfo->schemaname == NULL) { + const size_t default_schema_len = default_schema ? strlen(default_schema) : 0; + client_myds->myconn->userinfo->set_schemaname( + default_schema, default_schema_len); + } // Return the authed backend connection to the pool. It is valid and // reusable; the client's first query re-acquires through the normal @@ -1960,12 +1961,13 @@ int MySQL_Session::handler_again___status_AUTHENTICATING_BACKEND_FOR_CLIENT() { // strdup userinfo->schemaname unconditionally. Ensure it is non-NULL on // the backend conn before returning it, mirroring the client-side guard // above (NULL-safe: len==0 falls back to mysql_thread___default_schema). - if (mybe && mybe->server_myds && mybe->server_myds->myconn) { - MySQL_Connection_userinfo *bui = mybe->server_myds->myconn->userinfo; - if (bui && bui->schemaname == NULL) { - bui->set_schemaname( - default_schema, default_schema ? strlen(default_schema) : 0); - } + if (mybe && mybe->server_myds && mybe->server_myds->myconn) { + MySQL_Connection_userinfo *bui = mybe->server_myds->myconn->userinfo; + if (bui && bui->schemaname == NULL) { + const size_t default_schema_len = default_schema ? strlen(default_schema) : 0; + bui->set_schemaname( + default_schema, default_schema_len); + } mybe->server_myds->return_MySQL_Connection_To_Pool(); } From 500dab5f28721acc59db730713565950601ff1a9 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 08:46:18 +0000 Subject: [PATCH 010/227] Avoid repeated strlen in Admin passthrough auth flush parsing --- lib/Admin_Handler.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index ce4c54a8bc..f684b1a633 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -586,7 +586,8 @@ std::tuple parse_command_purge_query_digests(cha } // parse timestamp - mf_unique_ptr ts_str(strdup(query + strlen(prefix))); + const size_t prefix_len = strlen(prefix); + mf_unique_ptr ts_str(strdup(query + prefix_len)); char *ts_end = nullptr; long long ts = strtoll(trim_spaces_in_place(ts_str.get()), &ts_end, 10); @@ -1059,7 +1060,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ { static const char *pt_prefix = "PROXYSQL FLUSH PASSTHROUGH_AUTH_CACHE FOR USER "; - const size_t pt_prefix_len = strlen(pt_prefix); + static const size_t pt_prefix_len = sizeof("PROXYSQL FLUSH PASSTHROUGH_AUTH_CACHE FOR USER ") - 1; if (query_no_space_length > pt_prefix_len && !strncasecmp(pt_prefix, query_no_space, pt_prefix_len)) { const char *user_start = query_no_space + pt_prefix_len; From 05f106f0c74ab5321a2b82862d93524dcbdacb17 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 08:47:03 +0000 Subject: [PATCH 011/227] Cache string lengths in ProxySQL config query builders --- lib/ProxySQL_Config.cpp | 100 +++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 22 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index cd63f8206d..825493681e 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -1449,11 +1449,12 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { rows++; } } - if (root.exists("mysql_servers_ssl_params")==true) { // mysql_servers_ssl_params - const Setting &mysql_servers_ssl_params = root["mysql_servers_ssl_params"]; - int count = mysql_servers_ssl_params.getLength(); - char *q=(char *)"INSERT OR REPLACE INTO mysql_servers_ssl_params (hostname, port, username, ssl_ca, ssl_cert, ssl_key, ssl_capath, ssl_crl, ssl_crlpath, ssl_cipher, tls_version, comment) VALUES ('%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')"; - for (i=0; i< count; i++) { + if (root.exists("mysql_servers_ssl_params")==true) { // mysql_servers_ssl_params + const Setting &mysql_servers_ssl_params = root["mysql_servers_ssl_params"]; + int count = mysql_servers_ssl_params.getLength(); + char *q=(char *)"INSERT OR REPLACE INTO mysql_servers_ssl_params (hostname, port, username, ssl_ca, ssl_cert, ssl_key, ssl_capath, ssl_crl, ssl_crlpath, ssl_cipher, tls_version, comment) VALUES ('%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')"; + const size_t q_len = strlen(q); + for (i=0; i< count; i++) { const Setting &line = mysql_servers_ssl_params[i]; string hostname = ""; int port = 3306; @@ -1484,12 +1485,23 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { line.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); + const size_t hostname_len = hostname.length(); + const size_t username_len = username.length(); + const size_t ssl_ca_len = ssl_ca.length(); + const size_t ssl_cert_len = ssl_cert.length(); + const size_t ssl_key_len = ssl_key.length(); + const size_t ssl_capath_len = ssl_capath.length(); + const size_t ssl_crl_len = ssl_crl.length(); + const size_t ssl_crlpath_len = ssl_crlpath.length(); + const size_t ssl_cipher_len = ssl_cipher.length(); + const size_t tls_version_len = tls_version.length(); + const size_t escaped_comment_len = strlen(o); char *query=(char *)malloc( - strlen(q) - + hostname.length() + username.length() - + ssl_ca.length() + ssl_cert.length() + ssl_key.length() + ssl_capath.length() - + ssl_crl.length() + ssl_crlpath.length() + ssl_cipher.length() + tls_version.length() - + strlen(o) + 32); + q_len + + hostname_len + username_len + + ssl_ca_len + ssl_cert_len + ssl_key_len + ssl_capath_len + + ssl_crl_len + ssl_crlpath_len + ssl_cipher_len + tls_version_len + + escaped_comment_len + 32); sprintf(query, q, hostname.c_str() , port , username.c_str() , ssl_ca.c_str() , ssl_cert.c_str() , ssl_key.c_str() , ssl_capath.c_str() , @@ -2278,6 +2290,7 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { const Setting &pgsql_servers_ssl_params = root["pgsql_servers_ssl_params"]; int count = pgsql_servers_ssl_params.getLength(); char *q=(char *)"INSERT OR REPLACE INTO pgsql_servers_ssl_params (hostname, port, username, ssl_ca, ssl_cert, ssl_key, ssl_crl, ssl_crlpath, ssl_protocol_version_range, comment) VALUES ('%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')"; + const size_t q_len = strlen(q); for (i=0; i< count; i++) { const Setting &line = pgsql_servers_ssl_params[i]; string hostname = ""; @@ -2305,11 +2318,20 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { line.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); + const size_t hostname_len = hostname.length(); + const size_t username_len = username.length(); + const size_t ssl_ca_len = ssl_ca.length(); + const size_t ssl_cert_len = ssl_cert.length(); + const size_t ssl_key_len = ssl_key.length(); + const size_t ssl_crl_len = ssl_crl.length(); + const size_t ssl_crlpath_len = ssl_crlpath.length(); + const size_t ssl_protocol_version_range_len = ssl_protocol_version_range.length(); + const size_t escaped_comment_len = strlen(o); size_t query_len = ( - strlen(q) - + hostname.length() + username.length() - + ssl_ca.length() + ssl_cert.length() + ssl_key.length() - + ssl_crl.length() + ssl_crlpath.length() + ssl_protocol_version_range.length() + strlen(o) + 64 + q_len + + hostname_len + username_len + + ssl_ca_len + ssl_cert_len + ssl_key_len + + ssl_crl_len + ssl_crlpath_len + ssl_protocol_version_range_len + escaped_comment_len + 64 ); char *query=(char *)malloc(query_len); snprintf(query, query_len, q, hostname.c_str(), port, username.c_str(), ssl_ca.c_str(), ssl_cert.c_str(), ssl_key.c_str(), ssl_crl.c_str(), ssl_crlpath.c_str(), ssl_protocol_version_range.c_str(), o); @@ -2896,7 +2918,11 @@ int ProxySQL_Config::Read_MySQL_Query_Rules_Fast_Routing_from_configfile() { rule.lookupValue("comment", comment); char *o1 = strdup(comment.c_str()); char *o = escape_string_single_quotes(o1, false); - size_t query_len = strlen(q) + strlen(username.c_str()) + strlen(schemaname.c_str()) + strlen(o) + 64; + const size_t q_len = strlen(q); + const size_t username_len = username.size(); + const size_t schemaname_len = schemaname.size(); + const size_t escaped_comment_len = strlen(o); + size_t query_len = q_len + username_len + schemaname_len + escaped_comment_len + 64; char *query = (char *)malloc(query_len); snprintf(query, query_len, q, username.c_str(), schemaname.c_str(), flagIN, destination_hostgroup, o); admindb->execute(query); @@ -2932,7 +2958,11 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_Fast_Routing_from_configfile() { rule.lookupValue("comment", comment); char *o1 = strdup(comment.c_str()); char *o = escape_string_single_quotes(o1, false); - size_t query_len = strlen(q) + strlen(username.c_str()) + strlen(database.c_str()) + strlen(o) + 64; + const size_t q_len = strlen(q); + const size_t username_len = username.size(); + const size_t database_len = database.size(); + const size_t escaped_comment_len = strlen(o); + size_t query_len = q_len + username_len + database_len + escaped_comment_len + 64; char *query = (char *)malloc(query_len); snprintf(query, query_len, q, username.c_str(), database.c_str(), flagIN, destination_hostgroup, o); admindb->execute(query); @@ -2968,7 +2998,12 @@ int ProxySQL_Config::Read_MySQL_Firewall_from_configfile() { u.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - size_t query_len = strlen(q) + strlen(username.c_str()) + strlen(client_address.c_str()) + strlen(mode.c_str()) + strlen(o) + 32; + const size_t q_len = strlen(q); + const size_t username_len = username.size(); + const size_t client_address_len = client_address.size(); + const size_t mode_len = mode.size(); + const size_t escaped_comment_len = strlen(o); + size_t query_len = q_len + username_len + client_address_len + mode_len + escaped_comment_len + 32; char *query=(char *)malloc(query_len); snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), o); admindb->execute(query); @@ -3001,7 +3036,13 @@ int ProxySQL_Config::Read_MySQL_Firewall_from_configfile() { r.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - size_t query_len = strlen(q) + strlen(username.c_str()) + strlen(client_address.c_str()) + strlen(schemaname.c_str()) + strlen(digest.c_str()) + strlen(o) + 64; + const size_t q_len = strlen(q); + const size_t username_len = username.size(); + const size_t client_address_len = client_address.size(); + const size_t schemaname_len = schemaname.size(); + const size_t digest_len = digest.size(); + const size_t escaped_comment_len = strlen(o); + size_t query_len = q_len + username_len + client_address_len + schemaname_len + digest_len + escaped_comment_len + 64; char *query=(char *)malloc(query_len); snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), schemaname.c_str(), flagIN, digest.c_str(), o); admindb->execute(query); @@ -3022,7 +3063,9 @@ int ProxySQL_Config::Read_MySQL_Firewall_from_configfile() { std::string fingerprint=""; f.lookupValue("active", active); f.lookupValue("fingerprint", fingerprint); - size_t query_len = strlen(q) + strlen(fingerprint.c_str()) + 16; + const size_t q_len = strlen(q); + const size_t fingerprint_len = fingerprint.size(); + size_t query_len = q_len + fingerprint_len + 16; char *query=(char *)malloc(query_len); snprintf(query, query_len, q, active, fingerprint.c_str()); admindb->execute(query); @@ -3058,7 +3101,12 @@ int ProxySQL_Config::Read_PgSQL_Firewall_from_configfile() { u.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - size_t query_len = strlen(q) + strlen(username.c_str()) + strlen(client_address.c_str()) + strlen(mode.c_str()) + strlen(o) + 32; + const size_t q_len = strlen(q); + const size_t username_len = username.size(); + const size_t client_address_len = client_address.size(); + const size_t mode_len = mode.size(); + const size_t escaped_comment_len = strlen(o); + size_t query_len = q_len + username_len + client_address_len + mode_len + escaped_comment_len + 32; char *query=(char *)malloc(query_len); snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), o); admindb->execute(query); @@ -3091,7 +3139,13 @@ int ProxySQL_Config::Read_PgSQL_Firewall_from_configfile() { r.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - size_t query_len = strlen(q) + strlen(username.c_str()) + strlen(client_address.c_str()) + strlen(database.c_str()) + strlen(digest.c_str()) + strlen(o) + 64; + const size_t q_len = strlen(q); + const size_t username_len = username.size(); + const size_t client_address_len = client_address.size(); + const size_t database_len = database.size(); + const size_t digest_len = digest.size(); + const size_t escaped_comment_len = strlen(o); + size_t query_len = q_len + username_len + client_address_len + database_len + digest_len + escaped_comment_len + 64; char *query=(char *)malloc(query_len); snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), database.c_str(), flagIN, digest.c_str(), o); admindb->execute(query); @@ -3112,7 +3166,9 @@ int ProxySQL_Config::Read_PgSQL_Firewall_from_configfile() { std::string fingerprint=""; f.lookupValue("active", active); f.lookupValue("fingerprint", fingerprint); - size_t query_len = strlen(q) + strlen(fingerprint.c_str()) + 16; + const size_t q_len = strlen(q); + const size_t fingerprint_len = fingerprint.size(); + size_t query_len = q_len + fingerprint_len + 16; char *query=(char *)malloc(query_len); snprintf(query, query_len, q, active, fingerprint.c_str()); admindb->execute(query); From 3dd65390082667f69dbd7b93382358bf99478016 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:01:40 +0000 Subject: [PATCH 012/227] Avoid duplicate prefix strlen in admin purge command parsing --- lib/Admin_Handler.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index f684b1a633..a36c38e11d 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -526,14 +526,16 @@ bool is_admin_command_or_alias(const std::vector& cmds, char *query return false; } -const char * match_command_prefix(const std::vector& cmd_prefix, char *query, int query_len) { +const char * match_command_prefix(const std::vector& cmd_prefix, char *query, int query_len, size_t &prefix_len) { for (auto &prefix : cmd_prefix) { if ((unsigned int) query_len >= prefix.length() && !strncasecmp(prefix.c_str(), query, prefix.length())) { + prefix_len = prefix.length(); return prefix.c_str(); } } + prefix_len = 0; return nullptr; } @@ -576,8 +578,9 @@ std::tuple parse_command_purge_query_digests(cha bool match = false; enum SERVER_TYPE server_type = SERVER_TYPE_MYSQL; time_t last_seen = 0; + size_t prefix_len = 0; - const char *prefix = match_command_prefix(CMD_PREFIX_PURGE_QUERY_DIGESTS, query, query_len); + const char *prefix = match_command_prefix(CMD_PREFIX_PURGE_QUERY_DIGESTS, query, query_len, prefix_len); if (prefix) { match = true; @@ -586,7 +589,6 @@ std::tuple parse_command_purge_query_digests(cha } // parse timestamp - const size_t prefix_len = strlen(prefix); mf_unique_ptr ts_str(strdup(query + prefix_len)); char *ts_end = nullptr; long long ts = strtoll(trim_spaces_in_place(ts_str.get()), &ts_end, 10); From e6fa77e8d30346d9889d583d897d0848d139d508 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:01:40 +0000 Subject: [PATCH 013/227] Make MySQL FFTO query digest hashing string-safe --- lib/MySQLFFTO.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/MySQLFFTO.cpp b/lib/MySQLFFTO.cpp index 33a49ee0ed..fb87754ecb 100644 --- a/lib/MySQLFFTO.cpp +++ b/lib/MySQLFFTO.cpp @@ -276,14 +276,17 @@ void MySQLFFTO::report_query_stats(const std::string& query, unsigned long long char* ca = (char*)""; if (mysql_thread___query_digests_track_hostname && m_session->client_myds->addr.addr) ca = m_session->client_myds->addr.addr; uint64_t hash2; SpookyHash myhash; myhash.Init(19, 3); - const size_t username_len = ui->username ? strlen(ui->username) : 0; - myhash.Update(ui->username, username_len); + const char* username = ui->username ? ui->username : ""; + const size_t username_len = strlen(username); + myhash.Update(username, username_len); myhash.Update(&qp.digest, sizeof(qp.digest)); - const size_t schemaname_len = strlen(schemaname); - myhash.Update(schemaname, schemaname_len); + const char* safe_schemaname = schemaname ? schemaname : ""; + const size_t schemaname_len = strlen(safe_schemaname); + myhash.Update(safe_schemaname, schemaname_len); myhash.Update(&m_session->current_hostgroup, sizeof(m_session->current_hostgroup)); - const size_t ca_len = ca ? strlen(ca) : 0; - myhash.Update(ca, ca_len); + const char* safe_ca = ca ? ca : ""; + const size_t ca_len = strlen(safe_ca); + myhash.Update(safe_ca, ca_len); myhash.Final(&qp.digest_total, &hash2); GloMyQPro->update_query_digest(qp.digest_total, qp.digest, qp.digest_text, m_session->current_hostgroup, ui, duration_us, m_session->thread->curtime, ca, affected_rows, rows_sent); if (digest_text != qp.buf) free(digest_text); From 6f344f27b9c2a30d82c5c473ec161b1a869bfcfa Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:01:40 +0000 Subject: [PATCH 014/227] Make PgSQL FFTO query digest hashing string-safe --- lib/PgSQLFFTO.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/PgSQLFFTO.cpp b/lib/PgSQLFFTO.cpp index 428d2e9bfc..036cc2c645 100644 --- a/lib/PgSQLFFTO.cpp +++ b/lib/PgSQLFFTO.cpp @@ -303,14 +303,17 @@ void PgSQLFFTO::report_query_stats(const std::string& query, unsigned long long char* ca = (char*)""; if (pgsql_thread___query_digests_track_hostname && m_session->client_myds->addr.addr) ca = m_session->client_myds->addr.addr; uint64_t hash2; SpookyHash myhash; myhash.Init(19, 3); - const size_t username_len = ui->username ? strlen(ui->username) : 0; - myhash.Update(ui->username, username_len); + const char* username = ui->username ? ui->username : ""; + const size_t username_len = strlen(username); + myhash.Update(username, username_len); myhash.Update(&qp.digest, sizeof(qp.digest)); - const size_t schemaname_len = strlen(schemaname); - myhash.Update(schemaname, schemaname_len); + const char* safe_schemaname = schemaname ? schemaname : ""; + const size_t schemaname_len = strlen(safe_schemaname); + myhash.Update(safe_schemaname, schemaname_len); myhash.Update(&m_session->current_hostgroup, sizeof(m_session->current_hostgroup)); - const size_t ca_len = ca ? strlen(ca) : 0; - myhash.Update(ca, ca_len); + const char* safe_ca = ca ? ca : ""; + const size_t ca_len = strlen(safe_ca); + myhash.Update(safe_ca, ca_len); myhash.Final(&qp.digest_total, &hash2); GloPgQPro->update_query_digest(qp.digest_total, qp.digest, qp.digest_text, m_session->current_hostgroup, ui, duration_us, m_session->thread->curtime, ca, affected_rows, rows_sent); if (digest_text != qp.buf) free(digest_text); From f14af9f61d55726c0990733d9d95840a6291f1f8 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:01:40 +0000 Subject: [PATCH 015/227] Guard null cleartext/password pointers in auth flows --- lib/MySQL_Protocol.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index fb3d401f1c..6ccf7338f5 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -2364,8 +2364,9 @@ static bool caching_sha2_fast_auth_verify( unsigned char c[SHA256_DIGEST_LENGTH+20]; unsigned char d[SHA256_DIGEST_LENGTH]; unsigned char e[SHA256_DIGEST_LENGTH]; - const size_t cleartext_password_len = strlen(cleartext_password); - SHA256((const unsigned char *)cleartext_password, cleartext_password_len, a); + const char* safe_cleartext_password = cleartext_password ? cleartext_password : ""; + const size_t cleartext_password_len = strlen(safe_cleartext_password); + SHA256((const unsigned char *)safe_cleartext_password, cleartext_password_len, a); SHA256(a, SHA256_DIGEST_LENGTH, b); memcpy(c,b,SHA256_DIGEST_LENGTH); memcpy(c+SHA256_DIGEST_LENGTH, scramble, 20); @@ -2614,8 +2615,11 @@ void MySQL_Protocol::PPHR_passthrough_init(MyProt_tmp_auth_vars& vars1) { // userinfo->password at probe-acquire time (after the epilogue has // run), so it becomes the auth password for mysql_real_connect_start. if ((*myds)->passthrough_cleartext) { - const size_t passthrough_cleartext_len = strlen((*myds)->passthrough_cleartext); - memset((*myds)->passthrough_cleartext, 0, passthrough_cleartext_len); + char* passthrough_cleartext = (*myds)->passthrough_cleartext; + const size_t passthrough_cleartext_len = passthrough_cleartext ? strlen(passthrough_cleartext) : 0; + if (passthrough_cleartext_len) { + memset(passthrough_cleartext, 0, passthrough_cleartext_len); + } free((*myds)->passthrough_cleartext); (*myds)->passthrough_cleartext = NULL; } @@ -2755,12 +2759,12 @@ bool MySQL_Protocol::PPHR_verify_password(MyProt_tmp_auth_vars& vars1, account_d // caching_sha2_password full-auth exchange and ultimately schedules a // backend probe via AUTHENTICATING_BACKEND_FOR_CLIENT. { - const size_t vars1_password_len = - vars1.password != NULL ? strlen(vars1.password) : 0; - const bool empty_pw_case = - mysql_thread___passthrough_auth_empty_password - && vars1.password != NULL - && vars1_password_len == 0; + const char* safe_pass = vars1.password ? (const char*)vars1.password : ""; + const size_t vars1_password_len = strlen(safe_pass); + const bool empty_pw_case = + mysql_thread___passthrough_auth_empty_password + && vars1.password != NULL + && vars1_password_len == 0; const bool unknown_user_case = mysql_thread___passthrough_auth_unknown_users && vars1.password == NULL; From ddca219e7103c0d66c695b90aeb0294eb143ecd7 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:01:40 +0000 Subject: [PATCH 016/227] Avoid null pointer lengths and preserve schema fallback behavior --- lib/MySQL_Session.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index c8e3e356ac..058c95732c 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1783,8 +1783,11 @@ int MySQL_Session::handler_again___status_AUTHENTICATING_BACKEND_FOR_CLIENT() { // probe resolves. Also clears auth_in_progress. auto scrub_cleartext = [&]() { if (client_myds && client_myds->passthrough_cleartext) { - const size_t cleartext_len = strlen(client_myds->passthrough_cleartext); - memset(client_myds->passthrough_cleartext, 0, cleartext_len); + const char* cleartext = client_myds->passthrough_cleartext; + const size_t cleartext_len = cleartext ? strlen(cleartext) : 0; + if (cleartext_len) { + memset(client_myds->passthrough_cleartext, 0, cleartext_len); + } free(client_myds->passthrough_cleartext); client_myds->passthrough_cleartext = NULL; } @@ -1943,9 +1946,10 @@ int MySQL_Session::handler_again___status_AUTHENTICATING_BACKEND_FOR_CLIENT() { // SIGSEGVs. set_schemaname is NULL-safe: when len==0 it falls back to // mysql_thread___default_schema. if (client_myds->myconn->userinfo->schemaname == NULL) { - const size_t default_schema_len = default_schema ? strlen(default_schema) : 0; + const char* safe_default_schema = default_schema ? default_schema : ""; + const size_t default_schema_len = strlen(safe_default_schema); client_myds->myconn->userinfo->set_schemaname( - default_schema, default_schema_len); + safe_default_schema, default_schema_len); } // Return the authed backend connection to the pool. It is valid and @@ -1964,12 +1968,13 @@ int MySQL_Session::handler_again___status_AUTHENTICATING_BACKEND_FOR_CLIENT() { if (mybe && mybe->server_myds && mybe->server_myds->myconn) { MySQL_Connection_userinfo *bui = mybe->server_myds->myconn->userinfo; if (bui && bui->schemaname == NULL) { - const size_t default_schema_len = default_schema ? strlen(default_schema) : 0; + const char* safe_default_schema = default_schema ? default_schema : ""; + const size_t default_schema_len = strlen(safe_default_schema); bui->set_schemaname( - default_schema, default_schema_len); - } - mybe->server_myds->return_MySQL_Connection_To_Pool(); + safe_default_schema, default_schema_len); } + mybe->server_myds->return_MySQL_Connection_To_Pool(); + } // Frontend per-user connection accounting + max_connections // enforcement (mirrors the normal handshake-completion path). Two From a9a589ef12dd2420f754f1bf6171a2ec1f453441 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:01:40 +0000 Subject: [PATCH 017/227] Guard passthrough cleartext scrub length before memset --- lib/mysql_data_stream.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 077658ad80..b89a3929f7 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -401,8 +401,11 @@ MySQL_Data_Stream::~MySQL_Data_Stream() { if (passthrough_cleartext) { // Best-effort scrub before free; the cleartext password should // not linger in freed heap memory. - const size_t cleartext_len = strlen(passthrough_cleartext); - memset(passthrough_cleartext, 0, cleartext_len); + const char* safe_cleartext = passthrough_cleartext ? passthrough_cleartext : ""; + const size_t cleartext_len = strlen(safe_cleartext); + if (cleartext_len) { + memset(passthrough_cleartext, 0, cleartext_len); + } free(passthrough_cleartext); passthrough_cleartext = NULL; } From ae8d55033a8c9c2da19c248e4deecc3ead175244 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:01:40 +0000 Subject: [PATCH 018/227] Make ProxySQL config escaped-comment SQL builders null-safe --- lib/ProxySQL_Config.cpp | 98 +++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index 825493681e..e1f13dddd3 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -240,8 +240,9 @@ int ProxySQL_Config::Read_MySQL_Users_from_configfile(std::string& error) { user.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - char *query=(char *)malloc(strlen(q)+strlen(username.c_str())+strlen(password.c_str())+strlen(o)+strlen(attributes.c_str())+128); - sprintf(query,q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, default_schema.c_str(), schema_locked, transaction_persistent, fast_forward, max_connections, attributes.c_str(), o); + const char* safe_comment = o ? o : ""; + char *query=(char *)malloc(strlen(q)+strlen(username.c_str())+strlen(password.c_str())+strlen(safe_comment)+strlen(attributes.c_str())+128); + sprintf(query,q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, default_schema.c_str(), schema_locked, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); admindb->execute(query); if (o!=o1) free(o); free(o1); @@ -1394,8 +1395,9 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { server.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - char *query=(char *)malloc(strlen(q)+strlen(status.c_str())+strlen(address.c_str())+strlen(o)+128); - sprintf(query,q, address.c_str(), port, gtid_port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, o); + const char* safe_comment = o ? o : ""; + char *query=(char *)malloc(strlen(q)+strlen(status.c_str())+strlen(address.c_str())+strlen(safe_comment)+128); + sprintf(query,q, address.c_str(), port, gtid_port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); @@ -1437,8 +1439,10 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { } char *t1=strdup(check_type.c_str()); char *t=escape_string_single_quotes(t1, false); - char *query=(char *)malloc(strlen(q)+strlen(o)+strlen(t)+32); - sprintf(query,q, writer_hostgroup, reader_hostgroup, o, t); + const char* safe_comment = o ? o : ""; + const char* safe_check_type = t ? t : ""; + char *query=(char *)malloc(strlen(q)+strlen(safe_comment)+strlen(safe_check_type)+32); + sprintf(query,q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); @@ -1495,7 +1499,8 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t ssl_crlpath_len = ssl_crlpath.length(); const size_t ssl_cipher_len = ssl_cipher.length(); const size_t tls_version_len = tls_version.length(); - const size_t escaped_comment_len = strlen(o); + const char* safe_comment = o ? o : ""; + const size_t escaped_comment_len = strlen(safe_comment); char *query=(char *)malloc( q_len + hostname_len + username_len @@ -1506,7 +1511,7 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { hostname.c_str() , port , username.c_str() , ssl_ca.c_str() , ssl_cert.c_str() , ssl_key.c_str() , ssl_capath.c_str() , ssl_crl.c_str() , ssl_crlpath.c_str() , ssl_cipher.c_str() , tls_version.c_str() , - o); + safe_comment); admindb->execute(query); if (o!=o1) free(o); free(o1); @@ -1551,8 +1556,9 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { line.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - char *query=(char *)malloc(strlen(q)+strlen(o)+128); // 128 vs sizeof(int)*8 - sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, o); + const char* safe_comment = o ? o : ""; + char *query=(char *)malloc(strlen(q)+strlen(safe_comment)+128); // 128 vs sizeof(int)*8 + sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); @@ -1598,8 +1604,9 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { line.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - char *query=(char *)malloc(strlen(q)+strlen(o)+128); // 128 vs sizeof(int)*8 - sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, o); + const char* safe_comment = o ? o : ""; + char *query=(char *)malloc(strlen(q)+strlen(safe_comment)+128); // 128 vs sizeof(int)*8 + sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); @@ -1653,8 +1660,10 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *o=escape_string_single_quotes(o1, false); char *p1=strdup(domain_name.c_str()); char *p=escape_string_single_quotes(p1, false); - char *query=(char *)malloc(strlen(q)+strlen(o)+strlen(p)+256); // 128 vs sizeof(int)*8 - sprintf(query,q, writer_hostgroup, reader_hostgroup, active, aurora_port, p, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, autopurge_missing_checks, o); + const char* safe_comment = o ? o : ""; + const char* safe_domain = p ? p : ""; + char *query=(char *)malloc(strlen(q)+strlen(safe_comment)+strlen(safe_domain)+256); // 128 vs sizeof(int)*8 + sprintf(query,q, writer_hostgroup, reader_hostgroup, active, aurora_port, safe_domain, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, autopurge_missing_checks, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); @@ -1709,8 +1718,9 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { line.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - char *query=(char *)malloc(strlen(q)+strlen(o)+256); // 128 vs sizeof(int)*8 - sprintf(query,q, writer_hostgroup, reader_hostgroup, green_writer_str, green_reader_str, active, writer_is_also_reader, check_interval_ms, check_timeout_ms, o); + const char* safe_comment = o ? o : ""; + char *query=(char *)malloc(strlen(q)+strlen(safe_comment)+256); // 128 vs sizeof(int)*8 + sprintf(query,q, writer_hostgroup, reader_hostgroup, green_writer_str, green_reader_str, active, writer_is_also_reader, check_interval_ms, check_timeout_ms, safe_comment); admindb->execute(query); if (o!=o1) free(o); free(o1); @@ -1881,8 +1891,9 @@ int ProxySQL_Config::Read_ProxySQL_Servers_from_configfile(std::string& error) { server.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - char *query=(char *)malloc(strlen(q)+strlen(address.c_str())+strlen(o)+128); - sprintf(query, q, address.c_str(), port, weight, o); + const char* safe_comment = o ? o : ""; + char *query=(char *)malloc(strlen(q)+strlen(address.c_str())+strlen(safe_comment)+128); + sprintf(query, q, address.c_str(), port, weight, safe_comment); proxy_info("Cluster: Adding ProxySQL Servers %s:%d from config file\n", address.c_str(), port); //fprintf(stderr, "%s\n", query); admindb->execute(query); @@ -2147,8 +2158,9 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { server.lookupValue("comment", comment); char* o1 = strdup(comment.c_str()); char* o = escape_string_single_quotes(o1, false); - char* query = (char*)malloc(strlen(q) + strlen(status.c_str()) + strlen(address.c_str()) + strlen(o) + 128); - sprintf(query, q, address.c_str(), port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, o); + const char* safe_comment = o ? o : ""; + char* query = (char*)malloc(strlen(q) + strlen(status.c_str()) + strlen(address.c_str()) + strlen(safe_comment) + 128); + sprintf(query, q, address.c_str(), port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o != o1) free(o); @@ -2190,8 +2202,10 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { } char* t1 = strdup(check_type.c_str()); char* t = escape_string_single_quotes(t1, false); - char* query = (char*)malloc(strlen(q) + strlen(o) + strlen(t) + 32); - sprintf(query, q, writer_hostgroup, reader_hostgroup, o, t); + const char* safe_comment = o ? o : ""; + const char* safe_check_type = t ? t : ""; + char* query = (char*)malloc(strlen(q) + strlen(safe_comment) + strlen(safe_check_type) + 32); + sprintf(query, q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o != o1) free(o); @@ -2326,7 +2340,8 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { const size_t ssl_crl_len = ssl_crl.length(); const size_t ssl_crlpath_len = ssl_crlpath.length(); const size_t ssl_protocol_version_range_len = ssl_protocol_version_range.length(); - const size_t escaped_comment_len = strlen(o); + const char* safe_comment = o ? o : ""; + const size_t escaped_comment_len = strlen(safe_comment); size_t query_len = ( q_len + hostname_len + username_len @@ -2334,7 +2349,7 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { + ssl_crl_len + ssl_crlpath_len + ssl_protocol_version_range_len + escaped_comment_len + 64 ); char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, hostname.c_str(), port, username.c_str(), ssl_ca.c_str(), ssl_cert.c_str(), ssl_key.c_str(), ssl_crl.c_str(), ssl_crlpath.c_str(), ssl_protocol_version_range.c_str(), o); + snprintf(query, query_len, q, hostname.c_str(), port, username.c_str(), ssl_ca.c_str(), ssl_cert.c_str(), ssl_key.c_str(), ssl_crl.c_str(), ssl_crlpath.c_str(), ssl_protocol_version_range.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); @@ -2431,8 +2446,9 @@ int ProxySQL_Config::Read_PgSQL_Users_from_configfile(std::string& error) { user.lookupValue("comment", comment); char* o1 = strdup(comment.c_str()); char* o = escape_string_single_quotes(o1, false); - char* query = (char*)malloc(strlen(q) + strlen(username.c_str()) + strlen(password.c_str()) + strlen(o) + strlen(attributes.c_str()) + 128); - sprintf(query, q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, transaction_persistent, fast_forward, max_connections, attributes.c_str(), o); + const char* safe_comment = o ? o : ""; + char* query = (char*)malloc(strlen(q) + strlen(username.c_str()) + strlen(password.c_str()) + strlen(safe_comment) + strlen(attributes.c_str()) + 128); + sprintf(query, q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); @@ -2921,10 +2937,11 @@ int ProxySQL_Config::Read_MySQL_Query_Rules_Fast_Routing_from_configfile() { const size_t q_len = strlen(q); const size_t username_len = username.size(); const size_t schemaname_len = schemaname.size(); - const size_t escaped_comment_len = strlen(o); + const char* safe_comment = o ? o : ""; + const size_t escaped_comment_len = strlen(safe_comment); size_t query_len = q_len + username_len + schemaname_len + escaped_comment_len + 64; char *query = (char *)malloc(query_len); - snprintf(query, query_len, q, username.c_str(), schemaname.c_str(), flagIN, destination_hostgroup, o); + snprintf(query, query_len, q, username.c_str(), schemaname.c_str(), flagIN, destination_hostgroup, safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); @@ -2961,10 +2978,11 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_Fast_Routing_from_configfile() { const size_t q_len = strlen(q); const size_t username_len = username.size(); const size_t database_len = database.size(); - const size_t escaped_comment_len = strlen(o); + const char* safe_comment = o ? o : ""; + const size_t escaped_comment_len = strlen(safe_comment); size_t query_len = q_len + username_len + database_len + escaped_comment_len + 64; char *query = (char *)malloc(query_len); - snprintf(query, query_len, q, username.c_str(), database.c_str(), flagIN, destination_hostgroup, o); + snprintf(query, query_len, q, username.c_str(), database.c_str(), flagIN, destination_hostgroup, safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); @@ -3002,10 +3020,11 @@ int ProxySQL_Config::Read_MySQL_Firewall_from_configfile() { const size_t username_len = username.size(); const size_t client_address_len = client_address.size(); const size_t mode_len = mode.size(); - const size_t escaped_comment_len = strlen(o); + const char* safe_comment = o ? o : ""; + const size_t escaped_comment_len = strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + mode_len + escaped_comment_len + 32; char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), o); + snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); @@ -3041,10 +3060,11 @@ int ProxySQL_Config::Read_MySQL_Firewall_from_configfile() { const size_t client_address_len = client_address.size(); const size_t schemaname_len = schemaname.size(); const size_t digest_len = digest.size(); - const size_t escaped_comment_len = strlen(o); + const char* safe_comment = o ? o : ""; + const size_t escaped_comment_len = strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + schemaname_len + digest_len + escaped_comment_len + 64; char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), schemaname.c_str(), flagIN, digest.c_str(), o); + snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), schemaname.c_str(), flagIN, digest.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); @@ -3105,10 +3125,11 @@ int ProxySQL_Config::Read_PgSQL_Firewall_from_configfile() { const size_t username_len = username.size(); const size_t client_address_len = client_address.size(); const size_t mode_len = mode.size(); - const size_t escaped_comment_len = strlen(o); + const char* safe_comment = o ? o : ""; + const size_t escaped_comment_len = strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + mode_len + escaped_comment_len + 32; char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), o); + snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); @@ -3144,10 +3165,11 @@ int ProxySQL_Config::Read_PgSQL_Firewall_from_configfile() { const size_t client_address_len = client_address.size(); const size_t database_len = database.size(); const size_t digest_len = digest.size(); - const size_t escaped_comment_len = strlen(o); + const char* safe_comment = o ? o : ""; + const size_t escaped_comment_len = strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + database_len + digest_len + escaped_comment_len + 64; char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), database.c_str(), flagIN, digest.c_str(), o); + snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), database.c_str(), flagIN, digest.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); From 22b4a00a73c933a3d8083426bffb39c288839ac4 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:09:13 +0000 Subject: [PATCH 019/227] Cache repeated strlen inputs in admin command parse and handshake Compute command and server version lengths once before comparisons/copy paths: this reduces repeated strlen calls in Proxysql admin command dispatch and initial handshake payload construction, keeping the same behavior while matching cpp:S5813 guidance. --- lib/Admin_Handler.cpp | 47 ++++++++++++++++++++++++------------------ lib/MySQL_Protocol.cpp | 4 +++- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index a36c38e11d..433d762bf6 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -640,17 +640,24 @@ bool admin_handler_command_kill_mysql_connection(uint32_t session_thd_id, S* ses template bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_space_length, S* sess, ProxySQL_Admin *pa) { + static const char cmd_coredump[] = "PROXYSQL COREDUMP"; + static const size_t cmd_coredump_len = sizeof(cmd_coredump) - 1; + static const char cmd_coredump_compressed[] = "PROXYSQL COMPRESSEDCOREDUMP"; + static const size_t cmd_coredump_compressed_len = sizeof(cmd_coredump_compressed) - 1; + static const char cmd_cluster_node_uuid[] = "PROXYSQL CLUSTER_NODE_UUID "; + static const size_t cmd_cluster_node_uuid_len = sizeof(cmd_cluster_node_uuid) - 1; + #if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__)) && defined(__linux) - // currently only support x86-32, x86-64, ARM, and MIPS on Linux - if (!(strncasecmp("PROXYSQL COREDUMP", query_no_space, strlen("PROXYSQL COREDUMP")))) { - string filename = "core"; - if (query_no_space_length > strlen("PROXYSQL COREDUMP")) { - if (query_no_space[strlen("PROXYSQL COREDUMP")] == ' ') { - filename = string(query_no_space+strlen("PROXYSQL COREDUMP ")); - } else { - filename = ""; + // currently only support x86-32, x86-64, ARM, and MIPS on Linux + if (!(strncasecmp(cmd_coredump, query_no_space, cmd_coredump_len))) { + string filename = "core"; + if (query_no_space_length > (unsigned int)cmd_coredump_len) { + if (query_no_space[cmd_coredump_len] == ' ') { + filename = string(query_no_space + cmd_coredump_len + 1); + } else { + filename = ""; + } } - } if (filename == "") { proxy_error("Received incorrect PROXYSQL COREDUMP command: %s\n", query_no_space); } else { @@ -661,17 +668,17 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ string msg = "Coredump: " + filename; SPA->send_ok_msg_to_client(sess, (char *)msg.c_str(), 0, query_no_space); return false; - } - } - if (!(strncasecmp("PROXYSQL COMPRESSEDCOREDUMP", query_no_space, strlen("PROXYSQL COMPRESSEDCOREDUMP")))) { - string filename = "core"; - if (query_no_space_length > strlen("PROXYSQL COMPRESSEDCOREDUMP")) { - if (query_no_space[strlen("PROXYSQL COMPRESSEDCOREDUMP")] == ' ') { - filename = string(query_no_space+strlen("PROXYSQL COMPRESSEDCOREDUMP ")); - } else { - filename = ""; } } + if (!(strncasecmp(cmd_coredump_compressed, query_no_space, cmd_coredump_compressed_len))) { + string filename = "core"; + if (query_no_space_length > (unsigned int)cmd_coredump_compressed_len) { + if (query_no_space[cmd_coredump_compressed_len] == ' ') { + filename = string(query_no_space + cmd_coredump_compressed_len + 1); + } else { + filename = ""; + } + } if (filename == "") { proxy_error("Received incorrect PROXYSQL COMPRESSEDCOREDUMP command: %s\n", query_no_space); } else { @@ -686,8 +693,8 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ } #endif - if (!(strncasecmp("PROXYSQL CLUSTER_NODE_UUID ", query_no_space, strlen("PROXYSQL CLUSTER_NODE_UUID ")))) { - int l = strlen("PROXYSQL CLUSTER_NODE_UUID "); + if (!(strncasecmp(cmd_cluster_node_uuid, query_no_space, cmd_cluster_node_uuid_len))) { + int l = cmd_cluster_node_uuid_len; if (sess->client_myds->addr.port == 0) { proxy_warning("Received PROXYSQL CLUSTER_NODE_UUID not from TCP socket. Exiting client\n"); SPA->send_error_msg_to_client(sess, (char *)"Received PROXYSQL CLUSTER_NODE_UUID not from TCP socket"); diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 6ccf7338f5..0812de5479 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -1055,7 +1055,9 @@ bool MySQL_Protocol::generate_pkt_initial_handshake(bool send, void **ptr, unsig rand_st.seed2=rand()%rand_st.max_value; memcpy(_ptr+l, &protocol_version, sizeof(protocol_version)); l+=sizeof(protocol_version); - memcpy(_ptr+l, mysql_thread___server_version, strlen(mysql_thread___server_version)); l+=strlen(mysql_thread___server_version)+1; + const char* server_version = mysql_thread___server_version ? mysql_thread___server_version : ""; + const size_t server_version_len = strlen(server_version); + memcpy(_ptr+l, server_version, server_version_len); l+=server_version_len+1; memcpy(_ptr+l, &thread_id, sizeof(uint32_t)); l+=sizeof(uint32_t); //#ifdef MARIADB_BASE_VERSION // proxy_create_random_string(myds->myconn->myconn.scramble_buff+0,8,(struct my_rnd_struct *)&rand_st); From ecbc89c7b3b14383a19d93d92d510e23e6457adb Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:09:57 +0000 Subject: [PATCH 020/227] Replace repeated command-literal strlen checks in admin handler Use compile-time lengths for admin SQL command constants and reuse existing query_no_space_length in checksum dispatch checks to avoid repeated strlen calls across command matching paths. --- lib/Admin_Handler.cpp | 666 +++++++++++++++++++++--------------------- 1 file changed, 333 insertions(+), 333 deletions(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index 433d762bf6..b62fe70ce3 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -351,7 +351,7 @@ static bool extract_psql_pattern(const char* query, char* pattern_buf, size_t bu if (!relname_pos) return false; // Skip to the operator or LIKE keyword - const char* value_pos = relname_pos + strlen("c.relname"); + const char* value_pos = relname_pos + sizeof("c.relname") - 1; while (*value_pos && *value_pos == ' ') value_pos++; // Safety check: ensure we haven't gone past end of string @@ -752,7 +752,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } } - if (query_no_space_length==strlen("PROXYSQL READONLY") && !strncasecmp("PROXYSQL READONLY",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL READONLY") - 1 && !strncasecmp("PROXYSQL READONLY",query_no_space, query_no_space_length)) { // this command enables admin_read_only , so the admin module is in read_only mode proxy_info("Received PROXYSQL READONLY command\n"); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -760,7 +760,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); return false; } - if (query_no_space_length==strlen("PROXYSQL READWRITE") && !strncasecmp("PROXYSQL READWRITE",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL READWRITE") - 1 && !strncasecmp("PROXYSQL READWRITE",query_no_space, query_no_space_length)) { // this command disables admin_read_only , so the admin module won't be in read_only mode proxy_info("Received PROXYSQL WRITE command\n"); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -768,7 +768,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); return false; } - if (query_no_space_length == strlen("PROXYSQL START") && !strncasecmp("PROXYSQL START", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("PROXYSQL START") - 1 && !strncasecmp("PROXYSQL START", query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL START command\n"); ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; @@ -805,7 +805,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (query_no_space_length==strlen("PROXYSQL RESTART") && !strncasecmp("PROXYSQL RESTART",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL RESTART") - 1 && !strncasecmp("PROXYSQL RESTART",query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL RESTART command\n"); // This function was introduced into 'prometheus::Registry' for being // able to do a complete reset of all the 'prometheus counters'. It @@ -816,7 +816,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (query_no_space_length == strlen("PROXYSQL STOP") && !strncasecmp("PROXYSQL STOP", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("PROXYSQL STOP") - 1 && !strncasecmp("PROXYSQL STOP", query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL STOP command\n"); ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; @@ -871,7 +871,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (query_no_space_length==strlen("PROXYSQL PAUSE") && !strncasecmp("PROXYSQL PAUSE",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL PAUSE") - 1 && !strncasecmp("PROXYSQL PAUSE",query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL PAUSE command\n"); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; if (admin_nostart_) { @@ -894,7 +894,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ GloMTH->commit(); GloMTH->signal_all_threads(0); GloMTH->stop_listeners(); - admin_proxysql_mysql_paused=true; + admin_proxysql_mysql_paused=true; // we now rollback poll_timeout char buf[32]; sprintf(buf,"%d",admin_old_wait_timeout); @@ -925,7 +925,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (query_no_space_length==strlen("PROXYSQL RESUME") && !strncasecmp("PROXYSQL RESUME",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL RESUME") - 1 && !strncasecmp("PROXYSQL RESUME",query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL RESUME command\n"); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; if (admin_nostart_) { @@ -987,7 +987,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (query_no_space_length==strlen("PROXYSQL SHUTDOWN SLOW") && !strncasecmp("PROXYSQL SHUTDOWN SLOW",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL SHUTDOWN SLOW") - 1 && !strncasecmp("PROXYSQL SHUTDOWN SLOW",query_no_space, query_no_space_length)) { glovars.proxy_restart_on_error=false; glovars.reload=0; proxy_info("Received PROXYSQL SHUTDOWN SLOW command\n"); @@ -995,7 +995,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (query_no_space_length==strlen("PROXYSQL FLUSH LOGS") && !strncasecmp("PROXYSQL FLUSH LOGS",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL FLUSH LOGS") - 1 && !strncasecmp("PROXYSQL FLUSH LOGS",query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL FLUSH LOGS command\n"); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; SPA->flush_logs(); @@ -1003,7 +1003,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (query_no_space_length==strlen("PROXYSQL FLUSH QUERY CACHE") && !strncasecmp("PROXYSQL FLUSH QUERY CACHE",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL FLUSH QUERY CACHE") - 1 && !strncasecmp("PROXYSQL FLUSH QUERY CACHE",query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL FLUSH QUERY CACHE command\n"); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; if (GloMyQC) { @@ -1016,7 +1016,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (query_no_space_length == strlen("PROXYSQL FLUSH MYSQL QUERY CACHE") && !strncasecmp("PROXYSQL FLUSH MYSQL QUERY CACHE", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("PROXYSQL FLUSH MYSQL QUERY CACHE") - 1 && !strncasecmp("PROXYSQL FLUSH MYSQL QUERY CACHE", query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL FLUSH MYSQL QUERY CACHE command\n"); ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; if (GloMyQC) { @@ -1026,7 +1026,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (query_no_space_length == strlen("PROXYSQL FLUSH PGSQL QUERY CACHE") && !strncasecmp("PROXYSQL FLUSH PGSQL QUERY CACHE", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("PROXYSQL FLUSH PGSQL QUERY CACHE") - 1 && !strncasecmp("PROXYSQL FLUSH PGSQL QUERY CACHE", query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL FLUSH PGSQL QUERY CACHE command\n"); ProxySQL_Admin* SPA = (ProxySQL_Admin*)pa; uint64_t count = 0; @@ -1098,7 +1098,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ } if ( - (query_no_space_length==strlen("PROXYSQL FLUSH CONFIGDB") && !strncasecmp("PROXYSQL FLUSH CONFIGDB",query_no_space, query_no_space_length)) // see #923 + (query_no_space_length==sizeof("PROXYSQL FLUSH CONFIGDB") - 1 && !strncasecmp("PROXYSQL FLUSH CONFIGDB",query_no_space, query_no_space_length)) // see #923 ) { proxy_info("Received %s command\n", query_no_space); proxy_warning("A misconfigured configdb will cause undefined behaviors\n"); @@ -1109,7 +1109,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ } #ifdef DEBUG - if (query_no_space_length == strlen("PROXYSQL FLUSH STATS") && !strncasecmp("PROXYSQL FLUSH STATS", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("PROXYSQL FLUSH STATS") - 1 && !strncasecmp("PROXYSQL FLUSH STATS", query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL FLUSH STATS command\n"); ProxySQL_Admin *SPA = (ProxySQL_Admin *)pa; SPA->flush_stats(); @@ -1117,7 +1117,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (query_no_space_length == strlen("PROXYSQL FLUSH MYSQL STATS") && !strncasecmp("PROXYSQL FLUSH MYSQL STATS", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("PROXYSQL FLUSH MYSQL STATS") - 1 && !strncasecmp("PROXYSQL FLUSH MYSQL STATS", query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL FLUSH MYSQL STATS command\n"); ProxySQL_Admin *SPA = (ProxySQL_Admin *)pa; SPA->flush_mysql_stats(); @@ -1125,7 +1125,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (query_no_space_length == strlen("PROXYSQL FLUSH PGSQL STATS") && !strncasecmp("PROXYSQL FLUSH PGSQL STATS", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("PROXYSQL FLUSH PGSQL STATS") - 1 && !strncasecmp("PROXYSQL FLUSH PGSQL STATS", query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL FLUSH PGSQL STATS command\n"); ProxySQL_Admin *SPA = (ProxySQL_Admin *)pa; SPA->flush_pgsql_stats(); @@ -1135,7 +1135,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ #endif // DEBUG #ifdef PROXYSQLTSDB - if (query_no_space_length == strlen("PROXYSQL TSDB DOWNSAMPLE") && !strncasecmp("PROXYSQL TSDB DOWNSAMPLE", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("PROXYSQL TSDB DOWNSAMPLE") - 1 && !strncasecmp("PROXYSQL TSDB DOWNSAMPLE", query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL TSDB DOWNSAMPLE command\n"); if (GloProxyStats) { GloProxyStats->tsdb_downsample_metrics(); @@ -1182,13 +1182,13 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ } #ifndef NOJEM - if (query_no_space_length==strlen("PROXYSQL MEMPROFILE START") && !strncasecmp("PROXYSQL MEMPROFILE START",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL MEMPROFILE START") - 1 && !strncasecmp("PROXYSQL MEMPROFILE START",query_no_space, query_no_space_length)) { bool en=true; mallctl("prof.active", NULL, NULL, &en, sizeof(bool)); SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); return false; } - if (query_no_space_length==strlen("PROXYSQL MEMPROFILE STOP") && !strncasecmp("PROXYSQL MEMPROFILE STOP",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL MEMPROFILE STOP") - 1 && !strncasecmp("PROXYSQL MEMPROFILE STOP",query_no_space, query_no_space_length)) { bool en=false; mallctl("prof.active", NULL, NULL, &en, sizeof(bool)); SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); @@ -1197,13 +1197,13 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ #endif #ifdef WITHGCOV - if (query_no_space_length==strlen("PROXYSQL GCOV DUMP") && !strncasecmp("PROXYSQL GCOV DUMP",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL GCOV DUMP") - 1 && !strncasecmp("PROXYSQL GCOV DUMP",query_no_space, query_no_space_length)) { proxy_info("Received %s command\n", query_no_space); __gcov_dump(); SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); return false; } - if (query_no_space_length==strlen("PROXYSQL GCOV RESET") && !strncasecmp("PROXYSQL GCOV RESET",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL GCOV RESET") - 1 && !strncasecmp("PROXYSQL GCOV RESET",query_no_space, query_no_space_length)) { proxy_info("Received %s command\n", query_no_space); __gcov_reset(); SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); @@ -1211,7 +1211,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ } #endif - if (query_no_space_length==strlen("PROXYSQL KILL") && !strncasecmp("PROXYSQL KILL",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL KILL") - 1 && !strncasecmp("PROXYSQL KILL",query_no_space, query_no_space_length)) { proxy_info("Received PROXYSQL KILL command\n"); #ifdef DEBUG // In debug builds prefer coordinated shutdown to avoid teardown races. @@ -1222,7 +1222,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ #endif } - if (query_no_space_length==strlen("PROXYSQL SHUTDOWN") && !strncasecmp("PROXYSQL SHUTDOWN",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("PROXYSQL SHUTDOWN") - 1 && !strncasecmp("PROXYSQL SHUTDOWN",query_no_space, query_no_space_length)) { // in 2.1 , PROXYSQL SHUTDOWN behaves like PROXYSQL KILL : quick exit // the former PROXYQL SHUTDOWN is now replaced with PROXYSQL SHUTDOWN SLOW proxy_info("Received PROXYSQL SHUTDOWN command\n"); @@ -1274,10 +1274,10 @@ static char* mask_sensitive_values_in_query(const char* query) { /** * @brief Checks if a given variable name is considered sensitive (e.g., passwords, credentials). - * + * * This function identifies variable names that, if logged in plaintext, could * expose sensitive information. - * + * * @param var_name The name of the variable to check. * @return True if the variable name is sensitive, false otherwise. */ @@ -1346,8 +1346,8 @@ bool admin_handler_command_set(char *query_no_space, unsigned int query_no_space strstr(query_no_space, (char *)"mysql-default_authentication_plugin"); if (!skip_raw_query_log) { // issue #599 proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received command %s\n", query_no_space); - if (strncasecmp(query_no_space,(char *)"set autocommit",strlen((char *)"set autocommit"))) { - if (strncasecmp(query_no_space,(char *)"SET @@session.autocommit",strlen((char *)"SET @@session.autocommit"))) { + if (strncasecmp(query_no_space,(char *)"set autocommit",sizeof("set autocommit") - 1)) { + if (strncasecmp(query_no_space,(char *)"SET @@session.autocommit",sizeof("SET @@session.autocommit") - 1)) { char* masked_query = mask_sensitive_values_in_query(query_no_space); proxy_info("Received command %s\n", masked_query); free(masked_query); @@ -1356,7 +1356,7 @@ bool admin_handler_command_set(char *query_no_space, unsigned int query_no_space } // Get a pointer to the beginning of var=value entry and split to get var name and value - char *set_entry = query_no_space + strlen("SET "); + char *set_entry = query_no_space + sizeof("SET ") - 1; char *untrimmed_var_name=NULL; char *var_value=NULL; c_split_2(set_entry, "=", &untrimmed_var_name, &var_value); @@ -1423,11 +1423,11 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query #ifdef DEBUG if ((query_no_space_length>11) && ( (!strncasecmp("SAVE DEBUG ", query_no_space, 11)) || (!strncasecmp("LOAD DEBUG ", query_no_space, 11))) ) { if ( - (query_no_space_length==strlen("LOAD DEBUG TO MEMORY") && !strncasecmp("LOAD DEBUG TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD DEBUG TO MEMORY") - 1 && !strncasecmp("LOAD DEBUG TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD DEBUG TO MEM") && !strncasecmp("LOAD DEBUG TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD DEBUG TO MEM") - 1 && !strncasecmp("LOAD DEBUG TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD DEBUG FROM DISK") && !strncasecmp("LOAD DEBUG FROM DISK",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD DEBUG FROM DISK") - 1 && !strncasecmp("LOAD DEBUG FROM DISK",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); // we are now copying the data from memory to disk @@ -1447,11 +1447,11 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("SAVE DEBUG FROM MEMORY") && !strncasecmp("SAVE DEBUG FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE DEBUG FROM MEMORY") - 1 && !strncasecmp("SAVE DEBUG FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE DEBUG FROM MEM") && !strncasecmp("SAVE DEBUG FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE DEBUG FROM MEM") - 1 && !strncasecmp("SAVE DEBUG FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE DEBUG TO DISK") && !strncasecmp("SAVE DEBUG TO DISK",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE DEBUG TO DISK") - 1 && !strncasecmp("SAVE DEBUG TO DISK",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); // we are now copying the data from disk to memory @@ -1471,13 +1471,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD DEBUG FROM MEMORY") && !strncasecmp("LOAD DEBUG FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD DEBUG FROM MEMORY") - 1 && !strncasecmp("LOAD DEBUG FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD DEBUG FROM MEM") && !strncasecmp("LOAD DEBUG FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD DEBUG FROM MEM") - 1 && !strncasecmp("LOAD DEBUG FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD DEBUG TO RUNTIME") && !strncasecmp("LOAD DEBUG TO RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD DEBUG TO RUNTIME") - 1 && !strncasecmp("LOAD DEBUG TO RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD DEBUG TO RUN") && !strncasecmp("LOAD DEBUG TO RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD DEBUG TO RUN") - 1 && !strncasecmp("LOAD DEBUG TO RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1493,13 +1493,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("SAVE DEBUG TO MEMORY") && !strncasecmp("SAVE DEBUG TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE DEBUG TO MEMORY") - 1 && !strncasecmp("SAVE DEBUG TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE DEBUG TO MEM") && !strncasecmp("SAVE DEBUG TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE DEBUG TO MEM") - 1 && !strncasecmp("SAVE DEBUG TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE DEBUG FROM RUNTIME") && !strncasecmp("SAVE DEBUG FROM RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE DEBUG FROM RUNTIME") - 1 && !strncasecmp("SAVE DEBUG FROM RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE DEBUG FROM RUN") && !strncasecmp("SAVE DEBUG FROM RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE DEBUG FROM RUN") - 1 && !strncasecmp("SAVE DEBUG FROM RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1518,13 +1518,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query return false; if ( - (query_no_space_length==strlen("LOAD RESTAPI FROM MEMORY") && !strncasecmp("LOAD RESTAPI FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD RESTAPI FROM MEMORY") - 1 && !strncasecmp("LOAD RESTAPI FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD RESTAPI FROM MEM") && !strncasecmp("LOAD RESTAPI FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD RESTAPI FROM MEM") - 1 && !strncasecmp("LOAD RESTAPI FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD RESTAPI TO RUNTIME") && !strncasecmp("LOAD RESTAPI TO RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD RESTAPI TO RUNTIME") - 1 && !strncasecmp("LOAD RESTAPI TO RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD RESTAPI TO RUN") && !strncasecmp("LOAD RESTAPI TO RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD RESTAPI TO RUN") - 1 && !strncasecmp("LOAD RESTAPI TO RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1535,7 +1535,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD RESTAPI FROM CONFIG") && !strncasecmp("LOAD RESTAPI FROM CONFIG",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD RESTAPI FROM CONFIG") - 1 && !strncasecmp("LOAD RESTAPI FROM CONFIG",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); if (GloVars.configfile_open) { @@ -1563,13 +1563,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("SAVE RESTAPI TO MEMORY") && !strncasecmp("SAVE RESTAPI TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE RESTAPI TO MEMORY") - 1 && !strncasecmp("SAVE RESTAPI TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE RESTAPI TO MEM") && !strncasecmp("SAVE RESTAPI TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE RESTAPI TO MEM") - 1 && !strncasecmp("SAVE RESTAPI TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE RESTAPI FROM RUNTIME") && !strncasecmp("SAVE RESTAPI FROM RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE RESTAPI FROM RUNTIME") - 1 && !strncasecmp("SAVE RESTAPI FROM RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE RESTAPI FROM RUN") && !strncasecmp("SAVE RESTAPI FROM RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE RESTAPI FROM RUN") - 1 && !strncasecmp("SAVE RESTAPI FROM RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1586,13 +1586,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query return false; if ( - (query_no_space_length==strlen("LOAD SCHEDULER FROM MEMORY") && !strncasecmp("LOAD SCHEDULER FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SCHEDULER FROM MEMORY") - 1 && !strncasecmp("LOAD SCHEDULER FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD SCHEDULER FROM MEM") && !strncasecmp("LOAD SCHEDULER FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SCHEDULER FROM MEM") - 1 && !strncasecmp("LOAD SCHEDULER FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD SCHEDULER TO RUNTIME") && !strncasecmp("LOAD SCHEDULER TO RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SCHEDULER TO RUNTIME") - 1 && !strncasecmp("LOAD SCHEDULER TO RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD SCHEDULER TO RUN") && !strncasecmp("LOAD SCHEDULER TO RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SCHEDULER TO RUN") - 1 && !strncasecmp("LOAD SCHEDULER TO RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1603,7 +1603,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD SCHEDULER FROM CONFIG") && !strncasecmp("LOAD SCHEDULER FROM CONFIG",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SCHEDULER FROM CONFIG") - 1 && !strncasecmp("LOAD SCHEDULER FROM CONFIG",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); if (GloVars.configfile_open) { @@ -1631,13 +1631,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("SAVE SCHEDULER TO MEMORY") && !strncasecmp("SAVE SCHEDULER TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE SCHEDULER TO MEMORY") - 1 && !strncasecmp("SAVE SCHEDULER TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE SCHEDULER TO MEM") && !strncasecmp("SAVE SCHEDULER TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE SCHEDULER TO MEM") - 1 && !strncasecmp("SAVE SCHEDULER TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE SCHEDULER FROM RUNTIME") && !strncasecmp("SAVE SCHEDULER FROM RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE SCHEDULER FROM RUNTIME") - 1 && !strncasecmp("SAVE SCHEDULER FROM RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE SCHEDULER FROM RUN") && !strncasecmp("SAVE SCHEDULER FROM RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE SCHEDULER FROM RUN") - 1 && !strncasecmp("SAVE SCHEDULER FROM RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1702,11 +1702,11 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query #ifdef PROXYSQLCLICKHOUSE if ( ( GloVars.global.clickhouse_server == true ) && (query_no_space_length>22) && ( (!strncasecmp("SAVE CLICKHOUSE USERS ", query_no_space, 22)) || (!strncasecmp("LOAD CLICKHOUSE USERS ", query_no_space, 22))) ) { if ( - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO MEMORY") && !strncasecmp("LOAD CLICKHOUSE USERS TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE USERS TO MEMORY") - 1 && !strncasecmp("LOAD CLICKHOUSE USERS TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO MEM") && !strncasecmp("LOAD CLICKHOUSE USERS TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE USERS TO MEM") - 1 && !strncasecmp("LOAD CLICKHOUSE USERS TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS FROM DISK") && !strncasecmp("LOAD CLICKHOUSE USERS FROM DISK",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE USERS FROM DISK") - 1 && !strncasecmp("LOAD CLICKHOUSE USERS FROM DISK",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1717,11 +1717,11 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM MEMORY") && !strncasecmp("SAVE CLICKHOUSE USERS FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE USERS FROM MEMORY") - 1 && !strncasecmp("SAVE CLICKHOUSE USERS FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM MEM") && !strncasecmp("SAVE CLICKHOUSE USERS FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE USERS FROM MEM") - 1 && !strncasecmp("SAVE CLICKHOUSE USERS FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS TO DISK") && !strncasecmp("SAVE CLICKHOUSE USERS TO DISK",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE USERS TO DISK") - 1 && !strncasecmp("SAVE CLICKHOUSE USERS TO DISK",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1732,13 +1732,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS FROM MEMORY") && !strncasecmp("LOAD CLICKHOUSE USERS FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE USERS FROM MEMORY") - 1 && !strncasecmp("LOAD CLICKHOUSE USERS FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS FROM MEM") && !strncasecmp("LOAD CLICKHOUSE USERS FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE USERS FROM MEM") - 1 && !strncasecmp("LOAD CLICKHOUSE USERS FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO RUNTIME") && !strncasecmp("LOAD CLICKHOUSE USERS TO RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE USERS TO RUNTIME") - 1 && !strncasecmp("LOAD CLICKHOUSE USERS TO RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD CLICKHOUSE USERS TO RUN") && !strncasecmp("LOAD CLICKHOUSE USERS TO RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE USERS TO RUN") - 1 && !strncasecmp("LOAD CLICKHOUSE USERS TO RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1749,13 +1749,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS TO MEMORY") && !strncasecmp("SAVE CLICKHOUSE USERS TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE USERS TO MEMORY") - 1 && !strncasecmp("SAVE CLICKHOUSE USERS TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS TO MEM") && !strncasecmp("SAVE CLICKHOUSE USERS TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE USERS TO MEM") - 1 && !strncasecmp("SAVE CLICKHOUSE USERS TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM RUNTIME") && !strncasecmp("SAVE CLICKHOUSE USERS FROM RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE USERS FROM RUNTIME") - 1 && !strncasecmp("SAVE CLICKHOUSE USERS FROM RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE CLICKHOUSE USERS FROM RUN") && !strncasecmp("SAVE CLICKHOUSE USERS FROM RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE USERS FROM RUN") - 1 && !strncasecmp("SAVE CLICKHOUSE USERS FROM RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1792,7 +1792,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query return false; } - if ((query_no_space_length>17) && + if ((query_no_space_length>17) && ((!strncasecmp("SAVE MYSQL USERS ", query_no_space, 17)) || (!strncasecmp("LOAD MYSQL USERS ", query_no_space, 17)) || ((!strncasecmp("SAVE PGSQL USERS ", query_no_space, 17)) || (!strncasecmp("LOAD PGSQL USERS ", query_no_space, 17))))) { @@ -1802,7 +1802,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query tuple, vector>& t = load_save_disk_commands[modname]; if ( is_admin_command_or_alias(get<1>(t), query_no_space, query_no_space_length) ) { ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; - + if (is_pgsql) SPA->flush_pgsql_users__from_disk_to_memory(); else @@ -1819,7 +1819,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query SPA->flush_pgsql_users__from_memory_to_disk(); else SPA->flush_mysql_users__from_memory_to_disk(); - + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Saving %s to DISK\n", modname.c_str()); SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); return false; @@ -1844,7 +1844,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD MYSQL USERS FROM CONFIG") && (!strncasecmp("LOAD MYSQL USERS FROM CONFIG",query_no_space, query_no_space_length) || + (query_no_space_length==sizeof("LOAD MYSQL USERS FROM CONFIG") - 1 && (!strncasecmp("LOAD MYSQL USERS FROM CONFIG",query_no_space, query_no_space_length) || !strncasecmp("LOAD PGSQL USERS FROM CONFIG", query_no_space, query_no_space_length))) ) { proxy_info("Received %s command\n", query_no_space); @@ -1912,11 +1912,11 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query if ((query_no_space_length>28) && ( (!strncasecmp("SAVE SQLITESERVER VARIABLES ", query_no_space, 28)) || (!strncasecmp("LOAD SQLITESERVER VARIABLES ", query_no_space, 28))) ) { if ( - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO MEMORY") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SQLITESERVER VARIABLES TO MEMORY") - 1 && !strncasecmp("LOAD SQLITESERVER VARIABLES TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO MEM") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SQLITESERVER VARIABLES TO MEM") - 1 && !strncasecmp("LOAD SQLITESERVER VARIABLES TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES FROM DISK") && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM DISK",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SQLITESERVER VARIABLES FROM DISK") - 1 && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM DISK",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); l_free(*ql,*q); @@ -1926,11 +1926,11 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM MEMORY") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE SQLITESERVER VARIABLES FROM MEMORY") - 1 && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM MEM") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE SQLITESERVER VARIABLES FROM MEM") - 1 && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES TO DISK") && !strncasecmp("SAVE SQLITESERVER VARIABLES TO DISK",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE SQLITESERVER VARIABLES TO DISK") - 1 && !strncasecmp("SAVE SQLITESERVER VARIABLES TO DISK",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); l_free(*ql,*q); @@ -1940,13 +1940,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES FROM MEMORY") && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SQLITESERVER VARIABLES FROM MEMORY") - 1 && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES FROM MEM") && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SQLITESERVER VARIABLES FROM MEM") - 1 && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO RUNTIME") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SQLITESERVER VARIABLES TO RUNTIME") - 1 && !strncasecmp("LOAD SQLITESERVER VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES TO RUN") && !strncasecmp("LOAD SQLITESERVER VARIABLES TO RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD SQLITESERVER VARIABLES TO RUN") - 1 && !strncasecmp("LOAD SQLITESERVER VARIABLES TO RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1957,13 +1957,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES TO MEMORY") && !strncasecmp("SAVE SQLITESERVER VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE SQLITESERVER VARIABLES TO MEMORY") - 1 && !strncasecmp("SAVE SQLITESERVER VARIABLES TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES TO MEM") && !strncasecmp("SAVE SQLITESERVER VARIABLES TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE SQLITESERVER VARIABLES TO MEM") - 1 && !strncasecmp("SAVE SQLITESERVER VARIABLES TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM RUNTIME") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE SQLITESERVER VARIABLES FROM RUNTIME") - 1 && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE SQLITESERVER VARIABLES FROM RUN") && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE SQLITESERVER VARIABLES FROM RUN") - 1 && !strncasecmp("SAVE SQLITESERVER VARIABLES FROM RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1977,11 +1977,11 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query if ((query_no_space_length>26) && ( (!strncasecmp("SAVE CLICKHOUSE VARIABLES ", query_no_space, 26)) || (!strncasecmp("LOAD CLICKHOUSE VARIABLES ", query_no_space, 26))) ) { if ( - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO MEMORY") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE VARIABLES TO MEMORY") - 1 && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO MEM") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE VARIABLES TO MEM") - 1 && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES FROM DISK") && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM DISK",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE VARIABLES FROM DISK") - 1 && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM DISK",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); l_free(*ql,*q); @@ -1991,11 +1991,11 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM MEMORY") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE VARIABLES FROM MEMORY") - 1 && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM MEM") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE VARIABLES FROM MEM") - 1 && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES TO DISK") && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO DISK",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE VARIABLES TO DISK") - 1 && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO DISK",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); l_free(*ql,*q); @@ -2005,13 +2005,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES FROM MEMORY") && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE VARIABLES FROM MEMORY") - 1 && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES FROM MEM") && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE VARIABLES FROM MEM") - 1 && !strncasecmp("LOAD CLICKHOUSE VARIABLES FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO RUNTIME") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE VARIABLES TO RUNTIME") - 1 && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD CLICKHOUSE VARIABLES TO RUN") && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD CLICKHOUSE VARIABLES TO RUN") - 1 && !strncasecmp("LOAD CLICKHOUSE VARIABLES TO RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -2022,13 +2022,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES TO MEMORY") && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE VARIABLES TO MEMORY") - 1 && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES TO MEM") && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE VARIABLES TO MEM") - 1 && !strncasecmp("SAVE CLICKHOUSE VARIABLES TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM RUNTIME") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE VARIABLES FROM RUNTIME") - 1 && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE CLICKHOUSE VARIABLES FROM RUN") && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE CLICKHOUSE VARIABLES FROM RUN") - 1 && !strncasecmp("SAVE CLICKHOUSE VARIABLES FROM RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -2042,13 +2042,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query if (GloMyLdapAuth) { if ((query_no_space_length>20) && ( (!strncasecmp("SAVE LDAP VARIABLES ", query_no_space, 20)) || (!strncasecmp("LOAD LDAP VARIABLES ", query_no_space, 20))) ) { - + if ( - (query_no_space_length==strlen("LOAD LDAP VARIABLES TO MEMORY") && !strncasecmp("LOAD LDAP VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD LDAP VARIABLES TO MEMORY") - 1 && !strncasecmp("LOAD LDAP VARIABLES TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD LDAP VARIABLES TO MEM") && !strncasecmp("LOAD LDAP VARIABLES TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD LDAP VARIABLES TO MEM") - 1 && !strncasecmp("LOAD LDAP VARIABLES TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD LDAP VARIABLES FROM DISK") && !strncasecmp("LOAD LDAP VARIABLES FROM DISK",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD LDAP VARIABLES FROM DISK") - 1 && !strncasecmp("LOAD LDAP VARIABLES FROM DISK",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); l_free(*ql,*q); @@ -2056,13 +2056,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query *ql=strlen(*q)+1; return true; } - + if ( - (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM MEMORY") && !strncasecmp("SAVE LDAP VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE LDAP VARIABLES FROM MEMORY") - 1 && !strncasecmp("SAVE LDAP VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM MEM") && !strncasecmp("SAVE LDAP VARIABLES FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE LDAP VARIABLES FROM MEM") - 1 && !strncasecmp("SAVE LDAP VARIABLES FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE LDAP VARIABLES TO DISK") && !strncasecmp("SAVE LDAP VARIABLES TO DISK",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE LDAP VARIABLES TO DISK") - 1 && !strncasecmp("SAVE LDAP VARIABLES TO DISK",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); l_free(*ql,*q); @@ -2070,15 +2070,15 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query *ql=strlen(*q)+1; return true; } - + if ( - (query_no_space_length==strlen("LOAD LDAP VARIABLES FROM MEMORY") && !strncasecmp("LOAD LDAP VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD LDAP VARIABLES FROM MEMORY") - 1 && !strncasecmp("LOAD LDAP VARIABLES FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD LDAP VARIABLES FROM MEM") && !strncasecmp("LOAD LDAP VARIABLES FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD LDAP VARIABLES FROM MEM") - 1 && !strncasecmp("LOAD LDAP VARIABLES FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD LDAP VARIABLES TO RUNTIME") && !strncasecmp("LOAD LDAP VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD LDAP VARIABLES TO RUNTIME") - 1 && !strncasecmp("LOAD LDAP VARIABLES TO RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD LDAP VARIABLES TO RUN") && !strncasecmp("LOAD LDAP VARIABLES TO RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD LDAP VARIABLES TO RUN") - 1 && !strncasecmp("LOAD LDAP VARIABLES TO RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -2091,15 +2091,15 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query SPA->send_ok_msg_to_client(sess, info, 0, query_no_space); return false; } - + if ( - (query_no_space_length==strlen("SAVE LDAP VARIABLES TO MEMORY") && !strncasecmp("SAVE LDAP VARIABLES TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE LDAP VARIABLES TO MEMORY") - 1 && !strncasecmp("SAVE LDAP VARIABLES TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE LDAP VARIABLES TO MEM") && !strncasecmp("SAVE LDAP VARIABLES TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE LDAP VARIABLES TO MEM") - 1 && !strncasecmp("SAVE LDAP VARIABLES TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM RUNTIME") && !strncasecmp("SAVE LDAP VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE LDAP VARIABLES FROM RUNTIME") - 1 && !strncasecmp("SAVE LDAP VARIABLES FROM RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE LDAP VARIABLES FROM RUN") && !strncasecmp("SAVE LDAP VARIABLES FROM RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE LDAP VARIABLES FROM RUN") - 1 && !strncasecmp("SAVE LDAP VARIABLES FROM RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -2211,11 +2211,11 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD MYSQL VARIABLES FROM CONFIG") && !strncasecmp("LOAD MYSQL VARIABLES FROM CONFIG",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD PGSQL VARIABLES FROM CONFIG") && !strncasecmp("LOAD PGSQL VARIABLES FROM CONFIG", query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD SQLITESERVER VARIABLES FROM CONFIG") && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM CONFIG", query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD TSDB VARIABLES FROM CONFIG") && !strncasecmp("LOAD TSDB VARIABLES FROM CONFIG", query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD GENAI VARIABLES FROM CONFIG") && !strncasecmp("LOAD GENAI VARIABLES FROM CONFIG", query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD MYSQL VARIABLES FROM CONFIG") - 1 && !strncasecmp("LOAD MYSQL VARIABLES FROM CONFIG",query_no_space, query_no_space_length)) || + (query_no_space_length==sizeof("LOAD PGSQL VARIABLES FROM CONFIG") - 1 && !strncasecmp("LOAD PGSQL VARIABLES FROM CONFIG", query_no_space, query_no_space_length)) || + (query_no_space_length==sizeof("LOAD SQLITESERVER VARIABLES FROM CONFIG") - 1 && !strncasecmp("LOAD SQLITESERVER VARIABLES FROM CONFIG", query_no_space, query_no_space_length)) || + (query_no_space_length==sizeof("LOAD TSDB VARIABLES FROM CONFIG") - 1 && !strncasecmp("LOAD TSDB VARIABLES FROM CONFIG", query_no_space, query_no_space_length)) || + (query_no_space_length==sizeof("LOAD GENAI VARIABLES FROM CONFIG") - 1 && !strncasecmp("LOAD GENAI VARIABLES FROM CONFIG", query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); if (GloVars.configfile_open) { @@ -2348,9 +2348,9 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query return false; } } - + if ( - (query_no_space_length==strlen("LOAD MYSQL SERVERS FROM CONFIG") && (!strncasecmp("LOAD MYSQL SERVERS FROM CONFIG",query_no_space, query_no_space_length) || + (query_no_space_length==sizeof("LOAD MYSQL SERVERS FROM CONFIG") - 1 && (!strncasecmp("LOAD MYSQL SERVERS FROM CONFIG",query_no_space, query_no_space_length) || !strncasecmp("LOAD PGSQL SERVERS FROM CONFIG", query_no_space, query_no_space_length) ))) { proxy_info("Received %s command\n", query_no_space); @@ -2434,13 +2434,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query return false; */ if ( - (query_no_space_length==strlen("LOAD PROXYSQL SERVERS FROM MEMORY") && !strncasecmp("LOAD PROXYSQL SERVERS FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD PROXYSQL SERVERS FROM MEMORY") - 1 && !strncasecmp("LOAD PROXYSQL SERVERS FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD PROXYSQL SERVERS FROM MEM") && !strncasecmp("LOAD PROXYSQL SERVERS FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD PROXYSQL SERVERS FROM MEM") - 1 && !strncasecmp("LOAD PROXYSQL SERVERS FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD PROXYSQL SERVERS TO RUNTIME") && !strncasecmp("LOAD PROXYSQL SERVERS TO RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD PROXYSQL SERVERS TO RUNTIME") - 1 && !strncasecmp("LOAD PROXYSQL SERVERS TO RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD PROXYSQL SERVERS TO RUN") && !strncasecmp("LOAD PROXYSQL SERVERS TO RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD PROXYSQL SERVERS TO RUN") - 1 && !strncasecmp("LOAD PROXYSQL SERVERS TO RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -2460,13 +2460,13 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query return false; } if ( - (query_no_space_length==strlen("SAVE PROXYSQL SERVERS TO MEMORY") && !strncasecmp("SAVE PROXYSQL SERVERS TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE PROXYSQL SERVERS TO MEMORY") - 1 && !strncasecmp("SAVE PROXYSQL SERVERS TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE PROXYSQL SERVERS TO MEM") && !strncasecmp("SAVE PROXYSQL SERVERS TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE PROXYSQL SERVERS TO MEM") - 1 && !strncasecmp("SAVE PROXYSQL SERVERS TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE PROXYSQL SERVERS FROM RUNTIME") && !strncasecmp("SAVE PROXYSQL SERVERS FROM RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE PROXYSQL SERVERS FROM RUNTIME") - 1 && !strncasecmp("SAVE PROXYSQL SERVERS FROM RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE PROXYSQL SERVERS FROM RUN") && !strncasecmp("SAVE PROXYSQL SERVERS FROM RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE PROXYSQL SERVERS FROM RUN") - 1 && !strncasecmp("SAVE PROXYSQL SERVERS FROM RUN",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -2487,7 +2487,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD PROXYSQL SERVERS FROM CONFIG") && !strncasecmp("LOAD PROXYSQL SERVERS FROM CONFIG",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD PROXYSQL SERVERS FROM CONFIG") - 1 && !strncasecmp("LOAD PROXYSQL SERVERS FROM CONFIG",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); if (GloVars.configfile_open) { @@ -2532,7 +2532,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query return false; if ( - (query_no_space_length==strlen("LOAD MYSQL FIREWALL FROM CONFIG") && (!strncasecmp("LOAD MYSQL FIREWALL FROM CONFIG",query_no_space, query_no_space_length) || + (query_no_space_length==sizeof("LOAD MYSQL FIREWALL FROM CONFIG") - 1 && (!strncasecmp("LOAD MYSQL FIREWALL FROM CONFIG",query_no_space, query_no_space_length) || !strncasecmp("LOAD PGSQL FIREWALL FROM CONFIG", query_no_space, query_no_space_length))) ) { proxy_info("Received %s command\n", query_no_space); @@ -2566,21 +2566,21 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD MYSQL FIREWALL FROM MEMORY") && !strncasecmp("LOAD MYSQL FIREWALL FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD MYSQL FIREWALL FROM MEMORY") - 1 && !strncasecmp("LOAD MYSQL FIREWALL FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD MYSQL FIREWALL FROM MEM") && !strncasecmp("LOAD MYSQL FIREWALL FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD MYSQL FIREWALL FROM MEM") - 1 && !strncasecmp("LOAD MYSQL FIREWALL FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD MYSQL FIREWALL TO RUNTIME") && !strncasecmp("LOAD MYSQL FIREWALL TO RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD MYSQL FIREWALL TO RUNTIME") - 1 && !strncasecmp("LOAD MYSQL FIREWALL TO RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD MYSQL FIREWALL TO RUN") && !strncasecmp("LOAD MYSQL FIREWALL TO RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD MYSQL FIREWALL TO RUN") - 1 && !strncasecmp("LOAD MYSQL FIREWALL TO RUN",query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("LOAD PGSQL FIREWALL FROM MEMORY") && !strncasecmp("LOAD PGSQL FIREWALL FROM MEMORY", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("LOAD PGSQL FIREWALL FROM MEMORY") - 1 && !strncasecmp("LOAD PGSQL FIREWALL FROM MEMORY", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("LOAD PGSQL FIREWALL FROM MEM") && !strncasecmp("LOAD PGSQL FIREWALL FROM MEM", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("LOAD PGSQL FIREWALL FROM MEM") - 1 && !strncasecmp("LOAD PGSQL FIREWALL FROM MEM", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("LOAD PGSQL FIREWALL TO RUNTIME") && !strncasecmp("LOAD PGSQL FIREWALL TO RUNTIME", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("LOAD PGSQL FIREWALL TO RUNTIME") - 1 && !strncasecmp("LOAD PGSQL FIREWALL TO RUNTIME", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("LOAD PGSQL FIREWALL TO RUN") && !strncasecmp("LOAD PGSQL FIREWALL TO RUN", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("LOAD PGSQL FIREWALL TO RUN") - 1 && !strncasecmp("LOAD PGSQL FIREWALL TO RUN", query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -2595,21 +2595,21 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("SAVE MYSQL FIREWALL TO MEMORY") && !strncasecmp("SAVE MYSQL FIREWALL TO MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE MYSQL FIREWALL TO MEMORY") - 1 && !strncasecmp("SAVE MYSQL FIREWALL TO MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE MYSQL FIREWALL TO MEM") && !strncasecmp("SAVE MYSQL FIREWALL TO MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE MYSQL FIREWALL TO MEM") - 1 && !strncasecmp("SAVE MYSQL FIREWALL TO MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE MYSQL FIREWALL FROM RUNTIME") && !strncasecmp("SAVE MYSQL FIREWALL FROM RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE MYSQL FIREWALL FROM RUNTIME") - 1 && !strncasecmp("SAVE MYSQL FIREWALL FROM RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SAVE MYSQL FIREWALL FROM RUN") && !strncasecmp("SAVE MYSQL FIREWALL FROM RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SAVE MYSQL FIREWALL FROM RUN") - 1 && !strncasecmp("SAVE MYSQL FIREWALL FROM RUN",query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("SAVE PGSQL FIREWALL TO MEMORY") && !strncasecmp("SAVE PGSQL FIREWALL TO MEMORY", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE PGSQL FIREWALL TO MEMORY") - 1 && !strncasecmp("SAVE PGSQL FIREWALL TO MEMORY", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("SAVE PGSQL FIREWALL TO MEM") && !strncasecmp("SAVE PGSQL FIREWALL TO MEM", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE PGSQL FIREWALL TO MEM") - 1 && !strncasecmp("SAVE PGSQL FIREWALL TO MEM", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("SAVE PGSQL FIREWALL FROM RUNTIME") && !strncasecmp("SAVE PGSQL FIREWALL FROM RUNTIME", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE PGSQL FIREWALL FROM RUNTIME") - 1 && !strncasecmp("SAVE PGSQL FIREWALL FROM RUNTIME", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("SAVE PGSQL FIREWALL FROM RUN") && !strncasecmp("SAVE PGSQL FIREWALL FROM RUN", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE PGSQL FIREWALL FROM RUN") - 1 && !strncasecmp("SAVE PGSQL FIREWALL FROM RUN", query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); @@ -2635,7 +2635,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query return false; if ( - (query_no_space_length==strlen("LOAD MYSQL QUERY RULES FROM CONFIG") && (!strncasecmp("LOAD MYSQL QUERY RULES FROM CONFIG",query_no_space, query_no_space_length) || + (query_no_space_length==sizeof("LOAD MYSQL QUERY RULES FROM CONFIG") - 1 && (!strncasecmp("LOAD MYSQL QUERY RULES FROM CONFIG",query_no_space, query_no_space_length) || !strncasecmp("LOAD PGSQL QUERY RULES FROM CONFIG", query_no_space, query_no_space_length))) ) { proxy_info("Received %s command\n", query_no_space); @@ -2671,27 +2671,27 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD MYSQL QUERY RULES FROM MEMORY") && !strncasecmp("LOAD MYSQL QUERY RULES FROM MEMORY",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD MYSQL QUERY RULES FROM MEMORY") - 1 && !strncasecmp("LOAD MYSQL QUERY RULES FROM MEMORY",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD MYSQL QUERY RULES FROM MEM") && !strncasecmp("LOAD MYSQL QUERY RULES FROM MEM",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD MYSQL QUERY RULES FROM MEM") - 1 && !strncasecmp("LOAD MYSQL QUERY RULES FROM MEM",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD MYSQL QUERY RULES TO RUNTIME") && !strncasecmp("LOAD MYSQL QUERY RULES TO RUNTIME",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD MYSQL QUERY RULES TO RUNTIME") - 1 && !strncasecmp("LOAD MYSQL QUERY RULES TO RUNTIME",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("LOAD MYSQL QUERY RULES TO RUN") && !strncasecmp("LOAD MYSQL QUERY RULES TO RUN",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD MYSQL QUERY RULES TO RUN") - 1 && !strncasecmp("LOAD MYSQL QUERY RULES TO RUN",query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("LOAD PGSQL QUERY RULES FROM MEMORY") && !strncasecmp("LOAD PGSQL QUERY RULES FROM MEMORY", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("LOAD PGSQL QUERY RULES FROM MEMORY") - 1 && !strncasecmp("LOAD PGSQL QUERY RULES FROM MEMORY", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("LOAD PGSQL QUERY RULES FROM MEM") && !strncasecmp("LOAD PGSQL QUERY RULES FROM MEM", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("LOAD PGSQL QUERY RULES FROM MEM") - 1 && !strncasecmp("LOAD PGSQL QUERY RULES FROM MEM", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("LOAD PGSQL QUERY RULES TO RUNTIME") && !strncasecmp("LOAD PGSQL QUERY RULES TO RUNTIME", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("LOAD PGSQL QUERY RULES TO RUNTIME") - 1 && !strncasecmp("LOAD PGSQL QUERY RULES TO RUNTIME", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("LOAD PGSQL QUERY RULES TO RUN") && !strncasecmp("LOAD PGSQL QUERY RULES TO RUN", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("LOAD PGSQL QUERY RULES TO RUN") - 1 && !strncasecmp("LOAD PGSQL QUERY RULES TO RUN", query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; char* err = NULL; - + if (query_no_space[5] == 'P' || query_no_space[5] == 'p') err = SPA->load_pgsql_query_rules_to_runtime(); else @@ -2702,7 +2702,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded pgsql query rules to RUNTIME\n"); else proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded mysql query rules to RUNTIME\n"); - + SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); } else { SPA->send_error_msg_to_client(sess, err); @@ -2711,21 +2711,21 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length == strlen("SAVE MYSQL QUERY RULES TO MEMORY") && !strncasecmp("SAVE MYSQL QUERY RULES TO MEMORY", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE MYSQL QUERY RULES TO MEMORY") - 1 && !strncasecmp("SAVE MYSQL QUERY RULES TO MEMORY", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("SAVE MYSQL QUERY RULES TO MEM") && !strncasecmp("SAVE MYSQL QUERY RULES TO MEM", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE MYSQL QUERY RULES TO MEM") - 1 && !strncasecmp("SAVE MYSQL QUERY RULES TO MEM", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("SAVE MYSQL QUERY RULES FROM RUNTIME") && !strncasecmp("SAVE MYSQL QUERY RULES FROM RUNTIME", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE MYSQL QUERY RULES FROM RUNTIME") - 1 && !strncasecmp("SAVE MYSQL QUERY RULES FROM RUNTIME", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("SAVE MYSQL QUERY RULES FROM RUN") && !strncasecmp("SAVE MYSQL QUERY RULES FROM RUN", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE MYSQL QUERY RULES FROM RUN") - 1 && !strncasecmp("SAVE MYSQL QUERY RULES FROM RUN", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("SAVE PGSQL QUERY RULES TO MEMORY") && !strncasecmp("SAVE PGSQL QUERY RULES TO MEMORY", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE PGSQL QUERY RULES TO MEMORY") - 1 && !strncasecmp("SAVE PGSQL QUERY RULES TO MEMORY", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("SAVE PGSQL QUERY RULES TO MEM") && !strncasecmp("SAVE PGSQL QUERY RULES TO MEM", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE PGSQL QUERY RULES TO MEM") - 1 && !strncasecmp("SAVE PGSQL QUERY RULES TO MEM", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("SAVE PGSQL QUERY RULES FROM RUNTIME") && !strncasecmp("SAVE PGSQL QUERY RULES FROM RUNTIME", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE PGSQL QUERY RULES FROM RUNTIME") - 1 && !strncasecmp("SAVE PGSQL QUERY RULES FROM RUNTIME", query_no_space, query_no_space_length)) || - (query_no_space_length == strlen("SAVE PGSQL QUERY RULES FROM RUN") && !strncasecmp("SAVE PGSQL QUERY RULES FROM RUN", query_no_space, query_no_space_length)) + (query_no_space_length == sizeof("SAVE PGSQL QUERY RULES FROM RUN") - 1 && !strncasecmp("SAVE PGSQL QUERY RULES FROM RUN", query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); @@ -2770,7 +2770,7 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } if ( - (query_no_space_length==strlen("LOAD ADMIN VARIABLES FROM CONFIG") && !strncasecmp("LOAD ADMIN VARIABLES FROM CONFIG",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("LOAD ADMIN VARIABLES FROM CONFIG") - 1 && !strncasecmp("LOAD ADMIN VARIABLES FROM CONFIG",query_no_space, query_no_space_length)) ) { proxy_info("Received %s command\n", query_no_space); if (GloVars.configfile_open) { @@ -2807,8 +2807,8 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query } - if (!strncasecmp("SAVE CONFIG TO FILE", query_no_space, strlen("SAVE CONFIG TO FILE"))) { - std::string fileName = query_no_space + strlen("SAVE CONFIG TO FILE"); + if (!strncasecmp("SAVE CONFIG TO FILE", query_no_space, sizeof("SAVE CONFIG TO FILE") - 1)) { + std::string fileName = query_no_space + sizeof("SAVE CONFIG TO FILE") - 1; fileName.erase(0, fileName.find_first_not_of("\t\n\v\f\r ")); fileName.erase(fileName.find_last_not_of("\t\n\v\f\r ") + 1); @@ -2888,7 +2888,7 @@ std::string timediff_timezone_offset() { time(&rawtime); info = localtime(&rawtime); strftime(result, 8, "%z", info); - offset = (result[0] == '+') ? 1 : 0; + offset = (result[0] == '+') ? 1 : 0; time_zone_offset = ((std::string)(result)).substr(offset, 3-offset) + ":" + ((std::string)(result)).substr(3, 2) + ":00"; return time_zone_offset; @@ -2988,7 +2988,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { run_query = false; goto __run_query; } - + switch (hdr.type) { case PG_PKT_STARTUP_V2: case PG_PKT_STARTUP: @@ -3028,7 +3028,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { if (query_no_space_length) { // fix bug #925 - while (query_no_space_length && + while (query_no_space_length && (query_no_space[query_no_space_length-1]==';' || query_no_space[query_no_space_length-1]==' ')) { query_no_space_length--; query_no_space[query_no_space_length]=0; @@ -3120,7 +3120,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } } - if (!strncasecmp("SELECT LOGFILE", query_no_space, strlen("SELECT LOGFILE")) && strcasestr(query_no_space, "FROM INFORMATION_SCHEMA.FILES") != nullptr) { + if (!strncasecmp("SELECT LOGFILE", query_no_space, sizeof("SELECT LOGFILE") - 1) && strcasestr(query_no_space, "FROM INFORMATION_SCHEMA.FILES") != nullptr) { string err_msg = "Invalid command - SELECT .. FROM INFORMATION_SCHEMA.FILES. "; err_msg += "If you are using mysqldump, use --no-tablespaces flag to avoid this error message"; SPA->send_error_msg_to_client(sess, const_cast(err_msg.c_str())); @@ -3129,9 +3129,9 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - if (!strncasecmp("LOGENTRY ", query_no_space, strlen("LOGENTRY "))) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received command LOGENTRY: %s\n", query_no_space + strlen("LOGENTRY ")); - proxy_info("Received command LOGENTRY: %s\n", query_no_space + strlen("LOGENTRY ")); + if (!strncasecmp("LOGENTRY ", query_no_space, sizeof("LOGENTRY ") - 1)) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received command LOGENTRY: %s\n", query_no_space + sizeof("LOGENTRY ") - 1); + proxy_info("Received command LOGENTRY: %s\n", query_no_space + sizeof("LOGENTRY ") - 1); SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); run_query=false; goto __run_query; @@ -3139,7 +3139,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } - if (!strncasecmp("DUMP EVENTSLOG ", query_no_space, strlen("DUMP EVENTSLOG "))) { + if (!strncasecmp("DUMP EVENTSLOG ", query_no_space, sizeof("DUMP EVENTSLOG ") - 1)) { int num_rows = 0; proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received command DUMP EVENTSLOG: %s\n", query_no_space); proxy_info("Received command DUMP EVENTSLOG: %s\n", query_no_space); @@ -3165,7 +3165,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (!strncasecmp("DUMP PGSQL EVENTSLOG ", query_no_space, strlen("DUMP PGSQL EVENTSLOG "))) { + if (!strncasecmp("DUMP PGSQL EVENTSLOG ", query_no_space, sizeof("DUMP PGSQL EVENTSLOG ") - 1)) { int num_rows = 0; proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received command DUMP PGSQL EVENTSLOG: %s\n", query_no_space); proxy_info("Received command DUMP PGSQL EVENTSLOG: %s\n", query_no_space); @@ -3260,7 +3260,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { run_query=false; goto __run_query; } - + } } @@ -3481,7 +3481,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (!strncasecmp("TRUNCATE ", query_no_space, strlen("TRUNCATE "))) { + if (!strncasecmp("TRUNCATE ", query_no_space, sizeof("TRUNCATE ") - 1)) { if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats if (strstr(query_no_space,"stats_mysql_query_digest")) { bool truncate_digest_table = false; @@ -3555,7 +3555,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { // handles 'PURGE stats_mysql_query_digest TO '. // any entry in stats_mysql_query_digest where last_seen is less than will be deleted. - if (!strncasecmp("PURGE ", query_no_space, strlen("PURGE ")) + if (!strncasecmp("PURGE ", query_no_space, sizeof("PURGE ") - 1) && sess->session_type == PROXYSQL_SESSION_ADMIN ) { auto result = parse_command_purge_query_digests(query_no_space, query_no_space_length); @@ -3602,13 +3602,13 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { * PROXYSQL_SIMULATOR mysql_error 1 127.0.0.1 3306 1234 * ``` */ - if (!strncasecmp("PROXYSQL_SIMULATOR ", query_no_space, strlen("PROXYSQL_SIMULATOR "))) { + if (!strncasecmp("PROXYSQL_SIMULATOR ", query_no_space, sizeof("PROXYSQL_SIMULATOR ") - 1)) { if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats proxy_warning("Received PROXYSQL_SIMULATOR command: %s\n", query_no_space); re2::RE2::Options opts = re2::RE2::Options(RE2::Quiet); re2::RE2 pattern("\\s*(\\w+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+):(\\d+) (\\d+)\\s*\\;*", opts); - re2::StringPiece input(query_no_space + strlen("PROXYSQL_SIMULATOR")); + re2::StringPiece input(query_no_space + sizeof("PROXYSQL_SIMULATOR") - 1); std::string command, s_hg, srv_addr, s_port, s_errcode {}; bool c_res = re2::RE2::Consume(&input, pattern, &command, &s_hg, &srv_addr, &s_port, &s_errcode); @@ -3633,7 +3633,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { MySrvC* mysrvc = MyHGM->find_server_in_hg(i_hg, srv_addr, i_port); if (mysrvc != nullptr) { - int backup_shun_on_failures; + int backup_shun_on_failures; backup_shun_on_failures = mysql_thread___shun_on_failures; mysql_thread___shun_on_failures = 1; // Set the error twice to surpass 'mysql_thread___shun_on_failures' value. @@ -3685,7 +3685,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } } #endif // DEBUG - if (!strncasecmp("PROXYSQLTEST ", query_no_space, strlen("PROXYSQLTEST "))) { + if (!strncasecmp("PROXYSQLTEST ", query_no_space, sizeof("PROXYSQLTEST ") - 1)) { if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; SPA->ProxySQL_Test_Handler(SPA, sess, query_no_space, run_query); @@ -3698,7 +3698,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } - if (!strncasecmp("SHOW GLOBAL VARIABLES LIKE 'read_only'", query_no_space, strlen("SHOW GLOBAL VARIABLES LIKE 'read_only'"))) { + if (!strncasecmp("SHOW GLOBAL VARIABLES LIKE 'read_only'", query_no_space, sizeof("SHOW GLOBAL VARIABLES LIKE 'read_only'") - 1)) { l_free(query_length,query); char *q=(char *)"SELECT 'read_only' Variable_name, '%s' Value FROM global_variables WHERE Variable_name='admin-read_only'"; query_length=strlen(q)+5; @@ -3723,7 +3723,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (!strncasecmp("SELECT @@global.read_only", query_no_space, strlen("SELECT @@global.read_only"))) { + if (!strncasecmp("SELECT @@global.read_only", query_no_space, sizeof("SELECT @@global.read_only") - 1)) { l_free(query_length,query); char *q=(char *)"SELECT 'read_only' Variable_name, '%s' Value FROM global_variables WHERE Variable_name='admin-read_only'"; query_length=strlen(q)+5; @@ -3776,7 +3776,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } - if ((query_no_space_length == strlen("SELECT GLOBAL_CHECKSUM()")) && (!strncasecmp("SELECT GLOBAL_CHECKSUM()", query_no_space, strlen("SELECT GLOBAL_CHECKSUM()")))) { + if ((query_no_space_length == sizeof("SELECT GLOBAL_CHECKSUM()") - 1) && (!strncasecmp("SELECT GLOBAL_CHECKSUM()", query_no_space, sizeof("SELECT GLOBAL_CHECKSUM()") - 1))) { char buf[32]; pthread_mutex_lock(&GloVars.checksum_mutex); sprintf(buf,"%lu",GloVars.checksums_values.global_checksum); @@ -3884,91 +3884,91 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { !strncmp("/*!50503 SET ", query_no_space, 13) || !strncmp("/*!50717 SET ", query_no_space, 13) || !strncmp("/*M!100100 SET ", query_no_space, 15) || - !strncmp("/*!50717 SELECT ", query_no_space, strlen("/*!50717 SELECT ")) || - !strncmp("/*!50717 PREPARE ", query_no_space, strlen("/*!50717 PREPARE ")) || - !strncmp("/*!50717 EXECUTE ", query_no_space, strlen("/*!50717 EXECUTE ")) || - !strncmp("/*!50717 DEALLOCATE ", query_no_space, strlen("/*!50717 DEALLOCATE ")) || - !strncmp("/*!50112 SET ", query_no_space, strlen("/*!50112 SET ")) || - !strncmp("/*!50112 PREPARE ", query_no_space, strlen("/*!50112 PREPARE ")) || - !strncmp("/*!50112 EXECUTE ", query_no_space, strlen("/*!50112 EXECUTE ")) || - !strncmp("/*!50112 DEALLOCATE ", query_no_space, strlen("/*!50112 DEALLOCATE ")) || - !strncmp("/*!40000 ALTER TABLE", query_no_space, strlen("/*!40000 ALTER TABLE")) + !strncmp("/*!50717 SELECT ", query_no_space, sizeof("/*!50717 SELECT ") - 1) || + !strncmp("/*!50717 PREPARE ", query_no_space, sizeof("/*!50717 PREPARE ") - 1) || + !strncmp("/*!50717 EXECUTE ", query_no_space, sizeof("/*!50717 EXECUTE ") - 1) || + !strncmp("/*!50717 DEALLOCATE ", query_no_space, sizeof("/*!50717 DEALLOCATE ") - 1) || + !strncmp("/*!50112 SET ", query_no_space, sizeof("/*!50112 SET ") - 1) || + !strncmp("/*!50112 PREPARE ", query_no_space, sizeof("/*!50112 PREPARE ") - 1) || + !strncmp("/*!50112 EXECUTE ", query_no_space, sizeof("/*!50112 EXECUTE ") - 1) || + !strncmp("/*!50112 DEALLOCATE ", query_no_space, sizeof("/*!50112 DEALLOCATE ") - 1) || + !strncmp("/*!40000 ALTER TABLE", query_no_space, sizeof("/*!40000 ALTER TABLE") - 1) || - !strncmp("/*!40100 SET @@SQL_MODE='' */", query_no_space, strlen("/*!40100 SET @@SQL_MODE='' */")) + !strncmp("/*!40100 SET @@SQL_MODE='' */", query_no_space, sizeof("/*!40100 SET @@SQL_MODE='' */") - 1) || - !strncmp("/*!40103 SET TIME_ZONE=", query_no_space, strlen("/*!40103 SET TIME_ZONE=")) + !strncmp("/*!40103 SET TIME_ZONE=", query_no_space, sizeof("/*!40103 SET TIME_ZONE=") - 1) || - !strncmp("LOCK TABLES", query_no_space, strlen("LOCK TABLES")) + !strncmp("LOCK TABLES", query_no_space, sizeof("LOCK TABLES") - 1) || - !strncmp("UNLOCK TABLES", query_no_space, strlen("UNLOCK TABLES")) + !strncmp("UNLOCK TABLES", query_no_space, sizeof("UNLOCK TABLES") - 1) || - !strncmp("SET SQL_QUOTE_SHOW_CREATE=1", query_no_space, strlen("SET SQL_QUOTE_SHOW_CREATE=1")) + !strncmp("SET SQL_QUOTE_SHOW_CREATE=1", query_no_space, sizeof("SET SQL_QUOTE_SHOW_CREATE=1") - 1) || - !strncmp("SET SESSION character_set_results", query_no_space, strlen("SET SESSION character_set_results")) + !strncmp("SET SESSION character_set_results", query_no_space, sizeof("SET SESSION character_set_results") - 1) || - !strncasecmp("FLUSH /*!40101 LOCAL */ TABLES", query_no_space, strlen("FLUSH /*!40101 LOCAL */ TABLES")) + !strncasecmp("FLUSH /*!40101 LOCAL */ TABLES", query_no_space, sizeof("FLUSH /*!40101 LOCAL */ TABLES") - 1) || - !strncasecmp("FLUSH /*!40101 LOCAL */ LOGS", query_no_space, strlen("FLUSH /*!40101 LOCAL */ LOGS")) + !strncasecmp("FLUSH /*!40101 LOCAL */ LOGS", query_no_space, sizeof("FLUSH /*!40101 LOCAL */ LOGS") - 1) || - !strncasecmp("FLUSH TABLES WITH READ LOCK", query_no_space, strlen("FLUSH TABLES WITH READ LOCK")) + !strncasecmp("FLUSH TABLES WITH READ LOCK", query_no_space, sizeof("FLUSH TABLES WITH READ LOCK") - 1) || - !strncasecmp("USE ", query_no_space, strlen("USE ")) // this applies to all clients, not only mysqldump + !strncasecmp("USE ", query_no_space, sizeof("USE ") - 1) // this applies to all clients, not only mysqldump ) { SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); run_query=false; goto __run_query; } - if (query_no_space_length == strlen("SHOW MASTER STATUS") && !strncasecmp("SHOW MASTER STATUS", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("SHOW MASTER STATUS") - 1 && !strncasecmp("SHOW MASTER STATUS", query_no_space, query_no_space_length)) { l_free(query_length, query); query = l_strdup("SELECT '' AS 'File', 0 AS 'Position', '' AS 'Binlog_Do_DB', '' AS 'Binlog_Ignore_DB', '' AS 'Executed_Gtid_Set' WHERE 1=0"); query_length = strlen(query) + 1; goto __run_query; } - if (query_no_space_length == strlen("SHOW BINARY LOG STATUS") && !strncasecmp("SHOW BINARY LOG STATUS", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("SHOW BINARY LOG STATUS") - 1 && !strncasecmp("SHOW BINARY LOG STATUS", query_no_space, query_no_space_length)) { l_free(query_length, query); query = l_strdup("SELECT '' AS 'File', 0 AS 'Position', '' AS 'Binlog_Do_DB', '' AS 'Binlog_Ignore_DB', '' AS 'Executed_Gtid_Set' WHERE 1=0"); query_length = strlen(query) + 1; goto __run_query; } - if (query_no_space_length >= strlen("SHOW FUNCTION STATUS") && !strncasecmp("SHOW FUNCTION STATUS", query_no_space, strlen("SHOW FUNCTION STATUS"))) { + if (query_no_space_length >= sizeof("SHOW FUNCTION STATUS") - 1 && !strncasecmp("SHOW FUNCTION STATUS", query_no_space, sizeof("SHOW FUNCTION STATUS") - 1)) { l_free(query_length, query); query = l_strdup("SELECT '' AS 'Db', '' AS 'Name', '' AS 'Type', '' AS 'Definer', '' AS 'Modified', '' AS 'Created', '' AS 'Security_type', '' AS 'Comment', '' AS 'character_set_client', '' AS 'collation_connection', '' AS 'Database Collation' WHERE 1=0"); query_length = strlen(query) + 1; goto __run_query; } - if (query_no_space_length >= strlen("SHOW PROCEDURE STATUS") && !strncasecmp("SHOW PROCEDURE STATUS", query_no_space, strlen("SHOW PROCEDURE STATUS"))) { + if (query_no_space_length >= sizeof("SHOW PROCEDURE STATUS") - 1 && !strncasecmp("SHOW PROCEDURE STATUS", query_no_space, sizeof("SHOW PROCEDURE STATUS") - 1)) { l_free(query_length, query); query = l_strdup("SELECT '' AS 'Db', '' AS 'Name', '' AS 'Type', '' AS 'Definer', '' AS 'Modified', '' AS 'Created', '' AS 'Security_type', '' AS 'Comment', '' AS 'character_set_client', '' AS 'collation_connection', '' AS 'Database Collation' WHERE 1=0"); query_length = strlen(query) + 1; goto __run_query; } - if (query_no_space_length >= strlen("SHOW TRIGGERS") && !strncasecmp("SHOW TRIGGERS", query_no_space, strlen("SHOW TRIGGERS"))) { + if (query_no_space_length >= sizeof("SHOW TRIGGERS") - 1 && !strncasecmp("SHOW TRIGGERS", query_no_space, sizeof("SHOW TRIGGERS") - 1)) { l_free(query_length, query); query = l_strdup("SELECT '' AS 'Trigger', '' AS 'Event', '' AS 'Table', '' AS 'Statement', '' AS 'Timing', '' AS 'Created', '' AS 'sql_mode', '' AS 'Definer', '' AS 'character_set_client', '' AS 'collation_connection', '' AS 'Database Collation' WHERE 1=0"); query_length = strlen(query) + 1; goto __run_query; } - if (query_no_space_length >= strlen("SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS") && !strncasecmp("SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS", query_no_space, strlen("SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS"))) { + if (query_no_space_length >= sizeof("SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS") - 1 && !strncasecmp("SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS", query_no_space, sizeof("SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS") - 1)) { l_free(query_length, query); query = l_strdup("SELECT '' AS 'TRIGGER_NAME' WHERE 1=0"); query_length = strlen(query) + 1; goto __run_query; } - if (query_no_space_length >= strlen("SHOW EVENTS") && !strncasecmp("SHOW EVENTS", query_no_space, strlen("SHOW EVENTS"))) { + if (query_no_space_length >= sizeof("SHOW EVENTS") - 1 && !strncasecmp("SHOW EVENTS", query_no_space, sizeof("SHOW EVENTS") - 1)) { l_free(query_length, query); query = l_strdup("SELECT '' AS 'Db', '' AS 'Name', '' AS 'Definer', '' AS 'Time zone', '' AS 'Type', '' AS 'Execute at', '' AS 'Interval value', '' AS 'Interval field', '' AS 'Starts', '' AS 'Ends', '' AS 'Status', '' AS 'Originator', '' AS 'character_set_client', '' AS 'collation_connection', '' AS 'Database Collation' WHERE 1=0"); query_length = strlen(query) + 1; goto __run_query; } - if (!strncmp("SHOW STATUS LIKE 'binlog_snapshot_gtid_executed'", query_no_space, strlen("SHOW STATUS LIKE 'binlog_snapshot_gtid_executed'"))) { + if (!strncmp("SHOW STATUS LIKE 'binlog_snapshot_gtid_executed'", query_no_space, sizeof("SHOW STATUS LIKE 'binlog_snapshot_gtid_executed'") - 1)) { l_free(query_length, query); query = l_strdup("SELECT variable_name AS Variable_name, Variable_value AS Value FROM global_variables WHERE 1=0"); query_length = strlen(query)+1; @@ -3980,31 +3980,31 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { query_length = strlen(query)+1; goto __run_query; } - if (!strncmp("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = 'performance_schema' AND table_name = 'session_variables'", query_no_space, strlen("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = 'performance_schema' AND table_name = 'session_variables'"))) { + if (!strncmp("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = 'performance_schema' AND table_name = 'session_variables'", query_no_space, sizeof("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = 'performance_schema' AND table_name = 'session_variables'") - 1)) { l_free(query_length,query); query=l_strdup("SELECT 0 as 'COUNT(*)'"); query_length=strlen(query)+1; goto __run_query; } - if (!strncmp("SHOW VARIABLES LIKE 'gtid\\_mode'", query_no_space, strlen("SHOW VARIABLES LIKE 'gtid\\_mode'"))) { + if (!strncmp("SHOW VARIABLES LIKE 'gtid\\_mode'", query_no_space, sizeof("SHOW VARIABLES LIKE 'gtid\\_mode'") - 1)) { l_free(query_length,query); query=l_strdup("SELECT variable_name Variable_name, Variable_value Value FROM global_variables WHERE Variable_name='gtid_mode'"); query_length=strlen(query)+1; goto __run_query; } - if (!strncmp("select @@collation_database", query_no_space, strlen("select @@collation_database"))) { + if (!strncmp("select @@collation_database", query_no_space, sizeof("select @@collation_database") - 1)) { l_free(query_length,query); query=l_strdup("SELECT Collation '@@collation_database' FROM mysql_collations WHERE Collation='utf8_general_ci' LIMIT 1"); query_length=strlen(query)+1; goto __run_query; } - if (!strncmp("SHOW VARIABLES LIKE 'ndbinfo\\_version'", query_no_space, strlen("SHOW VARIABLES LIKE 'ndbinfo\\_version'"))) { + if (!strncmp("SHOW VARIABLES LIKE 'ndbinfo\\_version'", query_no_space, sizeof("SHOW VARIABLES LIKE 'ndbinfo\\_version'") - 1)) { l_free(query_length,query); query=l_strdup("SELECT variable_name Variable_name, Variable_value Value FROM global_variables WHERE Variable_name='ndbinfo_version'"); query_length=strlen(query)+1; goto __run_query; } - if (!strncasecmp("show table status like '", query_no_space, strlen("show table status like '"))) { + if (!strncasecmp("show table status like '", query_no_space, sizeof("show table status like '") - 1)) { char *strA=query_no_space+24; int strAl=strlen(strA); if (strAl<2) { // error @@ -4018,7 +4018,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { run_query=false; goto __run_query; } - if (!strncasecmp("show fields from ", query_no_space, strlen("show fields from "))) { + if (!strncasecmp("show fields from ", query_no_space, sizeof("show fields from ") - 1)) { char *strA=query_no_space+17; int strAl=strlen(strA); if (strAl==0) { // error @@ -4042,7 +4042,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } // FIXME: this should be removed, it is just a POC for issue #253 . What is important is the call to GloMTH->signal_all_threads(); - if (!strncasecmp("SIGNAL MYSQL THREADS", query_no_space, strlen("SIGNAL MYSQL THREADS"))) { + if (!strncasecmp("SIGNAL MYSQL THREADS", query_no_space, sizeof("SIGNAL MYSQL THREADS") - 1)) { GloMTH->signal_all_threads(); proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received %s command\n", query_no_space); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -4067,7 +4067,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { const char *mb = skip_leading_sql_comments(query_no_space, query_no_space_length); // fix bug #442 - if (!strncmp("SET SQL_SAFE_UPDATES=1", mb, strlen("SET SQL_SAFE_UPDATES=1"))) { + if (!strncmp("SET SQL_SAFE_UPDATES=1", mb, sizeof("SET SQL_SAFE_UPDATES=1") - 1)) { SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); run_query=false; goto __run_query; @@ -4075,25 +4075,25 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { // fix bug #1047 if ( - (!strncasecmp("BEGIN", mb, strlen("BEGIN"))) + (!strncasecmp("BEGIN", mb, sizeof("BEGIN") - 1)) || - (!strncasecmp("START TRANSACTION", mb, strlen("START TRANSACTION"))) + (!strncasecmp("START TRANSACTION", mb, sizeof("START TRANSACTION") - 1)) || - (!strncasecmp("COMMIT", mb, strlen("COMMIT"))) + (!strncasecmp("COMMIT", mb, sizeof("COMMIT") - 1)) || - (!strncasecmp("ROLLBACK", mb, strlen("ROLLBACK"))) + (!strncasecmp("ROLLBACK", mb, sizeof("ROLLBACK") - 1)) || - (!strncasecmp("SET character_set_results", mb, strlen("SET character_set_results"))) + (!strncasecmp("SET character_set_results", mb, sizeof("SET character_set_results") - 1)) || - (!strncasecmp("SET SQL_AUTO_IS_NULL", mb, strlen("SET SQL_AUTO_IS_NULL"))) + (!strncasecmp("SET SQL_AUTO_IS_NULL", mb, sizeof("SET SQL_AUTO_IS_NULL") - 1)) || - (!strncasecmp("SET NAMES", mb, strlen("SET NAMES"))) + (!strncasecmp("SET NAMES", mb, sizeof("SET NAMES") - 1)) || - (!strncasecmp("SET AUTOCOMMIT", mb, strlen("SET AUTOCOMMIT"))) + (!strncasecmp("SET AUTOCOMMIT", mb, sizeof("SET AUTOCOMMIT") - 1)) || - (!strncasecmp("SET @@session.autocommit", mb, strlen("SET @@session.autocommit"))) + (!strncasecmp("SET @@session.autocommit", mb, sizeof("SET @@session.autocommit") - 1)) || - (!strncasecmp("SET LOCK_WAIT_TIMEOUT", mb, strlen("SET LOCK_WAIT_TIMEOUT"))) + (!strncasecmp("SET LOCK_WAIT_TIMEOUT", mb, sizeof("SET LOCK_WAIT_TIMEOUT") - 1)) ) { SPA->send_ok_msg_to_client(sess, NULL, 0, query_no_space); run_query=false; @@ -4102,7 +4102,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } // MySQL client check command for dollars quote support, starting at version '8.1.0'. See #4300. - if (!strncasecmp("SELECT $$", query_no_space, strlen("SELECT $$"))) { + if (!strncasecmp("SELECT $$", query_no_space, sizeof("SELECT $$") - 1)) { pair err_info { get_dollar_quote_error(mysql_thread___server_version) }; SPA->send_error_msg_to_client(sess, const_cast(err_info.second), err_info.first); run_query=false; @@ -4118,7 +4118,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } } - if (!strncasecmp("select concat(@@version, ' ', @@version_comment)", query_no_space, strlen("select concat(@@version, ' ', @@version_comment)"))) { + if (!strncasecmp("select concat(@@version, ' ', @@version_comment)", query_no_space, sizeof("select concat(@@version, ' ', @@version_comment)") - 1)) { l_free(query_length,query); char *q = const_cast("SELECT '%s Admin Module'"); query_length = strlen(q) + strlen(PROXYSQL_VERSION) + 1; @@ -4154,7 +4154,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (!strncasecmp("select @@sql_mode", query_no_space, strlen("select @@sql_mode"))) { + if (!strncasecmp("select @@sql_mode", query_no_space, sizeof("select @@sql_mode") - 1)) { l_free(query_length,query); char *q = const_cast("SELECT \"\" as \"@@sql_mode\""); query_length = strlen(q) + strlen(PROXYSQL_VERSION) + 1; @@ -4164,7 +4164,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } // trivial implementation for 'connection_id()' to support 'mycli'. See #3247 - if (!strncasecmp("select connection_id()", query_no_space, strlen("select connection_id()"))) { + if (!strncasecmp("select connection_id()", query_no_space, sizeof("select connection_id()") - 1)) { l_free(query_length,query); // 'connection_id()' is always forced to be '0' query=l_strdup("SELECT 0 AS 'CONNECTION_ID()'"); @@ -4173,7 +4173,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } // implementation for 'SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())' in order to support'csharp' connector. See #2543 - if (!strncasecmp("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())", query_no_space, strlen("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())"))) { + if (!strncasecmp("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())", query_no_space, sizeof("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP())") - 1)) { l_free(query_length,query); char *query1=(char*)"SELECT '%s' as 'TIMEDIFF(NOW(), UTC_TIMESTAMP()'"; @@ -4199,7 +4199,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { !strncasecmp( "select @@max_allowed_packet, @@character_set_client, @@character_set_connection, @@license, @@sql_mode, @@lower_case_table_names", query_no_space, - strlen("select @@max_allowed_packet, @@character_set_client, @@character_set_connection, @@license, @@sql_mode, @@lower_case_table_names") + sizeof("select @@max_allowed_packet, @@character_set_client, @@character_set_connection, @@license, @@sql_mode, @@lower_case_table_names") - 1 ) ) { l_free(query_length,query); @@ -4235,13 +4235,13 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } } - if (!strncasecmp("SELECT @@version", query_no_space, strlen("SELECT @@version"))) { + if (!strncasecmp("SELECT @@version", query_no_space, sizeof("SELECT @@version") - 1)) { l_free(query_length,query); char *q=(char *)"SELECT '%s' AS '@@version'"; if (GloMyLdapAuth == nullptr) { query_length=strlen(q)+20+strlen(PROXYSQL_VERSION); } else { - query_length=strlen(q)+20+strlen(PROXYSQL_VERSION)+strlen("-Enterprise"); + query_length=strlen(q)+20+strlen(PROXYSQL_VERSION)+sizeof("-Enterprise") - 1; } query=(char *)l_alloc(query_length); if (GloMyLdapAuth == nullptr) { @@ -4252,13 +4252,13 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (!strncasecmp("SELECT version()", query_no_space, strlen("SELECT version()"))) { + if (!strncasecmp("SELECT version()", query_no_space, sizeof("SELECT version()") - 1)) { l_free(query_length,query); char *q=(char *)"SELECT '%s' AS 'version()'"; if (GloMyLdapAuth == nullptr) { query_length=strlen(q)+20+strlen(PROXYSQL_VERSION); } else { - query_length=strlen(q)+20+strlen(PROXYSQL_VERSION)+strlen("-Enterprise"); + query_length=strlen(q)+20+strlen(PROXYSQL_VERSION)+sizeof("-Enterprise") - 1; } query=(char *)l_alloc(query_length); if (GloMyLdapAuth == nullptr) { @@ -4269,9 +4269,9 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in", query_no_space, strlen("SHOW VARIABLES WHERE Variable_name in"))) { + if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in", query_no_space, sizeof("SHOW VARIABLES WHERE Variable_name in") - 1)) { // Allow MariaDB ConnectorJ to connect to Admin #743 - if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','sql_mode')", query_no_space, strlen("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','sql_mode')"))) { + if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','sql_mode')", query_no_space, sizeof("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','sql_mode')") - 1)) { l_free(query_length,query); char *q=(char *)"SELECT 'max_allowed_packet' Variable_name,'4194304' Value UNION ALL SELECT 'sql_mode', 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION' UNION ALL SELECT 'system_time_zone', 'UTC' UNION ALL SELECT 'time_zone','SYSTEM'"; query_length=strlen(q)+20; @@ -4280,7 +4280,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } // Allow MariaDB ConnectorJ 2.4.1 to connect to Admin #2009 - if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','auto_increment_increment')", query_no_space, strlen("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','auto_increment_increment')"))) { + if (!strncasecmp("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','auto_increment_increment')", query_no_space, sizeof("SHOW VARIABLES WHERE Variable_name in ('max_allowed_packet','system_time_zone','time_zone','auto_increment_increment')") - 1)) { l_free(query_length,query); char *q=(char *)"SELECT 'max_allowed_packet' Variable_name,'4194304' Value UNION ALL SELECT 'auto_increment_increment', '1' UNION ALL SELECT 'system_time_zone', 'UTC' UNION ALL SELECT 'time_zone','SYSTEM'"; query_length=strlen(q)+20; @@ -4382,154 +4382,154 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { char *error=NULL; int affected_rows=0; int cols=0; - if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL SERVERS") && !strncasecmp("CHECKSUM DISK MYSQL SERVERS", query_no_space, strlen(query_no_space))){ + if (query_no_space_length==sizeof("CHECKSUM DISK MYSQL SERVERS") - 1 && !strncasecmp("CHECKSUM DISK MYSQL SERVERS", query_no_space, query_no_space_length)){ char *q=(char *)"SELECT * FROM mysql_servers ORDER BY hostgroup_id, hostname, port"; tablename=(char *)"MYSQL SERVERS"; SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL USERS") && !strncasecmp("CHECKSUM DISK MYSQL USERS", query_no_space, strlen(query_no_space))){ + if (query_no_space_length==sizeof("CHECKSUM DISK MYSQL USERS") - 1 && !strncasecmp("CHECKSUM DISK MYSQL USERS", query_no_space, query_no_space_length)){ char *q=(char *)"SELECT * FROM mysql_users ORDER BY username"; tablename=(char *)"MYSQL USERS"; SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL QUERY RULES") && !strncasecmp("CHECKSUM DISK MYSQL QUERY RULES", query_no_space, strlen(query_no_space))){ + if (query_no_space_length==sizeof("CHECKSUM DISK MYSQL QUERY RULES") - 1 && !strncasecmp("CHECKSUM DISK MYSQL QUERY RULES", query_no_space, query_no_space_length)){ char *q=(char *)"SELECT * FROM mysql_query_rules ORDER BY rule_id"; tablename=(char *)"MYSQL QUERY RULES"; SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL VARIABLES") && !strncasecmp("CHECKSUM DISK MYSQL VARIABLES", query_no_space, strlen(query_no_space))){ + if (query_no_space_length==sizeof("CHECKSUM DISK MYSQL VARIABLES") - 1 && !strncasecmp("CHECKSUM DISK MYSQL VARIABLES", query_no_space, query_no_space_length)){ char *q=(char *)"SELECT * FROM global_variables WHERE variable_name LIKE 'mysql-%' ORDER BY variable_name"; tablename=(char *)"MYSQL VARIABLES"; SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if (strlen(query_no_space)==strlen("CHECKSUM DISK MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM DISK MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))){ + if (query_no_space_length==sizeof("CHECKSUM DISK MYSQL REPLICATION HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM DISK MYSQL REPLICATION HOSTGROUPS", query_no_space, query_no_space_length)){ char *q=(char *)"SELECT * FROM mysql_replication_hostgroups ORDER BY writer_hostgroup"; tablename=(char *)"MYSQL REPLICATION HOSTGROUPS"; SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL SERVERS") && !strncasecmp("CHECKSUM MEMORY MYSQL SERVERS", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MYSQL SERVERS") - 1 && !strncasecmp("CHECKSUM MEMORY MYSQL SERVERS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL SERVERS") && !strncasecmp("CHECKSUM MEM MYSQL SERVERS", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MYSQL SERVERS") - 1 && !strncasecmp("CHECKSUM MEM MYSQL SERVERS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL SERVERS") && !strncasecmp("CHECKSUM MYSQL SERVERS", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MYSQL SERVERS") - 1 && !strncasecmp("CHECKSUM MYSQL SERVERS", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM mysql_servers ORDER BY hostgroup_id, hostname, port"; tablename=(char *)"MYSQL SERVERS"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL USERS") && !strncasecmp("CHECKSUM MEMORY MYSQL USERS", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MYSQL USERS") - 1 && !strncasecmp("CHECKSUM MEMORY MYSQL USERS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL USERS") && !strncasecmp("CHECKSUM MEM MYSQL USERS", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MYSQL USERS") - 1 && !strncasecmp("CHECKSUM MEM MYSQL USERS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL USERS") && !strncasecmp("CHECKSUM MYSQL USERS", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MYSQL USERS") - 1 && !strncasecmp("CHECKSUM MYSQL USERS", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM mysql_users ORDER BY username"; tablename=(char *)"MYSQL USERS"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL QUERY RULES") && !strncasecmp("CHECKSUM MEMORY MYSQL QUERY RULES", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MYSQL QUERY RULES") - 1 && !strncasecmp("CHECKSUM MEMORY MYSQL QUERY RULES", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL QUERY RULES") && !strncasecmp("CHECKSUM MEM MYSQL QUERY RULES", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MYSQL QUERY RULES") - 1 && !strncasecmp("CHECKSUM MEM MYSQL QUERY RULES", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL QUERY RULES") && !strncasecmp("CHECKSUM MYSQL QUERY RULES", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MYSQL QUERY RULES") - 1 && !strncasecmp("CHECKSUM MYSQL QUERY RULES", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM mysql_query_rules ORDER BY rule_id"; tablename=(char *)"MYSQL QUERY RULES"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL VARIABLES") && !strncasecmp("CHECKSUM MEMORY MYSQL VARIABLES", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MYSQL VARIABLES") - 1 && !strncasecmp("CHECKSUM MEMORY MYSQL VARIABLES", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL VARIABLES") && !strncasecmp("CHECKSUM MEM MYSQL VARIABLES", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MYSQL VARIABLES") - 1 && !strncasecmp("CHECKSUM MEM MYSQL VARIABLES", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL VARIABLES") && !strncasecmp("CHECKSUM MYSQL VARIABLES", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MYSQL VARIABLES") - 1 && !strncasecmp("CHECKSUM MYSQL VARIABLES", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM global_variables WHERE variable_name LIKE 'mysql-%' ORDER BY variable_name"; tablename=(char *)"MYSQL VARIABLES"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MYSQL REPLICATION HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MEMORY MYSQL REPLICATION HOSTGROUPS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MYSQL REPLICATION HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MEM MYSQL REPLICATION HOSTGROUPS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MYSQL REPLICATION HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MYSQL REPLICATION HOSTGROUPS", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM mysql_replication_hostgroups ORDER BY writer_hostgroup"; tablename=(char *)"MYSQL REPLICATION HOSTGROUPS"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL GROUP REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MYSQL GROUP REPLICATION HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MEMORY MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL GROUP REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MYSQL GROUP REPLICATION HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MEM MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL GROUP REPLICATION HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MYSQL GROUP REPLICATION HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MYSQL GROUP REPLICATION HOSTGROUPS", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM mysql_group_replication_hostgroups ORDER BY writer_hostgroup"; tablename=(char *)"MYSQL GROUP REPLICATION HOSTGROUPS"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL GALERA HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL GALERA HOSTGROUPS", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MYSQL GALERA HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MEMORY MYSQL GALERA HOSTGROUPS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL GALERA HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL GALERA HOSTGROUPS", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MYSQL GALERA HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MEM MYSQL GALERA HOSTGROUPS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL GALERA HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL GALERA HOSTGROUPS", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MYSQL GALERA HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MYSQL GALERA HOSTGROUPS", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM mysql_galera_hostgroups ORDER BY writer_hostgroup"; tablename=(char *)"MYSQL GALERA HOSTGROUPS"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL AURORA HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL AURORA HOSTGROUPS", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MYSQL AURORA HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MEMORY MYSQL AURORA HOSTGROUPS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL AURORA HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL AURORA HOSTGROUPS", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MYSQL AURORA HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MEM MYSQL AURORA HOSTGROUPS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL AURORA HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL AURORA HOSTGROUPS", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MYSQL AURORA HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MYSQL AURORA HOSTGROUPS", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM mysql_aws_aurora_hostgroups ORDER BY writer_hostgroup"; tablename=(char *)"MYSQL AURORA HOSTGROUPS"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL RDS BGD HOSTGROUPS") && !strncasecmp("CHECKSUM MEMORY MYSQL RDS BGD HOSTGROUPS", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MYSQL RDS BGD HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MEMORY MYSQL RDS BGD HOSTGROUPS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL RDS BGD HOSTGROUPS") && !strncasecmp("CHECKSUM MEM MYSQL RDS BGD HOSTGROUPS", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MYSQL RDS BGD HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MEM MYSQL RDS BGD HOSTGROUPS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL RDS BGD HOSTGROUPS") && !strncasecmp("CHECKSUM MYSQL RDS BGD HOSTGROUPS", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MYSQL RDS BGD HOSTGROUPS") - 1 && !strncasecmp("CHECKSUM MYSQL RDS BGD HOSTGROUPS", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM mysql_aws_rds_bgd_hostgroups ORDER BY writer_hostgroup"; tablename=(char *)"MYSQL RDS BGD HOSTGROUPS"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL HOSTGROUP ATTRIBUTES") && !strncasecmp("CHECKSUM MEMORY MYSQL HOSTGROUP ATTRIBUTES", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MYSQL HOSTGROUP ATTRIBUTES") - 1 && !strncasecmp("CHECKSUM MEMORY MYSQL HOSTGROUP ATTRIBUTES", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL HOSTGROUP ATTRIBUTES") && !strncasecmp("CHECKSUM MEM MYSQL HOSTGROUP ATTRIBUTES", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MYSQL HOSTGROUP ATTRIBUTES") - 1 && !strncasecmp("CHECKSUM MEM MYSQL HOSTGROUP ATTRIBUTES", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL HOSTGROUP ATTRIBUTES") && !strncasecmp("CHECKSUM MYSQL HOSTGROUP ATTRIBUTES", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MYSQL HOSTGROUP ATTRIBUTES") - 1 && !strncasecmp("CHECKSUM MYSQL HOSTGROUP ATTRIBUTES", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM mysql_hostgroup_attributes ORDER BY hostgroup_id"; tablename=(char *)"MYSQL HOSTGROUP ATTRIBUTES"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MYSQL SERVERS SSL PARAMS") && !strncasecmp("CHECKSUM MEMORY MYSQL SERVERS SSL PARAMS", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MYSQL SERVERS SSL PARAMS") - 1 && !strncasecmp("CHECKSUM MEMORY MYSQL SERVERS SSL PARAMS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MYSQL SERVERS SSL PARAMS") && !strncasecmp("CHECKSUM MEM MYSQL SERVERS SSL PARAMS", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MYSQL SERVERS SSL PARAMS") - 1 && !strncasecmp("CHECKSUM MEM MYSQL SERVERS SSL PARAMS", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MYSQL SERVERS SSL PARAMS") && !strncasecmp("CHECKSUM MYSQL SERVERS SSL PARAMS", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MYSQL SERVERS SSL PARAMS") - 1 && !strncasecmp("CHECKSUM MYSQL SERVERS SSL PARAMS", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM mysql_servers_ssl_params ORDER BY hostname, port, username"; tablename=(char *)"MYSQL HOSTGROUP ATTRIBUTES"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } // MCP (Model Context Protocol) VARIABLES CHECKSUM - if (strlen(query_no_space)==strlen("CHECKSUM DISK MCP VARIABLES") && !strncasecmp("CHECKSUM DISK MCP VARIABLES", query_no_space, strlen(query_no_space))){ + if (query_no_space_length==sizeof("CHECKSUM DISK MCP VARIABLES") - 1 && !strncasecmp("CHECKSUM DISK MCP VARIABLES", query_no_space, query_no_space_length)){ char *q=(char *)"SELECT * FROM global_variables WHERE variable_name LIKE 'mcp-%' ORDER BY variable_name"; tablename=(char *)"MCP VARIABLES"; SPA->configdb->execute_statement(q, &error, &cols, &affected_rows, &resultset); } - if ((strlen(query_no_space)==strlen("CHECKSUM MEMORY MCP VARIABLES") && !strncasecmp("CHECKSUM MEMORY MCP VARIABLES", query_no_space, strlen(query_no_space))) + if ((query_no_space_length==sizeof("CHECKSUM MEMORY MCP VARIABLES") - 1 && !strncasecmp("CHECKSUM MEMORY MCP VARIABLES", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MEM MCP VARIABLES") && !strncasecmp("CHECKSUM MEM MCP VARIABLES", query_no_space, strlen(query_no_space))) + (query_no_space_length==sizeof("CHECKSUM MEM MCP VARIABLES") - 1 && !strncasecmp("CHECKSUM MEM MCP VARIABLES", query_no_space, query_no_space_length)) || - (strlen(query_no_space)==strlen("CHECKSUM MCP VARIABLES") && !strncasecmp("CHECKSUM MCP VARIABLES", query_no_space, strlen(query_no_space)))){ + (query_no_space_length==sizeof("CHECKSUM MCP VARIABLES") - 1 && !strncasecmp("CHECKSUM MCP VARIABLES", query_no_space, query_no_space_length))){ char *q=(char *)"SELECT * FROM global_variables WHERE variable_name LIKE 'mcp-%' ORDER BY variable_name"; tablename=(char *)"MCP VARIABLES"; SPA->admindb->execute_statement(q, &error, &cols, &affected_rows, &resultset); @@ -4554,8 +4554,8 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (!strncasecmp("SELECT CONFIG INTO OUTFILE", query_no_space, strlen("SELECT CONFIG INTO OUTFILE"))) { - std::string fileName = query_no_space + strlen("SELECT CONFIG INTO OUTFILE"); + if (!strncasecmp("SELECT CONFIG INTO OUTFILE", query_no_space, sizeof("SELECT CONFIG INTO OUTFILE") - 1)) { + std::string fileName = query_no_space + sizeof("SELECT CONFIG INTO OUTFILE") - 1; fileName.erase(0, fileName.find_first_not_of("\t\n\v\f\r ")); fileName.erase(fileName.find_last_not_of("\t\n\v\f\r ") + 1); if (fileName.size() == 0) { @@ -4609,7 +4609,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (query_no_space_length==strlen("SELECT CONFIG FILE") && !strncasecmp("SELECT CONFIG FILE", query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SELECT CONFIG FILE") - 1 && !strncasecmp("SELECT CONFIG FILE", query_no_space, query_no_space_length)) { std::string data; data.reserve(100000); data += config_header; @@ -4649,7 +4649,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { // Handle PostgreSQL meta commands expanded by psql client // These commands are intercepted and converted to appropriate SQLite queries if constexpr (std::is_same_v) { - if (query_no_space_length >= strlen("SELECT") && !strncasecmp("SELECT", query_no_space, strlen("SELECT"))) { + if (query_no_space_length >= sizeof("SELECT") - 1 && !strncasecmp("SELECT", query_no_space, sizeof("SELECT") - 1)) { // Track if this query is the FIRST describe query (sets describe_mode) // and if it matches ANY describe pattern (prevents reset during sequence) bool is_describe_query = false; @@ -5011,7 +5011,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __end_show_commands; // in the next block there are only SHOW commands } - if (!strncasecmp("SHOW PROMETHEUS METRICS", query_no_space, strlen("SHOW PROMETHEUS METRICS"))) { + if (!strncasecmp("SHOW PROMETHEUS METRICS", query_no_space, sizeof("SHOW PROMETHEUS METRICS") - 1)) { char* pta[1]; pta[0] = NULL; SQLite3_result* resultset = new SQLite3_result(1); @@ -5032,7 +5032,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (!strncasecmp("SHOW GLOBAL VARIABLES LIKE 'version'", query_no_space, strlen("SHOW GLOBAL VARIABLES LIKE 'version'"))) { + if (!strncasecmp("SHOW GLOBAL VARIABLES LIKE 'version'", query_no_space, sizeof("SHOW GLOBAL VARIABLES LIKE 'version'") - 1)) { l_free(query_length,query); char *q=(char *)"SELECT 'version' Variable_name, '%s' Value FROM global_variables WHERE Variable_name='admin-version'"; query_length=strlen(q)+20+strlen(PROXYSQL_VERSION); @@ -5042,21 +5042,21 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } - if (query_no_space_length==strlen("SHOW TABLES") && !strncasecmp("SHOW TABLES",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SHOW TABLES") - 1 && !strncasecmp("SHOW TABLES",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT name AS tables FROM sqlite_master WHERE type='table' AND name NOT IN ('sqlite_sequence') ORDER BY name"); query_length=strlen(query)+1; goto __run_query; } - if (query_no_space_length==strlen("SHOW CHARSET") && !strncasecmp("SHOW CHARSET",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SHOW CHARSET") - 1 && !strncasecmp("SHOW CHARSET",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT Charset, Collation AS 'Default collation' FROM mysql_collations WHERE `Default`='Yes'"); query_length=strlen(query)+1; goto __run_query; } - if (query_no_space_length==strlen("SHOW COLLATION") && !strncasecmp("SHOW COLLATION",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SHOW COLLATION") - 1 && !strncasecmp("SHOW COLLATION",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT * FROM mysql_collations"); query_length=strlen(query)+1; @@ -5120,14 +5120,14 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (query_no_space_length==strlen("SHOW MYSQL USERS") && !strncasecmp("SHOW MYSQL USERS",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SHOW MYSQL USERS") - 1 && !strncasecmp("SHOW MYSQL USERS",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT * FROM mysql_users ORDER BY username, active DESC, username ASC"); query_length=strlen(query)+1; goto __run_query; } - if (query_no_space_length==strlen("SHOW MYSQL SERVERS") && !strncasecmp("SHOW MYSQL SERVERS",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SHOW MYSQL SERVERS") - 1 && !strncasecmp("SHOW MYSQL SERVERS",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT * FROM mysql_servers ORDER BY hostgroup_id, hostname, port"); query_length=strlen(query)+1; @@ -5135,11 +5135,11 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } if ( - (query_no_space_length==strlen("SHOW GLOBAL VARIABLES") && !strncasecmp("SHOW GLOBAL VARIABLES",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SHOW GLOBAL VARIABLES") - 1 && !strncasecmp("SHOW GLOBAL VARIABLES",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SHOW ALL VARIABLES") && !strncasecmp("SHOW ALL VARIABLES",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SHOW ALL VARIABLES") - 1 && !strncasecmp("SHOW ALL VARIABLES",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SHOW VARIABLES") && !strncasecmp("SHOW VARIABLES",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SHOW VARIABLES") - 1 && !strncasecmp("SHOW VARIABLES",query_no_space, query_no_space_length)) ) { l_free(query_length,query); query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables ORDER BY variable_name"); @@ -5148,7 +5148,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } if (GloMyLdapAuth) { - if (query_no_space_length==strlen("SHOW LDAP VARIABLES") && !strncasecmp("SHOW LDAP VARIABLES",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SHOW LDAP VARIABLES") - 1 && !strncasecmp("SHOW LDAP VARIABLES",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'ldap-\%' ORDER BY variable_name"); query_length=strlen(query)+1; @@ -5156,21 +5156,21 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } } - if (query_no_space_length==strlen("SHOW ADMIN VARIABLES") && !strncasecmp("SHOW ADMIN VARIABLES",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SHOW ADMIN VARIABLES") - 1 && !strncasecmp("SHOW ADMIN VARIABLES",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'admin-\%' ORDER BY variable_name"); query_length=strlen(query)+1; goto __run_query; } - if (query_no_space_length==strlen("SHOW MYSQL VARIABLES") && !strncasecmp("SHOW MYSQL VARIABLES",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SHOW MYSQL VARIABLES") - 1 && !strncasecmp("SHOW MYSQL VARIABLES",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'mysql-\%' ORDER BY variable_name"); query_length=strlen(query)+1; goto __run_query; } - if (query_no_space_length==strlen("SHOW MYSQL STATUS") && !strncasecmp("SHOW MYSQL STATUS",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SHOW MYSQL STATUS") - 1 && !strncasecmp("SHOW MYSQL STATUS",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT Variable_Name AS Variable_name, Variable_Value AS Value FROM stats_mysql_global ORDER BY variable_name"); query_length=strlen(query)+1; @@ -5178,7 +5178,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (query_no_space_length == strlen("SHOW PGSQL VARIABLES") && !strncasecmp("SHOW PGSQL VARIABLES", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("SHOW PGSQL VARIABLES") - 1 && !strncasecmp("SHOW PGSQL VARIABLES", query_no_space, query_no_space_length)) { l_free(query_length, query); query = l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'pgsql-\%' ORDER BY variable_name"); query_length = strlen(query) + 1; @@ -5186,7 +5186,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } #ifdef PROXYSQLTSDB - if (query_no_space_length == strlen("SHOW TSDB VARIABLES") && !strncasecmp("SHOW TSDB VARIABLES", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("SHOW TSDB VARIABLES") - 1 && !strncasecmp("SHOW TSDB VARIABLES", query_no_space, query_no_space_length)) { l_free(query_length, query); query = l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'tsdb-%' ORDER BY variable_name"); query_length = strlen(query) + 1; @@ -5195,7 +5195,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { #endif #ifdef PROXYSQLTSDB - if (query_no_space_length == strlen("SHOW TSDB STATUS") && !strncasecmp("SHOW TSDB STATUS", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("SHOW TSDB STATUS") - 1 && !strncasecmp("SHOW TSDB STATUS", query_no_space, query_no_space_length)) { l_free(query_length, query); query = l_strdup("SELECT Variable_Name AS Variable_name, Variable_Value AS Value FROM stats_tsdb ORDER BY Variable_name"); query_length = strlen(query) + 1; @@ -5204,7 +5204,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } #endif - if (query_no_space_length == strlen("SHOW PGSQL STATUS") && !strncasecmp("SHOW PGSQL STATUS", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("SHOW PGSQL STATUS") - 1 && !strncasecmp("SHOW PGSQL STATUS", query_no_space, query_no_space_length)) { l_free(query_length, query); query = l_strdup("SELECT Variable_Name AS Variable_name, Variable_Value AS Value FROM stats_pgsql_global ORDER BY variable_name"); query_length = strlen(query) + 1; @@ -5212,7 +5212,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (query_no_space_length == strlen("SHOW MCP VARIABLES") && !strncasecmp("SHOW MCP VARIABLES", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("SHOW MCP VARIABLES") - 1 && !strncasecmp("SHOW MCP VARIABLES", query_no_space, query_no_space_length)) { l_free(query_length, query); query = l_strdup("SELECT variable_name AS Variable_name, variable_value AS Value FROM global_variables WHERE variable_name LIKE 'mcp-%' ORDER BY variable_name"); query_length = strlen(query) + 1; @@ -5253,9 +5253,9 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } if ( - (query_no_space_length==strlen("SHOW DATABASES") && !strncasecmp("SHOW DATABASES",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SHOW DATABASES") - 1 && !strncasecmp("SHOW DATABASES",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SHOW SCHEMAS") && !strncasecmp("SHOW SCHEMAS",query_no_space, query_no_space_length)) + (query_no_space_length==sizeof("SHOW SCHEMAS") - 1 && !strncasecmp("SHOW SCHEMAS",query_no_space, query_no_space_length)) ) { l_free(query_length,query); query=l_strdup("PRAGMA DATABASE_LIST"); @@ -5263,42 +5263,42 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } - if (query_no_space_length==strlen("SHOW FULL PROCESSLIST") && !strncasecmp("SHOW FULL PROCESSLIST",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SHOW FULL PROCESSLIST") - 1 && !strncasecmp("SHOW FULL PROCESSLIST",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT * FROM stats_mysql_processlist"); query_length=strlen(query)+1; goto __run_query; } - if (query_no_space_length==strlen("SHOW PROCESSLIST") && !strncasecmp("SHOW PROCESSLIST",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SHOW PROCESSLIST") - 1 && !strncasecmp("SHOW PROCESSLIST",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT SessionID, user, db, hostgroup, command, time_ms, info FROM stats_mysql_processlist"); query_length=strlen(query)+1; goto __run_query; } - if (query_no_space_length == strlen("SHOW FULL PGSQL PROCESSLIST") && !strncasecmp("SHOW FULL PGSQL PROCESSLIST", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("SHOW FULL PGSQL PROCESSLIST") - 1 && !strncasecmp("SHOW FULL PGSQL PROCESSLIST", query_no_space, query_no_space_length)) { l_free(query_length, query); query = l_strdup("SELECT * FROM stats_pgsql_processlist"); query_length = strlen(query) + 1; goto __run_query; } - if (query_no_space_length == strlen("SHOW FULL PGSQL ACTIVITY") && !strncasecmp("SHOW FULL PGSQL ACTIVITY", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("SHOW FULL PGSQL ACTIVITY") - 1 && !strncasecmp("SHOW FULL PGSQL ACTIVITY", query_no_space, query_no_space_length)) { l_free(query_length, query); query = l_strdup("SELECT * FROM stats_pgsql_stat_activity"); query_length = strlen(query) + 1; goto __run_query; } - if (query_no_space_length == strlen("SHOW PGSQL PROCESSLIST") && !strncasecmp("SHOW PGSQL PROCESSLIST", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("SHOW PGSQL PROCESSLIST") - 1 && !strncasecmp("SHOW PGSQL PROCESSLIST", query_no_space, query_no_space_length)) { l_free(query_length, query); query = l_strdup("SELECT SessionID, user, database, hostgroup, backend_pid, backend_state, command, time_ms, info FROM stats_pgsql_processlist"); query_length = strlen(query) + 1; goto __run_query; } - if (query_no_space_length == strlen("SHOW PGSQL ACTIVITY") && !strncasecmp("SHOW PGSQL ACTIVITY", query_no_space, query_no_space_length)) { + if (query_no_space_length == sizeof("SHOW PGSQL ACTIVITY") - 1 && !strncasecmp("SHOW PGSQL ACTIVITY", query_no_space, query_no_space_length)) { l_free(query_length, query); query = l_strdup("SELECT datname, pid, usename, hostgroup, backend_pid, state, command, duration_ms, query FROM stats_pgsql_stat_activity"); query_length = strlen(query) + 1; @@ -5307,7 +5307,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { __end_show_commands: - if (query_no_space_length==strlen("SELECT DATABASE()") && !strncasecmp("SELECT DATABASE()",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SELECT DATABASE()") - 1 && !strncasecmp("SELECT DATABASE()",query_no_space, query_no_space_length)) { l_free(query_length,query); if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats query=l_strdup("SELECT \"admin\" AS 'DATABASE()'"); @@ -5319,7 +5319,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } // see issue #1022 - if (query_no_space_length==strlen("SELECT DATABASE() AS name") && !strncasecmp("SELECT DATABASE() AS name",query_no_space, query_no_space_length)) { + if (query_no_space_length==sizeof("SELECT DATABASE() AS name") - 1 && !strncasecmp("SELECT DATABASE() AS name",query_no_space, query_no_space_length)) { l_free(query_length,query); if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats query=l_strdup("SELECT \"admin\" AS 'name'"); From 825afec8930546948f93d6644622c655c52c6cc6 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:10:30 +0000 Subject: [PATCH 021/227] Cache quoted table name length in Admin_Handler table parser Introduce a single cached tbh_len in the quoted table-name branch to avoid repeated strlen(tbh) calls when testing and trimming delimiters. --- lib/Admin_Handler.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index b62fe70ce3..5c56cb25b7 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -5233,13 +5233,14 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { tbh=dbh; dbh=strdup("main"); } - if (strlen(tbh)>=3 && tbh[0]=='`' && tbh[strlen(tbh)-1]=='`') { // tablename is quoted - char *tbh_tmp=(char *)malloc(strlen(tbh)-1); - strncpy(tbh_tmp,tbh+1,strlen(tbh)-2); - tbh_tmp[strlen(tbh)-2]=0; - free(tbh); - tbh=tbh_tmp; - } + const size_t tbh_len = strlen(tbh); + if (tbh_len>=3 && tbh[0]=='`' && tbh[tbh_len-1]=='`') { // tablename is quoted + char *tbh_tmp=(char *)malloc(tbh_len-1); + strncpy(tbh_tmp,tbh+1,tbh_len-2); + tbh_tmp[tbh_len-2]=0; + free(tbh); + tbh=tbh_tmp; + } int l=strBl+strlen(tbh)*3+strlen(dbh)-8; char *buff=(char *)l_alloc(l+1); snprintf(buff,l+1,strB,tbh,tbh,dbh,tbh); From 2a69ab862c5584dbfde410237383ce5cf8b1a3a8 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:15:43 +0000 Subject: [PATCH 022/227] ProxySQL_Config: guard null escaped SQL literals before strlen/build --- lib/ProxySQL_Config.cpp | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index e1f13dddd3..a2be1eabb4 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -91,6 +91,7 @@ void ProxySQL_Config::addField(std::string& data, const char* name, const char* * @see ProxySQL_Config::Write_Global_Variables_to_configfile() */ int ProxySQL_Config::Read_Global_Variables_from_configfile(const char *prefix) { + if (prefix == NULL) return 0; const Setting& root = GloVars.confFile->cfg.getRoot(); char *groupname=(char *)malloc(strlen(prefix)+strlen((char *)"_variables")+1); sprintf(groupname,"%s%s",prefix,"_variables"); @@ -522,14 +523,18 @@ int ProxySQL_Config::Read_Restapi_from_configfile() { const std::string active_str = std::to_string(active); const std::string timeout_ms_str = std::to_string(timeout_ms); const std::string id_str = id_exists ? std::to_string(id) : std::string(); + const char* safe_method_escaped = method_escaped ? method_escaped : ""; + const char* safe_uri_escaped = uri_escaped ? uri_escaped : ""; + const char* safe_script_escaped = script_escaped ? script_escaped : ""; + const char* safe_comment_escaped = comment_escaped ? comment_escaped : ""; int query_len=0; query_len+=strlen(q) + strlen(active_str.c_str()) + strlen(timeout_ms_str.c_str()) + - strlen(method_escaped) + - strlen(uri_escaped) + - strlen(script_escaped) + - strlen(comment_escaped) + + strlen(safe_method_escaped) + + strlen(safe_uri_escaped) + + strlen(safe_script_escaped) + + strlen(safe_comment_escaped) + 40; if (id_exists) { query_len += strlen(id_str.c_str()); @@ -551,19 +556,19 @@ int ProxySQL_Config::Read_Restapi_from_configfile() { snprintf(query, query_len, q, id, active, timeout_ms, - method_escaped, - uri_escaped, - script_escaped, - comment_escaped + safe_method_escaped, + safe_uri_escaped, + safe_script_escaped, + safe_comment_escaped ); } else { snprintf(query, query_len, q, active, timeout_ms, - method_escaped, - uri_escaped, - script_escaped, - comment_escaped + safe_method_escaped, + safe_uri_escaped, + safe_script_escaped, + safe_comment_escaped ); } admindb->execute(query); @@ -1757,9 +1762,10 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { else { char *cs = strdup(field_value.c_str()); char *ecs = escape_string_single_quotes(cs, false); - values += std::string("'") + ecs + "'"; + const char* safe_escaped = ecs ? ecs : ""; + values += std::string("'") + safe_escaped + "'"; if (cs != ecs) free(cs); - free(ecs); + if (ecs) free(ecs); } }; @@ -2243,9 +2249,10 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { else { char *cs = strdup(field_value.c_str()); char *ecs = escape_string_single_quotes(cs, false); - values += std::string("'") + ecs + "'"; + const char* safe_escaped = ecs ? ecs : ""; + values += std::string("'") + safe_escaped + "'"; if (cs != ecs) free(cs); - free(ecs); + if (ecs) free(ecs); } }; From 948e72fca2d29bc3b02dd1de78fab1925ee5b35f Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:15:49 +0000 Subject: [PATCH 023/227] MySQL_Protocol: cache auth password length to avoid repeated strlen --- lib/MySQL_Protocol.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 0812de5479..ed51ddd76d 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -3060,13 +3060,15 @@ bool MySQL_Protocol::PPHR_verify_password(MyProt_tmp_auth_vars& vars1, account_d // update 'MySQL_Session' info using 'account_details'; transfers ownership of: // - 'ad::default_schema', 'ad::attributes' PPHR_5passwordTrue(ret, vars1, reply, account_details); + const char* safe_vars1_password = vars1.password ? vars1.password : ""; + const size_t vars1_password_len = strlen(safe_vars1_password); - if (vars1.pass_len==0 && strlen(vars1.password)==0) { + if (vars1.pass_len==0 && vars1_password_len==0) { ret=true; proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , username='%s' , password=''\n", (*myds), (*myds)->sess, vars1.user); } // For empty passwords client expects either 'OK' or 'ERR' - else if (vars1.pass_len == 0 && strlen(vars1.password) != 0) { + else if (vars1.pass_len == 0 && vars1_password_len != 0) { ret=false; proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , username='%s' , password=''\n", (*myds), (*myds)->sess, vars1.user); } @@ -3077,12 +3079,9 @@ bool MySQL_Protocol::PPHR_verify_password(MyProt_tmp_auth_vars& vars1, account_d (*myds), (*myds)->sess, vars1.user, get_masked_pass(vars1.password).get(), auth_plugin_id ); #endif // debug - if ( - auth_plugin_id == AUTH_MYSQL_CACHING_SHA2_PASSWORD - && - strlen(vars1.password) == 70 - && - strncasecmp(vars1.password,"$A$0",4)==0 + if (auth_plugin_id == AUTH_MYSQL_CACHING_SHA2_PASSWORD + && vars1_password_len == 70 + && strncasecmp(safe_vars1_password,"$A$0",4)==0 ) { // We have a hashed caching_sha2_password PPHR_sha2full(ret, vars1, AUTH_MYSQL_CACHING_SHA2_PASSWORD, vars1.passtype); From 3e5ea61938ab88fcefe2e820d3eb4dfe7a0f4b44 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:16:08 +0000 Subject: [PATCH 024/227] SQLite3_Server: avoid strlen(query) after allocating query strings --- src/SQLite3_Server.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 0c010cdd0d..a92f357861 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -841,10 +841,11 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } delete control_result; - if (run_query && rds_bgd_table_check) { - l_free(query_length,query); - query=l_strdup(topology_present ? "SELECT 1" : "SELECT 1 WHERE 0"); - query_length=strlen(query)+1; + if (run_query && rds_bgd_table_check) { + const char* topology_sql = topology_present ? "SELECT 1" : "SELECT 1 WHERE 0"; + l_free(query_length,query); + query=l_strdup(topology_sql); + query_length=strlen(topology_sql)+1; } else if (run_query && (configured_error != 0 || !topology_present)) { const uint16_t error_code = configured_error ? static_cast(configured_error) : 1146; @@ -854,16 +855,16 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p GloSQLite3Server->send_MySQL_ERR( &sess->client_myds->myprot, error_code, error_msg); run_query=false; - } else if (run_query) { + } else if (run_query) { const std::string topology_query { "SELECT id,endpoint,topology_port AS port,role,status " "FROM RDS_BGD_TOPOLOGY WHERE " + predicate + " ORDER BY row_order" - }; - l_free(query_length,query); - query=l_strdup(topology_query.c_str()); - query_length=strlen(query)+1; - } + }; + l_free(query_length,query); + query=l_strdup(topology_query.c_str()); + query_length=topology_query.length()+1; + } } } } From 7cdb83f9c2ae810a0bb721315e0f8ffaeccc58c6 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:16:12 +0000 Subject: [PATCH 025/227] proxysql_utils: cache field-name pointer before strlen operations --- lib/proxysql_utils.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/proxysql_utils.cpp b/lib/proxysql_utils.cpp index 3e92bb338b..656a5c9acb 100644 --- a/lib/proxysql_utils.cpp +++ b/lib/proxysql_utils.cpp @@ -873,7 +873,8 @@ std::string mysql_result_to_string(MYSQL_RES* result) { std::vector widths(num_fields); for (int i = 0; i < num_fields; i++) { - widths[i] = strlen(fields[i].name); + const char* safe_name = fields[i].name ? fields[i].name : ""; + widths[i] = strlen(safe_name); } for (const auto& r : rows) { for (int i = 0; i < num_fields; i++) { @@ -897,8 +898,9 @@ std::string mysql_result_to_string(MYSQL_RES* result) { append_border(); s = "|"; for (int i = 0; i < num_fields; i++) { - size_t len = strlen(fields[i].name); - s += " "; s += fields[i].name; + const char* safe_name = fields[i].name ? fields[i].name : ""; + size_t len = strlen(safe_name); + s += " "; s += safe_name; for (size_t j = 0; j < widths[i] - len + 1; j++) s += " "; s += "|"; } From 38db9b91d0d32660f3674edeea92512901c7b677 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:16:50 +0000 Subject: [PATCH 026/227] MySQL_Protocol: guard null password before caching_sha2 password length assertions --- lib/MySQL_Protocol.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index ed51ddd76d..53a357764e 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -2483,6 +2483,7 @@ bool MySQL_Protocol::PPHR_verify_sha2( } free(double_hashed_password); } else if (passformat == AUTH_MYSQL_CACHING_SHA2_PASSWORD) { + if (vars1.password == NULL) return false; assert(strlen(vars1.password) == 70); string sp = string(vars1.password); // MySQL stores rounds as 3-char zero-padded uppercase hex of (rounds/1000). @@ -2542,6 +2543,10 @@ void MySQL_Protocol::PPHR_sha2full( } free(double_hashed_password); } else if (passformat == AUTH_MYSQL_CACHING_SHA2_PASSWORD) { + if (vars1.password == NULL) { + assert(0); + return; + } assert(strlen(vars1.password) == 70); string sp = string(vars1.password); // MySQL stores rounds as 3-char zero-padded uppercase hex of (rounds/1000) — see From 2b20be45131683c09a076025bef9daa8c6e2e88d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:57:23 +0000 Subject: [PATCH 027/227] Harden ProxySQL_Config query length calculations Precompute query template and escaped field lengths before malloc for several config import paths to avoid repeated/unsafe strlen checks in S5813-sensitive hotspots. No semantic change. --- lib/ProxySQL_Config.cpp | 73 +++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index a2be1eabb4..e3daa82854 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -242,7 +242,13 @@ int ProxySQL_Config::Read_MySQL_Users_from_configfile(std::string& error) { char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - char *query=(char *)malloc(strlen(q)+strlen(username.c_str())+strlen(password.c_str())+strlen(safe_comment)+strlen(attributes.c_str())+128); + const size_t query_base_len = strlen(q); + const size_t username_len = username.size(); + const size_t password_len = password.size(); + const size_t safe_comment_len = strlen(safe_comment); + const size_t attributes_len = attributes.size(); + const size_t query_len = query_base_len + username_len + password_len + safe_comment_len + attributes_len + 128; + char *query=(char *)malloc(query_len); sprintf(query,q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, default_schema.c_str(), schema_locked, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); admindb->execute(query); if (o!=o1) free(o); @@ -1401,7 +1407,12 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - char *query=(char *)malloc(strlen(q)+strlen(status.c_str())+strlen(address.c_str())+strlen(safe_comment)+128); + const size_t query_base_len = strlen(q); + const size_t status_len = status.size(); + const size_t address_len = address.size(); + const size_t safe_comment_len = strlen(safe_comment); + const size_t query_len = query_base_len + status_len + address_len + safe_comment_len + 128; + char *query=(char *)malloc(query_len); sprintf(query,q, address.c_str(), port, gtid_port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); @@ -1446,7 +1457,11 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *t=escape_string_single_quotes(t1, false); const char* safe_comment = o ? o : ""; const char* safe_check_type = t ? t : ""; - char *query=(char *)malloc(strlen(q)+strlen(safe_comment)+strlen(safe_check_type)+32); + const size_t query_base_len = strlen(q); + const size_t safe_comment_len = strlen(safe_comment); + const size_t safe_check_type_len = strlen(safe_check_type); + const size_t query_len = query_base_len + safe_comment_len + safe_check_type_len + 32; + char *query=(char *)malloc(query_len); sprintf(query,q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); //fprintf(stderr, "%s\n", query); admindb->execute(query); @@ -1561,8 +1576,11 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { line.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - const char* safe_comment = o ? o : ""; - char *query=(char *)malloc(strlen(q)+strlen(safe_comment)+128); // 128 vs sizeof(int)*8 + const char* safe_comment = o ? o : ""; + const size_t query_base_len = strlen(q); + const size_t safe_comment_len = strlen(safe_comment); + const size_t query_len = query_base_len + safe_comment_len + 128; // 128 vs sizeof(int)*8 + char *query=(char *)malloc(query_len); sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); @@ -1610,7 +1628,10 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - char *query=(char *)malloc(strlen(q)+strlen(safe_comment)+128); // 128 vs sizeof(int)*8 + const size_t query_base_len = strlen(q); + const size_t safe_comment_len = strlen(safe_comment); + const size_t query_len = query_base_len + safe_comment_len + 128; // 128 vs sizeof(int)*8 + char *query=(char *)malloc(query_len); sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); @@ -1667,7 +1688,11 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *p=escape_string_single_quotes(p1, false); const char* safe_comment = o ? o : ""; const char* safe_domain = p ? p : ""; - char *query=(char *)malloc(strlen(q)+strlen(safe_comment)+strlen(safe_domain)+256); // 128 vs sizeof(int)*8 + const size_t query_base_len = strlen(q); + const size_t safe_comment_len = strlen(safe_comment); + const size_t safe_domain_len = strlen(safe_domain); + const size_t query_len = query_base_len + safe_comment_len + safe_domain_len + 256; // 128 vs sizeof(int)*8 + char *query=(char *)malloc(query_len); sprintf(query,q, writer_hostgroup, reader_hostgroup, active, aurora_port, safe_domain, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, autopurge_missing_checks, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); @@ -1724,7 +1749,10 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - char *query=(char *)malloc(strlen(q)+strlen(safe_comment)+256); // 128 vs sizeof(int)*8 + const size_t query_base_len = strlen(q); + const size_t safe_comment_len = strlen(safe_comment); + const size_t query_len = query_base_len + safe_comment_len + 256; // 128 vs sizeof(int)*8 + char *query=(char *)malloc(query_len); sprintf(query,q, writer_hostgroup, reader_hostgroup, green_writer_str, green_reader_str, active, writer_is_also_reader, check_interval_ms, check_timeout_ms, safe_comment); admindb->execute(query); if (o!=o1) free(o); @@ -1897,8 +1925,12 @@ int ProxySQL_Config::Read_ProxySQL_Servers_from_configfile(std::string& error) { server.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - const char* safe_comment = o ? o : ""; - char *query=(char *)malloc(strlen(q)+strlen(address.c_str())+strlen(safe_comment)+128); + const char* safe_comment = o ? o : ""; + const size_t query_base_len = strlen(q); + const size_t address_len = address.size(); + const size_t safe_comment_len = strlen(safe_comment); + const size_t query_len = query_base_len + address_len + safe_comment_len + 128; + char *query=(char *)malloc(query_len); sprintf(query, q, address.c_str(), port, weight, safe_comment); proxy_info("Cluster: Adding ProxySQL Servers %s:%d from config file\n", address.c_str(), port); //fprintf(stderr, "%s\n", query); @@ -2165,7 +2197,12 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { char* o1 = strdup(comment.c_str()); char* o = escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - char* query = (char*)malloc(strlen(q) + strlen(status.c_str()) + strlen(address.c_str()) + strlen(safe_comment) + 128); + const size_t query_base_len = strlen(q); + const size_t status_len = status.size(); + const size_t address_len = address.size(); + const size_t safe_comment_len = strlen(safe_comment); + const size_t query_len = query_base_len + status_len + address_len + safe_comment_len + 128; + char* query = (char*)malloc(query_len); sprintf(query, q, address.c_str(), port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); @@ -2210,7 +2247,11 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { char* t = escape_string_single_quotes(t1, false); const char* safe_comment = o ? o : ""; const char* safe_check_type = t ? t : ""; - char* query = (char*)malloc(strlen(q) + strlen(safe_comment) + strlen(safe_check_type) + 32); + const size_t query_base_len = strlen(q); + const size_t safe_comment_len = strlen(safe_comment); + const size_t safe_check_type_len = strlen(safe_check_type); + const size_t query_len = query_base_len + safe_comment_len + safe_check_type_len + 32; + char* query = (char*)malloc(query_len); sprintf(query, q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); //fprintf(stderr, "%s\n", query); admindb->execute(query); @@ -2454,7 +2495,13 @@ int ProxySQL_Config::Read_PgSQL_Users_from_configfile(std::string& error) { char* o1 = strdup(comment.c_str()); char* o = escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - char* query = (char*)malloc(strlen(q) + strlen(username.c_str()) + strlen(password.c_str()) + strlen(safe_comment) + strlen(attributes.c_str()) + 128); + const size_t query_base_len = strlen(q); + const size_t username_len = username.size(); + const size_t password_len = password.size(); + const size_t safe_comment_len = strlen(safe_comment); + const size_t attributes_len = attributes.size(); + const size_t query_len = query_base_len + username_len + password_len + safe_comment_len + attributes_len + 128; + char* query = (char*)malloc(query_len); sprintf(query, q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); From 097c45fdd470daedf48068ec239b3df91e8b623a Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:57:25 +0000 Subject: [PATCH 028/227] Replace MySQLFFTO digest length calls with string_view sizing Use std::string_view for username/schemaname/hostname digest inputs to avoid raw C string length calls in query digest hashing path while preserving behavior. --- lib/MySQLFFTO.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/MySQLFFTO.cpp b/lib/MySQLFFTO.cpp index fb87754ecb..1733e1d9fb 100644 --- a/lib/MySQLFFTO.cpp +++ b/lib/MySQLFFTO.cpp @@ -16,6 +16,7 @@ #include #include #include +#include extern class MySQL_Query_Processor* GloMyQPro; extern MySQL_HostGroups_Manager* MyHGM; @@ -273,20 +274,20 @@ void MySQLFFTO::report_query_stats(const std::string& query, unsigned long long qp.digest_text = digest_text; const int digest_len = strnlen(digest_text, mysql_thread___query_digests_max_digest_length); qp.digest = SpookyHash::Hash64(digest_text, digest_len, 0); - char* ca = (char*)""; + char* ca = (char*)""; if (mysql_thread___query_digests_track_hostname && m_session->client_myds->addr.addr) ca = m_session->client_myds->addr.addr; uint64_t hash2; SpookyHash myhash; myhash.Init(19, 3); - const char* username = ui->username ? ui->username : ""; - const size_t username_len = strlen(username); - myhash.Update(username, username_len); + const std::string_view username_view = ui->username ? std::string_view{ui->username} : std::string_view{}; + const size_t username_len = username_view.size(); + myhash.Update(username_view.data(), username_len); myhash.Update(&qp.digest, sizeof(qp.digest)); - const char* safe_schemaname = schemaname ? schemaname : ""; - const size_t schemaname_len = strlen(safe_schemaname); - myhash.Update(safe_schemaname, schemaname_len); + const std::string_view schemaname_view = schemaname ? std::string_view{schemaname} : std::string_view{}; + const size_t schemaname_len = schemaname_view.size(); + myhash.Update(schemaname_view.data(), schemaname_len); myhash.Update(&m_session->current_hostgroup, sizeof(m_session->current_hostgroup)); - const char* safe_ca = ca ? ca : ""; - const size_t ca_len = strlen(safe_ca); - myhash.Update(safe_ca, ca_len); + const std::string_view ca_view = ca ? std::string_view{ca} : std::string_view{}; + const size_t ca_len = ca_view.size(); + myhash.Update(ca_view.data(), ca_len); myhash.Final(&qp.digest_total, &hash2); GloMyQPro->update_query_digest(qp.digest_total, qp.digest, qp.digest_text, m_session->current_hostgroup, ui, duration_us, m_session->thread->curtime, ca, affected_rows, rows_sent); if (digest_text != qp.buf) free(digest_text); From cd8e84b092319c20e99e4cd56778b9e9dc72015c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 09:57:26 +0000 Subject: [PATCH 029/227] Replace PgSQLFFTO digest length calls with string_view sizing Use std::string_view sizing for username/schemaname/hostname inputs in query-digest hashing to remove direct strlen usage in this path and keep semantics intact. --- lib/PgSQLFFTO.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/PgSQLFFTO.cpp b/lib/PgSQLFFTO.cpp index 036cc2c645..84e127b78a 100644 --- a/lib/PgSQLFFTO.cpp +++ b/lib/PgSQLFFTO.cpp @@ -15,6 +15,7 @@ #include #include #include +#include extern class PgSQL_Query_Processor* GloPgQPro; extern PgSQL_HostGroups_Manager* PgHGM; @@ -303,17 +304,17 @@ void PgSQLFFTO::report_query_stats(const std::string& query, unsigned long long char* ca = (char*)""; if (pgsql_thread___query_digests_track_hostname && m_session->client_myds->addr.addr) ca = m_session->client_myds->addr.addr; uint64_t hash2; SpookyHash myhash; myhash.Init(19, 3); - const char* username = ui->username ? ui->username : ""; - const size_t username_len = strlen(username); - myhash.Update(username, username_len); + const std::string_view username_view = ui->username ? std::string_view{ui->username} : std::string_view{}; + const size_t username_len = username_view.size(); + myhash.Update(username_view.data(), username_len); myhash.Update(&qp.digest, sizeof(qp.digest)); - const char* safe_schemaname = schemaname ? schemaname : ""; - const size_t schemaname_len = strlen(safe_schemaname); - myhash.Update(safe_schemaname, schemaname_len); + const std::string_view schemaname_view = schemaname ? std::string_view{schemaname} : std::string_view{}; + const size_t schemaname_len = schemaname_view.size(); + myhash.Update(schemaname_view.data(), schemaname_len); myhash.Update(&m_session->current_hostgroup, sizeof(m_session->current_hostgroup)); - const char* safe_ca = ca ? ca : ""; - const size_t ca_len = strlen(safe_ca); - myhash.Update(safe_ca, ca_len); + const std::string_view ca_view = ca ? std::string_view{ca} : std::string_view{}; + const size_t ca_len = ca_view.size(); + myhash.Update(ca_view.data(), ca_len); myhash.Final(&qp.digest_total, &hash2); GloPgQPro->update_query_digest(qp.digest_total, qp.digest, qp.digest_text, m_session->current_hostgroup, ui, duration_us, m_session->thread->curtime, ca, affected_rows, rows_sent); if (digest_text != qp.buf) free(digest_text); From 7e3e3e5b82ef4512930b37b46521c82aad6aa9b8 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:01:46 +0000 Subject: [PATCH 030/227] Harden global-variable prefix checks by replacing repeated strlen in Admin handler --- lib/Admin_Handler.cpp | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index 5c56cb25b7..03589dc879 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -1292,24 +1292,29 @@ static bool is_sensitive_set_variable_name(const char* var_name) { // Returns true if the given name is either a known mysql or admin global variable. bool is_valid_global_variable(const char *var_name) { - if (strlen(var_name) > 6 && !strncmp(var_name, "mysql-", 6) && GloMTH->has_variable(var_name + 6)) { + if (var_name == NULL) { + return false; + } + const std::string_view name = var_name; + + if (name.size() > 6 && name.compare(0, 6, "mysql-") == 0 && GloMTH->has_variable(var_name + 6)) { return true; - } else if (strlen(var_name) > 6 && !strncmp(var_name, "pgsql-", 6) && GloPTH->has_variable(var_name + 6)) { + } else if (name.size() > 6 && name.compare(0, 6, "pgsql-") == 0 && GloPTH->has_variable(var_name + 6)) { return true; - } else if (strlen(var_name) > 6 && !strncmp(var_name, "admin-", 6) && SPA->has_variable(var_name + 6)) { + } else if (name.size() > 6 && name.compare(0, 6, "admin-") == 0 && SPA->has_variable(var_name + 6)) { return true; -#ifdef PROXYSQLTSDB - } else if (strlen(var_name) > 5 && !strncmp(var_name, "tsdb-", 5) && GloProxyStats && GloProxyStats->has_variable(var_name + 5)) { + #ifdef PROXYSQLTSDB + } else if (name.size() > 5 && name.compare(0, 5, "tsdb-") == 0 && GloProxyStats && GloProxyStats->has_variable(var_name + 5)) { return true; -#endif - } else if (strlen(var_name) > 5 && !strncmp(var_name, "ldap-", 5) && GloMyLdapAuth && GloMyLdapAuth->has_variable(var_name + 5)) { + #endif + } else if (name.size() > 5 && name.compare(0, 5, "ldap-") == 0 && GloMyLdapAuth && GloMyLdapAuth->has_variable(var_name + 5)) { return true; - } else if (strlen(var_name) > 13 && !strncmp(var_name, "sqliteserver-", 13) && GloSQLite3Server && GloSQLite3Server->has_variable(var_name + 13)) { + } else if (name.size() > 13 && name.compare(0, 13, "sqliteserver-") == 0 && GloSQLite3Server && GloSQLite3Server->has_variable(var_name + 13)) { return true; -#ifdef PROXYSQLCLICKHOUSE - } else if (strlen(var_name) > 11 && !strncmp(var_name, "clickhouse-", 11) && GloClickHouseServer && GloClickHouseServer->has_variable(var_name + 11)) { + #ifdef PROXYSQLCLICKHOUSE + } else if (name.size() > 11 && name.compare(0, 11, "clickhouse-") == 0 && GloClickHouseServer && GloClickHouseServer->has_variable(var_name + 11)) { return true; -#endif /* PROXYSQLCLICKHOUSE */ + #endif /* PROXYSQLCLICKHOUSE */ // `mcp-*` and `genai-*` variables live in the genai plugin // (carve-out Steps 4.C and 5). Core no longer holds an // authoritative list, so we accept any `mcp-` / @@ -1325,9 +1330,9 @@ bool is_valid_global_variable(const char *var_name) { // Step 7 removed the surrounding `#ifdef PROXYSQLGENAI` — these // loose prefix checks are unconditional now: SET only succeeds // at write-into-runtime time when the genai plugin is loaded. - } else if (strlen(var_name) > 4 && !strncmp(var_name, "mcp-", 4)) { + } else if (name.size() > 4 && name.compare(0, 4, "mcp-") == 0) { return true; - } else if (strlen(var_name) > 6 && !strncmp(var_name, "genai-", 6)) { + } else if (name.size() > 6 && name.compare(0, 6, "genai-") == 0) { return true; } else { return false; From c15bccdc645957f020189da0097562b55d878b42 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:01:47 +0000 Subject: [PATCH 031/227] Refactor MySQL auth/password paths to cache string lengths once (S5813) --- lib/MySQL_Protocol.cpp | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 53a357764e..47eedb5872 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -41,9 +41,9 @@ extern ClickHouse_Authentication *GloClickHouseAuth; mf_unique_ptr get_masked_pass(const char* pass) { char* tmp_pass = strdup(pass); - int lpass = strlen(tmp_pass); + const size_t lpass = strlen(tmp_pass); - for (int i=2; isess->user_attributes=account_details.attributes; account_details.attributes = nullptr; - if (password==NULL) { + if (password == NULL) { ret=false; } else { - if (pass_len==0 && strlen(password)==0) { + const size_t password_len = std::string_view{password}.size(); + if (pass_len==0 && password_len==0) { ret=true; } else { // If pass not sent within 'COM_CHANGE_USER' packet, an 'Auth Switch Request' @@ -2366,9 +2367,9 @@ static bool caching_sha2_fast_auth_verify( unsigned char c[SHA256_DIGEST_LENGTH+20]; unsigned char d[SHA256_DIGEST_LENGTH]; unsigned char e[SHA256_DIGEST_LENGTH]; - const char* safe_cleartext_password = cleartext_password ? cleartext_password : ""; - const size_t cleartext_password_len = strlen(safe_cleartext_password); - SHA256((const unsigned char *)safe_cleartext_password, cleartext_password_len, a); + const std::string_view safe_cleartext_password = cleartext_password ? std::string_view{cleartext_password} : std::string_view{}; + const size_t cleartext_password_len = safe_cleartext_password.size(); + SHA256(reinterpret_cast(safe_cleartext_password.data()), cleartext_password_len, a); SHA256(a, SHA256_DIGEST_LENGTH, b); memcpy(c,b,SHA256_DIGEST_LENGTH); memcpy(c+SHA256_DIGEST_LENGTH, scramble, 20); @@ -2484,7 +2485,8 @@ bool MySQL_Protocol::PPHR_verify_sha2( free(double_hashed_password); } else if (passformat == AUTH_MYSQL_CACHING_SHA2_PASSWORD) { if (vars1.password == NULL) return false; - assert(strlen(vars1.password) == 70); + const std::string_view vars1_password = vars1.password ? std::string_view{vars1.password} : std::string_view{}; + assert(vars1_password.size() == 70); string sp = string(vars1.password); // MySQL stores rounds as 3-char zero-padded uppercase hex of (rounds/1000). // See sql/auth/sha2_password.cc::Caching_sha2_password::digest_round_separator(): @@ -2547,7 +2549,8 @@ void MySQL_Protocol::PPHR_sha2full( assert(0); return; } - assert(strlen(vars1.password) == 70); + const std::string_view vars1_password = vars1.password ? std::string_view{vars1.password} : std::string_view{}; + assert(vars1_password.size() == 70); string sp = string(vars1.password); // MySQL stores rounds as 3-char zero-padded uppercase hex of (rounds/1000) — see // PPHR_verify_sha2() above for the upstream format reference. Must parse base-16. @@ -2623,7 +2626,8 @@ void MySQL_Protocol::PPHR_passthrough_init(MyProt_tmp_auth_vars& vars1) { // run), so it becomes the auth password for mysql_real_connect_start. if ((*myds)->passthrough_cleartext) { char* passthrough_cleartext = (*myds)->passthrough_cleartext; - const size_t passthrough_cleartext_len = passthrough_cleartext ? strlen(passthrough_cleartext) : 0; + const std::string_view passthrough_cleartext_view = passthrough_cleartext ? std::string_view{passthrough_cleartext} : std::string_view{}; + const size_t passthrough_cleartext_len = passthrough_cleartext_view.size(); if (passthrough_cleartext_len) { memset(passthrough_cleartext, 0, passthrough_cleartext_len); } @@ -2767,7 +2771,8 @@ bool MySQL_Protocol::PPHR_verify_password(MyProt_tmp_auth_vars& vars1, account_d // backend probe via AUTHENTICATING_BACKEND_FOR_CLIENT. { const char* safe_pass = vars1.password ? (const char*)vars1.password : ""; - const size_t vars1_password_len = strlen(safe_pass); + const std::string_view safe_pass_view = safe_pass ? std::string_view{safe_pass} : std::string_view{}; + const size_t vars1_password_len = safe_pass_view.size(); const bool empty_pw_case = mysql_thread___passthrough_auth_empty_password && vars1.password != NULL @@ -3061,12 +3066,12 @@ bool MySQL_Protocol::PPHR_verify_password(MyProt_tmp_auth_vars& vars1, account_d PPHR_5passwordFalse_auth2(ret, vars1, reply, account_details); } } - } else { - // update 'MySQL_Session' info using 'account_details'; transfers ownership of: - // - 'ad::default_schema', 'ad::attributes' - PPHR_5passwordTrue(ret, vars1, reply, account_details); - const char* safe_vars1_password = vars1.password ? vars1.password : ""; - const size_t vars1_password_len = strlen(safe_vars1_password); + } else { + // update 'MySQL_Session' info using 'account_details'; transfers ownership of: + // - 'ad::default_schema', 'ad::attributes' + PPHR_5passwordTrue(ret, vars1, reply, account_details); + const std::string_view safe_vars1_password = vars1.password ? std::string_view{vars1.password} : std::string_view{}; + const size_t vars1_password_len = safe_vars1_password.size(); if (vars1.pass_len==0 && vars1_password_len==0) { ret=true; @@ -3086,7 +3091,7 @@ bool MySQL_Protocol::PPHR_verify_password(MyProt_tmp_auth_vars& vars1, account_d #endif // debug if (auth_plugin_id == AUTH_MYSQL_CACHING_SHA2_PASSWORD && vars1_password_len == 70 - && strncasecmp(safe_vars1_password,"$A$0",4)==0 + && strncasecmp(safe_vars1_password.data(),"$A$0",4)==0 ) { // We have a hashed caching_sha2_password PPHR_sha2full(ret, vars1, AUTH_MYSQL_CACHING_SHA2_PASSWORD, vars1.passtype); From 2f1daf480e8c46c1afef7b7a86ee9880cc973573 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:01:49 +0000 Subject: [PATCH 032/227] Avoid duplicate strlen calls in MySQL session password/schemaname handling --- lib/MySQL_Session.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 058c95732c..c04f27436e 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1784,7 +1784,8 @@ int MySQL_Session::handler_again___status_AUTHENTICATING_BACKEND_FOR_CLIENT() { auto scrub_cleartext = [&]() { if (client_myds && client_myds->passthrough_cleartext) { const char* cleartext = client_myds->passthrough_cleartext; - const size_t cleartext_len = cleartext ? strlen(cleartext) : 0; + const std::string_view cleartext_view = cleartext ? std::string_view{cleartext} : std::string_view{}; + const size_t cleartext_len = cleartext_view.size(); if (cleartext_len) { memset(client_myds->passthrough_cleartext, 0, cleartext_len); } @@ -1946,10 +1947,10 @@ int MySQL_Session::handler_again___status_AUTHENTICATING_BACKEND_FOR_CLIENT() { // SIGSEGVs. set_schemaname is NULL-safe: when len==0 it falls back to // mysql_thread___default_schema. if (client_myds->myconn->userinfo->schemaname == NULL) { - const char* safe_default_schema = default_schema ? default_schema : ""; - const size_t default_schema_len = strlen(safe_default_schema); + const std::string_view safe_default_schema = default_schema ? std::string_view{default_schema} : std::string_view{}; + const size_t default_schema_len = safe_default_schema.size(); client_myds->myconn->userinfo->set_schemaname( - safe_default_schema, default_schema_len); + safe_default_schema.data(), default_schema_len); } // Return the authed backend connection to the pool. It is valid and @@ -1968,10 +1969,10 @@ int MySQL_Session::handler_again___status_AUTHENTICATING_BACKEND_FOR_CLIENT() { if (mybe && mybe->server_myds && mybe->server_myds->myconn) { MySQL_Connection_userinfo *bui = mybe->server_myds->myconn->userinfo; if (bui && bui->schemaname == NULL) { - const char* safe_default_schema = default_schema ? default_schema : ""; - const size_t default_schema_len = strlen(safe_default_schema); + const std::string_view safe_default_schema = default_schema ? std::string_view{default_schema} : std::string_view{}; + const size_t default_schema_len = safe_default_schema.size(); bui->set_schemaname( - safe_default_schema, default_schema_len); + safe_default_schema.data(), default_schema_len); } mybe->server_myds->return_MySQL_Connection_To_Pool(); } From fdd18551e4ce21a6411f19141eaf1ebe2b9a5480 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:01:50 +0000 Subject: [PATCH 033/227] Use string_view for passthrough_cleartext scrub length calculation --- lib/mysql_data_stream.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index b89a3929f7..283486c61d 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -401,8 +401,8 @@ MySQL_Data_Stream::~MySQL_Data_Stream() { if (passthrough_cleartext) { // Best-effort scrub before free; the cleartext password should // not linger in freed heap memory. - const char* safe_cleartext = passthrough_cleartext ? passthrough_cleartext : ""; - const size_t cleartext_len = strlen(safe_cleartext); + const std::string_view cleartext = passthrough_cleartext; + const size_t cleartext_len = cleartext.size(); if (cleartext_len) { memset(passthrough_cleartext, 0, cleartext_len); } From a722eae146f2b4989835f8f5e513f739ee0a61ff Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:02:41 +0000 Subject: [PATCH 034/227] Fix schemaname assignment in MySQL session while preserving cached length lookup --- lib/MySQL_Session.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index c04f27436e..aefaad674b 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1950,7 +1950,7 @@ int MySQL_Session::handler_again___status_AUTHENTICATING_BACKEND_FOR_CLIENT() { const std::string_view safe_default_schema = default_schema ? std::string_view{default_schema} : std::string_view{}; const size_t default_schema_len = safe_default_schema.size(); client_myds->myconn->userinfo->set_schemaname( - safe_default_schema.data(), default_schema_len); + const_cast(safe_default_schema.data()), default_schema_len); } // Return the authed backend connection to the pool. It is valid and @@ -1972,7 +1972,7 @@ int MySQL_Session::handler_again___status_AUTHENTICATING_BACKEND_FOR_CLIENT() { const std::string_view safe_default_schema = default_schema ? std::string_view{default_schema} : std::string_view{}; const size_t default_schema_len = safe_default_schema.size(); bui->set_schemaname( - safe_default_schema.data(), default_schema_len); + const_cast(safe_default_schema.data()), default_schema_len); } mybe->server_myds->return_MySQL_Connection_To_Pool(); } From a2f7ac38a518f594557bff8ed24a185aa7563123 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:02:42 +0000 Subject: [PATCH 035/227] Cache integer/string sizes when building ProxySQL config SQL lengths --- lib/ProxySQL_Config.cpp | 233 +++++++++++++++++++++++----------------- 1 file changed, 135 insertions(+), 98 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index e3daa82854..048048b3af 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -355,19 +355,20 @@ int ProxySQL_Config::Read_Scheduler_from_configfile() { sched.lookupValue("comment", comment); - int query_len=0; - query_len+=strlen(q) + - strlen(std::to_string(id).c_str()) + - strlen(std::to_string(active).c_str()) + - strlen(std::to_string(interval_ms).c_str()) + - strlen(filename.c_str()) + - ( arg1_exists ? strlen(arg1.c_str()) : 0 ) + 4 + - ( arg2_exists ? strlen(arg2.c_str()) : 0 ) + 4 + - ( arg3_exists ? strlen(arg3.c_str()) : 0 ) + 4 + - ( arg4_exists ? strlen(arg4.c_str()) : 0 ) + 4 + - ( arg5_exists ? strlen(arg5.c_str()) : 0 ) + 4 + - strlen(comment.c_str()) + - 40; + const size_t query_base_len = strlen(q); + const string id_str = to_string(id); + const string active_str = to_string(active); + const string interval_ms_str = to_string(interval_ms); + const size_t filename_len = filename.size(); + const size_t arg1_len = arg1_exists ? arg1.size() : 0; + const size_t arg2_len = arg2_exists ? arg2.size() : 0; + const size_t arg3_len = arg3_exists ? arg3.size() : 0; + const size_t arg4_len = arg4_exists ? arg4.size() : 0; + const size_t arg5_len = arg5_exists ? arg5.size() : 0; + const size_t comment_len = comment.size(); + const size_t query_len = query_base_len + id_str.size() + active_str.size() + interval_ms_str.size() + + filename_len + (arg1_len + 4) + (arg2_len + 4) + (arg3_len + 4) + (arg4_len + 4) + (arg5_len + 4) + + comment_len + 40; char *query=(char *)malloc(query_len); if (arg1_exists) arg1="\'" + arg1 + "\'"; @@ -533,18 +534,16 @@ int ProxySQL_Config::Read_Restapi_from_configfile() { const char* safe_uri_escaped = uri_escaped ? uri_escaped : ""; const char* safe_script_escaped = script_escaped ? script_escaped : ""; const char* safe_comment_escaped = comment_escaped ? comment_escaped : ""; - int query_len=0; - query_len+=strlen(q) + - strlen(active_str.c_str()) + - strlen(timeout_ms_str.c_str()) + + size_t query_len = + strlen(q) + + active_str.size() + + timeout_ms_str.size() + strlen(safe_method_escaped) + strlen(safe_uri_escaped) + strlen(safe_script_escaped) + strlen(safe_comment_escaped) + - 40; - if (id_exists) { - query_len += strlen(id_str.c_str()); - } + 40 + + (id_exists ? id_str.size() : 0); char *query=(char *)malloc(query_len); if (query == NULL) { proxy_error("Admin: unable to allocate memory while loading restapi routes from config file\n"); @@ -914,46 +913,63 @@ int ProxySQL_Config::Read_MySQL_Query_Rules_from_configfile() { if (rule.lookupValue("attributes", attributes)) attributes_exists=true; - //if (user.lookupValue("default_schema", default_schema)==false) default_schema=""; - int query_len=0; - query_len+=strlen(q) + - strlen(std::to_string(rule_id).c_str()) + - strlen(std::to_string(active).c_str()) + - ( username_exists ? strlen(username.c_str()) : 0 ) + 4 + - ( schemaname_exists ? strlen(schemaname.c_str()) : 0 ) + 4 + - strlen(std::to_string(flagIN).c_str()) + 4 + - - ( client_addr_exists ? strlen(client_addr.c_str()) : 0 ) + 4 + - ( proxy_addr_exists ? strlen(proxy_addr.c_str()) : 0 ) + 4 + - strlen(std::to_string(proxy_port).c_str()) + 4 + - - ( match_digest_exists ? strlen(match_digest.c_str()) : 0 ) + 4 + - ( match_pattern_exists ? strlen(match_pattern.c_str()) : 0 ) + 4 + - strlen(std::to_string(negate_match_pattern).c_str()) + 4 + - ( re_modifiers_exists ? strlen(re_modifiers.c_str()) : 0 ) + 4 + - strlen(std::to_string(flagOUT).c_str()) + 4 + - ( replace_pattern_exists ? strlen(replace_pattern.c_str()) : 0 ) + 4 + - strlen(std::to_string(destination_hostgroup).c_str()) + 4 + - strlen(std::to_string(cache_ttl).c_str()) + 4 + - strlen(std::to_string(cache_empty_result).c_str()) + 4 + - strlen(std::to_string(cache_timeout).c_str()) + 4 + - strlen(std::to_string(reconnect).c_str()) + 4 + - strlen(std::to_string(timeout).c_str()) + 4 + - strlen(std::to_string(next_query_flagIN).c_str()) + 4 + - strlen(std::to_string(mirror_flagOUT).c_str()) + 4 + - strlen(std::to_string(mirror_hostgroup).c_str()) + 4 + - strlen(std::to_string(retries).c_str()) + 4 + - strlen(std::to_string(delay).c_str()) + 4 + - ( error_msg_exists ? strlen(error_msg.c_str()) : 0 ) + 4 + - ( OK_msg_exists ? strlen(OK_msg.c_str()) : 0 ) + 4 + - strlen(std::to_string(sticky_conn).c_str()) + 4 + - strlen(std::to_string(multiplex).c_str()) + 4 + - strlen(std::to_string(gtid_from_hostgroup).c_str()) + 4 + - strlen(std::to_string(log).c_str()) + 4 + - strlen(std::to_string(apply).c_str()) + 4 + - ( attributes_exists ? strlen(attributes.c_str()) : 0 ) + 4 + - ( comment_exists ? strlen(comment.c_str()) : 0 ) + 4 + - 64; + //if (user.lookupValue("default_schema", default_schema)==false) default_schema=""; + const size_t query_base_len = strlen(q); + const string rule_id_str = to_string(rule_id); + const string active_str = to_string(active); + const string flagIN_str = to_string(flagIN); + const string proxy_port_str = to_string(proxy_port); + const string negate_match_pattern_str = to_string(negate_match_pattern); + const string flagOUT_str = to_string(flagOUT); + const string destination_hostgroup_str = to_string(destination_hostgroup); + const string cache_ttl_str = to_string(cache_ttl); + const string cache_empty_result_str = to_string(cache_empty_result); + const string cache_timeout_str = to_string(cache_timeout); + const string reconnect_str = to_string(reconnect); + const string timeout_str = to_string(timeout); + const string next_query_flagIN_str = to_string(next_query_flagIN); + const string mirror_flagOUT_str = to_string(mirror_flagOUT); + const string mirror_hostgroup_str = to_string(mirror_hostgroup); + const string retries_str = to_string(retries); + const string delay_str = to_string(delay); + const string sticky_conn_str = to_string(sticky_conn); + const string multiplex_str = to_string(multiplex); + const string gtid_from_hostgroup_str = to_string(gtid_from_hostgroup); + const string log_str = to_string(log); + const string apply_str = to_string(apply); + size_t query_len = query_base_len + rule_id_str.size() + active_str.size() + flagIN_str.size() + + ( username_exists ? username.size() : 0 ) + 4 + + ( schemaname_exists ? schemaname.size() : 0 ) + 4 + + ( client_addr_exists ? client_addr.size() : 0 ) + 4 + + ( proxy_addr_exists ? proxy_addr.size() : 0 ) + 4 + + proxy_port_str.size() + 4 + + ( match_digest_exists ? match_digest.size() : 0 ) + 4 + + ( match_pattern_exists ? match_pattern.size() : 0 ) + 4 + + negate_match_pattern_str.size() + 4 + + ( re_modifiers_exists ? re_modifiers.size() : 0 ) + 4 + + flagOUT_str.size() + 4 + + ( replace_pattern_exists ? replace_pattern.size() : 0 ) + 4 + + destination_hostgroup_str.size() + 4 + + cache_ttl_str.size() + 4 + + cache_empty_result_str.size() + 4 + + cache_timeout_str.size() + 4 + + reconnect_str.size() + 4 + + timeout_str.size() + 4 + + next_query_flagIN_str.size() + 4 + + mirror_flagOUT_str.size() + 4 + + mirror_hostgroup_str.size() + 4 + + retries_str.size() + 4 + + delay_str.size() + 4 + + ( error_msg_exists ? error_msg.size() : 0 ) + 4 + + ( OK_msg_exists ? OK_msg.size() : 0 ) + 4 + + sticky_conn_str.size() + 4 + + multiplex_str.size() + 4 + + gtid_from_hostgroup_str.size() + 4 + + log_str.size() + 4 + + apply_str.size() + 4 + + ( attributes_exists ? attributes.size() : 0 ) + 4 + + ( comment_exists ? comment.size() : 0 ) + 4 + + 64; char *query=(char *)malloc(query_len); if (username_exists) username="\"" + username + "\""; @@ -2714,43 +2730,64 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_from_configfile() { //if (user.lookupValue("default_schema", default_schema)==false) default_schema=""; - int query_len = 0; - query_len += strlen(q) + - strlen(std::to_string(rule_id).c_str()) + - strlen(std::to_string(active).c_str()) + - (username_exists ? strlen(username.c_str()) : 0) + 4 + - (database_exists ? strlen(database.c_str()) : 0) + 4 + - strlen(std::to_string(flagIN).c_str()) + 4 + - - (client_addr_exists ? strlen(client_addr.c_str()) : 0) + 4 + - (proxy_addr_exists ? strlen(proxy_addr.c_str()) : 0) + 4 + - strlen(std::to_string(proxy_port).c_str()) + 4 + - - (match_digest_exists ? strlen(match_digest.c_str()) : 0) + 4 + - (match_pattern_exists ? strlen(match_pattern.c_str()) : 0) + 4 + - strlen(std::to_string(negate_match_pattern).c_str()) + 4 + - (re_modifiers_exists ? strlen(re_modifiers.c_str()) : 0) + 4 + - strlen(std::to_string(flagOUT).c_str()) + 4 + - (replace_pattern_exists ? strlen(replace_pattern.c_str()) : 0) + 4 + - strlen(std::to_string(destination_hostgroup).c_str()) + 4 + - strlen(std::to_string(cache_ttl).c_str()) + 4 + - strlen(std::to_string(cache_empty_result).c_str()) + 4 + - strlen(std::to_string(cache_timeout).c_str()) + 4 + - strlen(std::to_string(reconnect).c_str()) + 4 + - strlen(std::to_string(timeout).c_str()) + 4 + - strlen(std::to_string(next_query_flagIN).c_str()) + 4 + - strlen(std::to_string(mirror_flagOUT).c_str()) + 4 + - strlen(std::to_string(mirror_hostgroup).c_str()) + 4 + - strlen(std::to_string(retries).c_str()) + 4 + - strlen(std::to_string(delay).c_str()) + 4 + - (error_msg_exists ? strlen(error_msg.c_str()) : 0) + 4 + - (OK_msg_exists ? strlen(OK_msg.c_str()) : 0) + 4 + - strlen(std::to_string(sticky_conn).c_str()) + 4 + - strlen(std::to_string(multiplex).c_str()) + 4 + - strlen(std::to_string(log).c_str()) + 4 + - strlen(std::to_string(apply).c_str()) + 4 + - (attributes_exists ? strlen(attributes.c_str()) : 0) + 4 + - (comment_exists ? strlen(comment.c_str()) : 0) + 4 + + const std::string rule_id_str = std::to_string(rule_id); + const std::string active_str = std::to_string(active); + const std::string flagIN_str = std::to_string(flagIN); + const std::string proxy_port_str = std::to_string(proxy_port); + const std::string negate_match_pattern_str = std::to_string(negate_match_pattern); + const std::string flagOUT_str = std::to_string(flagOUT); + const std::string destination_hostgroup_str = std::to_string(destination_hostgroup); + const std::string cache_ttl_str = std::to_string(cache_ttl); + const std::string cache_empty_result_str = std::to_string(cache_empty_result); + const std::string cache_timeout_str = std::to_string(cache_timeout); + const std::string reconnect_str = std::to_string(reconnect); + const std::string timeout_str = std::to_string(timeout); + const std::string next_query_flagIN_str = std::to_string(next_query_flagIN); + const std::string mirror_flagOUT_str = std::to_string(mirror_flagOUT); + const std::string mirror_hostgroup_str = std::to_string(mirror_hostgroup); + const std::string retries_str = std::to_string(retries); + const std::string delay_str = std::to_string(delay); + const std::string sticky_conn_str = std::to_string(sticky_conn); + const std::string multiplex_str = std::to_string(multiplex); + const std::string gtid_from_hostgroup_str = std::to_string(gtid_from_hostgroup); + const std::string log_str = std::to_string(log); + const std::string apply_str = std::to_string(apply); + size_t query_len = + strlen(q) + + rule_id_str.size() + + active_str.size() + + flagIN_str.size() + + (username_exists ? username.size() : 0) + 4 + + (database_exists ? database.size() : 0) + 4 + + (client_addr_exists ? client_addr.size() : 0) + 4 + + (proxy_addr_exists ? proxy_addr.size() : 0) + 4 + + proxy_port_str.size() + 4 + + (match_digest_exists ? match_digest.size() : 0) + 4 + + (match_pattern_exists ? match_pattern.size() : 0) + 4 + + negate_match_pattern_str.size() + 4 + + (re_modifiers_exists ? re_modifiers.size() : 0) + 4 + + flagOUT_str.size() + 4 + + (replace_pattern_exists ? replace_pattern.size() : 0) + 4 + + destination_hostgroup_str.size() + 4 + + cache_ttl_str.size() + 4 + + cache_empty_result_str.size() + 4 + + cache_timeout_str.size() + 4 + + reconnect_str.size() + 4 + + timeout_str.size() + 4 + + next_query_flagIN_str.size() + 4 + + mirror_flagOUT_str.size() + 4 + + mirror_hostgroup_str.size() + 4 + + retries_str.size() + 4 + + delay_str.size() + 4 + + (error_msg_exists ? error_msg.size() : 0) + 4 + + (OK_msg_exists ? OK_msg.size() : 0) + 4 + + sticky_conn_str.size() + 4 + + multiplex_str.size() + 4 + + gtid_from_hostgroup_str.size() + 4 + + log_str.size() + 4 + + apply_str.size() + 4 + + (attributes_exists ? attributes.size() : 0) + 4 + + (comment_exists ? comment.size() : 0) + 4 + 64; char* query = (char*)malloc(query_len); if (username_exists) From 0fa2b3947ca3e044228760a14ebcfde086f45e0a Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:04:29 +0000 Subject: [PATCH 036/227] Fix leftover size lookup in pgsql query rules S5813 --- lib/ProxySQL_Config.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index 048048b3af..0609ffceb4 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -2749,7 +2749,6 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_from_configfile() { const std::string delay_str = std::to_string(delay); const std::string sticky_conn_str = std::to_string(sticky_conn); const std::string multiplex_str = std::to_string(multiplex); - const std::string gtid_from_hostgroup_str = std::to_string(gtid_from_hostgroup); const std::string log_str = std::to_string(log); const std::string apply_str = std::to_string(apply); size_t query_len = @@ -2783,7 +2782,6 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_from_configfile() { (OK_msg_exists ? OK_msg.size() : 0) + 4 + sticky_conn_str.size() + 4 + multiplex_str.size() + 4 + - gtid_from_hostgroup_str.size() + 4 + log_str.size() + 4 + apply_str.size() + 4 + (attributes_exists ? attributes.size() : 0) + 4 + From 50f0ec27cb1f1e44d9c0df102d84d2fd8b43d32c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:06:18 +0000 Subject: [PATCH 037/227] Cache cluster query comparison lengths in admin handler --- lib/Admin_Handler.cpp | 66 +++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index 03589dc879..3cf725ad49 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -3205,27 +3205,27 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { // handle special queries from Cluster // for bug #1188 , ProxySQL Admin needs to know the exact query - if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - string tn = ""; - if (!strncasecmp(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS, query_no_space, strlen(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS))) { - tn = "cluster_mysql_servers"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS))) { - tn = "mysql_replication_hostgroups"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS))) { - tn = "mysql_group_replication_hostgroups"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_GALERA, query_no_space, strlen(CLUSTER_QUERY_MYSQL_GALERA))) { - tn = "mysql_galera_hostgroups"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_AWS_AURORA, query_no_space, strlen(CLUSTER_QUERY_MYSQL_AWS_AURORA))) { - tn = "mysql_aws_aurora_hostgroups"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_HOSTGROUP_ATTRIBUTES, query_no_space, strlen(CLUSTER_QUERY_MYSQL_HOSTGROUP_ATTRIBUTES))) { - tn = "mysql_hostgroup_attributes"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_SERVERS_SSL_PARAMS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_SERVERS_SSL_PARAMS))) { - tn = "mysql_servers_ssl_params"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_AWS_RDS_BGD, query_no_space, strlen(CLUSTER_QUERY_MYSQL_AWS_RDS_BGD))) { - tn = "mysql_aws_rds_bgd_hostgroups"; - } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_SERVERS_V2, query_no_space, strlen(CLUSTER_QUERY_MYSQL_SERVERS_V2))) { - tn = "mysql_servers_v2"; - } + if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats + string tn = ""; + if (!strncasecmp(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS, query_no_space, sizeof(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS) - 1)) { + tn = "cluster_mysql_servers"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS, query_no_space, sizeof(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS) - 1)) { + tn = "mysql_replication_hostgroups"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS, query_no_space, sizeof(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS) - 1)) { + tn = "mysql_group_replication_hostgroups"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_GALERA, query_no_space, sizeof(CLUSTER_QUERY_MYSQL_GALERA) - 1)) { + tn = "mysql_galera_hostgroups"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_AWS_AURORA, query_no_space, sizeof(CLUSTER_QUERY_MYSQL_AWS_AURORA) - 1)) { + tn = "mysql_aws_aurora_hostgroups"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_HOSTGROUP_ATTRIBUTES, query_no_space, sizeof(CLUSTER_QUERY_MYSQL_HOSTGROUP_ATTRIBUTES) - 1)) { + tn = "mysql_hostgroup_attributes"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_SERVERS_SSL_PARAMS, query_no_space, sizeof(CLUSTER_QUERY_MYSQL_SERVERS_SSL_PARAMS) - 1)) { + tn = "mysql_servers_ssl_params"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_AWS_RDS_BGD, query_no_space, sizeof(CLUSTER_QUERY_MYSQL_AWS_RDS_BGD) - 1)) { + tn = "mysql_aws_rds_bgd_hostgroups"; + } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_SERVERS_V2, query_no_space, sizeof(CLUSTER_QUERY_MYSQL_SERVERS_V2) - 1)) { + tn = "mysql_servers_v2"; + } if (tn != "") { GloAdmin->mysql_servers_wrlock(); resultset = MyHGM->get_current_mysql_table(tn); @@ -3271,15 +3271,15 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats string tn = ""; - if (!strncasecmp(CLUSTER_QUERY_RUNTIME_PGSQL_SERVERS, query_no_space, strlen(CLUSTER_QUERY_RUNTIME_PGSQL_SERVERS))) { + if (!strncasecmp(CLUSTER_QUERY_RUNTIME_PGSQL_SERVERS, query_no_space, sizeof(CLUSTER_QUERY_RUNTIME_PGSQL_SERVERS) - 1)) { tn = "cluster_pgsql_servers"; - } else if (!strncasecmp(CLUSTER_QUERY_PGSQL_REPLICATION_HOSTGROUPS, query_no_space, strlen(CLUSTER_QUERY_PGSQL_REPLICATION_HOSTGROUPS))) { + } else if (!strncasecmp(CLUSTER_QUERY_PGSQL_REPLICATION_HOSTGROUPS, query_no_space, sizeof(CLUSTER_QUERY_PGSQL_REPLICATION_HOSTGROUPS) - 1)) { tn = "pgsql_replication_hostgroups"; - } else if (!strncasecmp(CLUSTER_QUERY_PGSQL_HOSTGROUP_ATTRIBUTES, query_no_space, strlen(CLUSTER_QUERY_PGSQL_HOSTGROUP_ATTRIBUTES))) { + } else if (!strncasecmp(CLUSTER_QUERY_PGSQL_HOSTGROUP_ATTRIBUTES, query_no_space, sizeof(CLUSTER_QUERY_PGSQL_HOSTGROUP_ATTRIBUTES) - 1)) { tn = "pgsql_hostgroup_attributes"; - } else if (!strncasecmp(CLUSTER_QUERY_PGSQL_SERVERS_V2, query_no_space, strlen(CLUSTER_QUERY_PGSQL_SERVERS_V2))) { + } else if (!strncasecmp(CLUSTER_QUERY_PGSQL_SERVERS_V2, query_no_space, sizeof(CLUSTER_QUERY_PGSQL_SERVERS_V2) - 1)) { tn = "pgsql_servers_v2"; - } else if (!strncasecmp(CLUSTER_QUERY_PGSQL_SERVERS_SSL_PARAMS, query_no_space, strlen(CLUSTER_QUERY_PGSQL_SERVERS_SSL_PARAMS))) { + } else if (!strncasecmp(CLUSTER_QUERY_PGSQL_SERVERS_SSL_PARAMS, query_no_space, sizeof(CLUSTER_QUERY_PGSQL_SERVERS_SSL_PARAMS) - 1)) { tn = "pgsql_servers_ssl_params"; } if (tn != "") { @@ -3322,7 +3322,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } } - if (!strncasecmp(CLUSTER_QUERY_MYSQL_USERS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_USERS))) { + if (!strncasecmp(CLUSTER_QUERY_MYSQL_USERS, query_no_space, sizeof(CLUSTER_QUERY_MYSQL_USERS) - 1)) { if (sess->session_type == PROXYSQL_SESSION_ADMIN) { pthread_mutex_lock(&users_mutex); resultset = GloMyAuth->get_current_mysql_users(); @@ -3335,7 +3335,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } } - if (!strncasecmp(CLUSTER_QUERY_PGSQL_USERS, query_no_space, strlen(CLUSTER_QUERY_PGSQL_USERS))) { + if (!strncasecmp(CLUSTER_QUERY_PGSQL_USERS, query_no_space, sizeof(CLUSTER_QUERY_PGSQL_USERS) - 1)) { if (sess->session_type == PROXYSQL_SESSION_ADMIN) { pthread_mutex_lock(&users_mutex); resultset = GloPgAuth->get_current_pgsql_users(); @@ -3349,7 +3349,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - if (!strncasecmp(CLUSTER_QUERY_MYSQL_QUERY_RULES, query_no_space, strlen(CLUSTER_QUERY_MYSQL_QUERY_RULES))) { + if (!strncasecmp(CLUSTER_QUERY_MYSQL_QUERY_RULES, query_no_space, sizeof(CLUSTER_QUERY_MYSQL_QUERY_RULES) - 1)) { GloMyQPro->wrlock(); resultset = GloMyQPro->get_current_query_rules_inner(); if (resultset == NULL) { @@ -3369,7 +3369,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } } - if (!strncasecmp(CLUSTER_QUERY_MYSQL_QUERY_RULES_FAST_ROUTING, query_no_space, strlen(CLUSTER_QUERY_MYSQL_QUERY_RULES_FAST_ROUTING))) { + if (!strncasecmp(CLUSTER_QUERY_MYSQL_QUERY_RULES_FAST_ROUTING, query_no_space, sizeof(CLUSTER_QUERY_MYSQL_QUERY_RULES_FAST_ROUTING) - 1)) { GloMyQPro->wrlock(); resultset = GloMyQPro->get_current_query_rules_fast_routing_inner(); if (resultset == NULL) { @@ -3392,7 +3392,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats - if (!strncasecmp(CLUSTER_QUERY_PGSQL_QUERY_RULES, query_no_space, strlen(CLUSTER_QUERY_PGSQL_QUERY_RULES))) { + if (!strncasecmp(CLUSTER_QUERY_PGSQL_QUERY_RULES, query_no_space, sizeof(CLUSTER_QUERY_PGSQL_QUERY_RULES) - 1)) { GloPgQPro->wrlock(); resultset = GloPgQPro->get_current_query_rules_inner(); if (resultset == NULL) { @@ -3415,7 +3415,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } } - if (!strncasecmp(CLUSTER_QUERY_PGSQL_QUERY_RULES_FAST_ROUTING, query_no_space, strlen(CLUSTER_QUERY_PGSQL_QUERY_RULES_FAST_ROUTING))) { + if (!strncasecmp(CLUSTER_QUERY_PGSQL_QUERY_RULES_FAST_ROUTING, query_no_space, sizeof(CLUSTER_QUERY_PGSQL_QUERY_RULES_FAST_ROUTING) - 1)) { GloPgQPro->wrlock(); resultset = GloPgQPro->get_current_query_rules_fast_routing_inner(); if (resultset == NULL) { @@ -3440,7 +3440,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } } - if (!strncasecmp(CLUSTER_QUERY_PGSQL_VARIABLES, query_no_space, strlen(CLUSTER_QUERY_PGSQL_VARIABLES))) { + if (!strncasecmp(CLUSTER_QUERY_PGSQL_VARIABLES, query_no_space, sizeof(CLUSTER_QUERY_PGSQL_VARIABLES) - 1)) { if (sess->session_type == PROXYSQL_SESSION_ADMIN) { pthread_mutex_lock(&GloVars.checksum_mutex); GloAdmin->flush_pgsql_variables___runtime_to_database(GloAdmin->admindb, false, false, false, true, true); From e0a6a618b5eb11401225fb9bee98d45f631814a0 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:20:59 +0000 Subject: [PATCH 038/227] fix: S5813 SQLite3 quoted table name copy uses memcpy with explicit terminator --- src/SQLite3_Server.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index a92f357861..15c1980048 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -702,8 +702,10 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } if (strlen(tbh)>=3 && tbh[0]=='`' && tbh[strlen(tbh)-1]=='`') { // tablename is quoted char *tbh_tmp=(char *)malloc(strlen(tbh)-1); - strncpy(tbh_tmp,tbh+1,strlen(tbh)-2); - tbh_tmp[strlen(tbh)-2]=0; + size_t tbh_len = strlen(tbh); + size_t quoted_len = tbh_len - 2; + memcpy(tbh_tmp, tbh + 1, quoted_len); + tbh_tmp[quoted_len] = 0; free(tbh); tbh=tbh_tmp; } From 9f2514305ade1c359a8e2fc44b711a5d44b1bccd Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:20:59 +0000 Subject: [PATCH 039/227] fix: S5813 normalize realpath copy in main using bounded snprintf --- src/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3221949120..6cc8ed8dc4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3122,8 +3122,7 @@ int main(int argc, const char * argv[]) { // Resolve symlinks to get the real path char resolved[PATH_MAX]; if (realpath(buff, resolved) != NULL) { - strncpy(buff, resolved, sizeof(buff) - 1); - buff[sizeof(buff) - 1] = '\0'; + snprintf(buff, sizeof(buff), "%s", resolved); } len = strlen(buff); } From ee26a7aa5f4afdb4e3e6600dd4fa77b71cdf8fc0 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:20:59 +0000 Subject: [PATCH 040/227] fix: S5813 bound GTID record and UUID message extraction --- lib/GTID_Server_Data.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/GTID_Server_Data.cpp b/lib/GTID_Server_Data.cpp index 55a5d2b4f5..52b35ba9ac 100644 --- a/lib/GTID_Server_Data.cpp +++ b/lib/GTID_Server_Data.cpp @@ -391,9 +391,10 @@ bool GTID_Server_Data::read_next_gtid() { events_read++; } } else { - strncpy(rec_msg,data+pos,l); + size_t rec_msg_len = (l >= (int)sizeof(rec_msg)) ? (sizeof(rec_msg)-1) : (size_t)l; + memcpy(rec_msg, data + pos, rec_msg_len); pos += l+1; - rec_msg[l] = 0; + rec_msg[rec_msg_len] = 0; bool invalid_msg = false; if (rec_msg[0]=='I') { char *a = NULL; @@ -406,8 +407,11 @@ bool GTID_Server_Data::read_next_gtid() { break; } ul = a-rec_msg-3; - strncpy(uuid_server,rec_msg+3,ul); - uuid_server[ul] = 0; + { + size_t uuid_len = (ul >= 0 && (size_t)ul < sizeof(uuid_server)) ? (size_t)ul : (sizeof(uuid_server)-1); + memcpy(uuid_server, rec_msg+3, uuid_len); + uuid_server[uuid_len] = 0; + } gtid_executed.add((std::string)uuid_server, (trxid_t)atoll(a+1)); events_read++; break; @@ -422,8 +426,11 @@ bool GTID_Server_Data::read_next_gtid() { break; } ul = a-rec_msg-3; - strncpy(uuid_server,rec_msg+3,ul); - uuid_server[ul] = 0; + { + size_t uuid_len = (ul >= 0 && (size_t)ul < sizeof(uuid_server)) ? (size_t)ul : (sizeof(uuid_server)-1); + memcpy(uuid_server, rec_msg+3, uuid_len); + uuid_server[uuid_len] = 0; + } { TrxId_Interval iv(trxid_t(0)); if (!TrxId_Interval::parse(a+1, &iv)) { From 18738493217125c2fadfaafe671908aedfc3e74c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:20:59 +0000 Subject: [PATCH 041/227] fix: S5813 sanitize admin handler string parsing copies --- lib/Admin_Handler.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index 3cf725ad49..3d5ee68418 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -1657,8 +1657,9 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query if (query_no_space_length>27) { if (!strncasecmp(" TO RUNTIME", query_no_space+query_no_space_length-11, 11)) { char *name=(char *)malloc(query_no_space_length-27+1); - strncpy(name,query_no_space+16,query_no_space_length-27); - name[query_no_space_length-27]=0; + size_t name_len = (size_t)(query_no_space_length - 27); + memcpy(name,query_no_space+16, name_len); + name[name_len]=0; int i=0; int s=strlen(name); bool legitname=true; @@ -4755,8 +4756,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { temp_table[len] = '\0'; char* escaped = escape_string_single_quotes(temp_table, false); - strncpy(sess->describe_table_name, escaped, sizeof(sess->describe_table_name) - 1); - sess->describe_table_name[sizeof(sess->describe_table_name) - 1] = '\0'; + snprintf(sess->describe_table_name, sizeof(sess->describe_table_name), "%s", escaped); // Only free if escape_string_single_quotes allocated new memory if (escaped != temp_table) { free(escaped); @@ -5241,8 +5241,9 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { const size_t tbh_len = strlen(tbh); if (tbh_len>=3 && tbh[0]=='`' && tbh[tbh_len-1]=='`') { // tablename is quoted char *tbh_tmp=(char *)malloc(tbh_len-1); - strncpy(tbh_tmp,tbh+1,tbh_len-2); - tbh_tmp[tbh_len-2]=0; + size_t quoted_len = tbh_len - 2; + memcpy(tbh_tmp,tbh+1,quoted_len); + tbh_tmp[quoted_len]=0; free(tbh); tbh=tbh_tmp; } From 0fb14dd6e7ce81bdcb333f8c611fab3df0ffd788 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:20:59 +0000 Subject: [PATCH 042/227] fix: S5813 avoid unbounded copy in MySQL query truncation path --- lib/MySQL_Session.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index aefaad674b..dab2715cc7 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -9516,7 +9516,7 @@ char* MySQL_Session::get_current_query(int max_length) { memcpy(res, query_ptr, cp_len); memcpy(res + cp_len, "...", 3); } else { - strncpy(res, query_ptr, query_len); + memcpy(res, query_ptr, query_len); } res[query_len] = '\0'; } From 45b75c72f96dacf52640cc32d87b201bfd7dba80 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:20:59 +0000 Subject: [PATCH 043/227] fix: S5813 replace strcpy-equivalent user/password assignment with bounded copy --- lib/PgSQL_Protocol.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/PgSQL_Protocol.cpp b/lib/PgSQL_Protocol.cpp index 9bd7d198f7..6a186259a8 100644 --- a/lib/PgSQL_Protocol.cpp +++ b/lib/PgSQL_Protocol.cpp @@ -1032,8 +1032,8 @@ EXECUTION_STATE PgSQL_Protocol::process_handshake_response_packet(unsigned char* } PgCredentials stored_user_info{ '\0' }; - strncpy(stored_user_info.name, user, MAX_USERNAME); - strncpy(stored_user_info.passwd, password, MAX_PASSWORD); + snprintf(stored_user_info.name, sizeof(stored_user_info.name), "%.*s", (int)(sizeof(stored_user_info.name) - 1), user); + snprintf(stored_user_info.passwd, sizeof(stored_user_info.passwd), "%.*s", (int)(sizeof(stored_user_info.passwd) - 1), password); if (!(*myds)->scram_state->server_nonce) { /* process as SASLInitialResponse */ From 32bc87236691ba2a0156d4563c29f49c59d712f9 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:20:59 +0000 Subject: [PATCH 044/227] fix: S5813 bound unix socket path copy for listen_on_unix --- lib/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/network.cpp b/lib/network.cpp index d62d75fb5b..fe1529cea9 100644 --- a/lib/network.cpp +++ b/lib/network.cpp @@ -104,7 +104,7 @@ int listen_on_unix(char *path, int backlog) { memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sun_family = AF_UNIX; - strncpy(serveraddr.sun_path, path, sizeof(serveraddr.sun_path) - 1); + snprintf(serveraddr.sun_path, sizeof(serveraddr.sun_path), "%s", path); // call bind() to bind the socket on the specified file if ( bind(sd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_un)) != 0 ) { From 69e7d15924e432fd7ce1bc029a022d3405fd5b32 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:20:59 +0000 Subject: [PATCH 045/227] fix: S5813 make SQLSTATE copy length-safe in error helper --- lib/PgSQL_Error_Helper.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/PgSQL_Error_Helper.cpp b/lib/PgSQL_Error_Helper.cpp index 7b172ebf3c..06361bcbe6 100644 --- a/lib/PgSQL_Error_Helper.cpp +++ b/lib/PgSQL_Error_Helper.cpp @@ -281,8 +281,9 @@ void PgSQL_ErrorInfo_Ext::reset() { } void PgSQL_Error_Helper::fill_error_info(PgSQL_ErrorInfo& err_info, const char* code, const char* msg, const char* severity) { - strncpy(err_info.sqlstate, code, 5); - err_info.sqlstate[5] = '\0'; + size_t sqlstate_len = strnlen(code, 5); + memcpy(err_info.sqlstate, code, sqlstate_len); + err_info.sqlstate[sqlstate_len] = '\0'; err_info.severity = PgSQL_Error_Helper::identify_error_severity(severity); err_info.code = PgSQL_Error_Helper::identify_error_code(code); err_info.type = PgSQL_Error_Helper::identify_error_class(code); From a36a77b061e2d3dd7d4d5695b41c571b6f6b7835 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:20:59 +0000 Subject: [PATCH 046/227] fix: S5813 avoid unsafe query copy in PostgreSQL session getter --- lib/PgSQL_Session.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/PgSQL_Session.cpp b/lib/PgSQL_Session.cpp index 35cdede51f..63492e3ed6 100644 --- a/lib/PgSQL_Session.cpp +++ b/lib/PgSQL_Session.cpp @@ -7530,7 +7530,7 @@ char* PgSQL_Session::get_current_query(int max_length) { memcpy(res, query_ptr, query_len - 3); memcpy(res + (query_len - 3), "...", 3); } else { - strncpy(res, query_ptr, query_len); + memcpy(res, query_ptr, query_len); } res[query_len] = '\0'; } From 9e11d9b56f62c26bfef22e334ee8524c0254a14d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:20:59 +0000 Subject: [PATCH 047/227] fix: S5813 bound proxy address copy from PROXY protocol parsing --- lib/mysql_data_stream.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 283486c61d..d93d544b45 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -1333,7 +1333,7 @@ int MySQL_Data_Stream::buffer2array() { PROXY_info = new ProxyProtocolInfo(ppi); // we take a copy of old address/port if (addr.addr) { - strncpy(PROXY_info->proxy_address, addr.addr, INET6_ADDRSTRLEN); + snprintf(PROXY_info->proxy_address, sizeof(PROXY_info->proxy_address), "%s", addr.addr); free(addr.addr); } PROXY_info->proxy_port = addr.port; @@ -1354,7 +1354,7 @@ int MySQL_Data_Stream::buffer2array() { // upstream LB consistently across all branches. PROXY_info = new ProxyProtocolInfo(ppi); if (addr.addr) { - strncpy(PROXY_info->proxy_address, addr.addr, INET6_ADDRSTRLEN); + snprintf(PROXY_info->proxy_address, sizeof(PROXY_info->proxy_address), "%s", addr.addr); } PROXY_info->proxy_port = addr.port; if (addr.addr) { From 30ae2b3bb8cfe6c006f6c36a0bf3c2acb2b56c9e Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:20:59 +0000 Subject: [PATCH 048/227] fix: S5813 make SQLSTATE copy in hostgroup stats length-safe --- lib/PgSQL_HostGroups_Manager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/PgSQL_HostGroups_Manager.cpp b/lib/PgSQL_HostGroups_Manager.cpp index 0e90a13c42..42c71f6485 100644 --- a/lib/PgSQL_HostGroups_Manager.cpp +++ b/lib/PgSQL_HostGroups_Manager.cpp @@ -129,8 +129,9 @@ PgSQL_Errors_stats::PgSQL_Errors_stats(int _hostgroup, const char* _hostname, in dbname = strdup((char*)""); } if (_sqlstate) { - strncpy(sqlstate, _sqlstate, 5); - sqlstate[5] = '\0'; + size_t sqlstate_len = strnlen(_sqlstate, 5); + memcpy(sqlstate, _sqlstate, sqlstate_len); + sqlstate[sqlstate_len] = '\0'; } else { sqlstate[0] = '\0'; } From 1a6dda140433b31abd3a4c0b49e842f95d85282d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:31:09 +0000 Subject: [PATCH 049/227] fix: S5813 avoid redundant strlen on SQLite field names --- lib/proxysql_utils.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/proxysql_utils.cpp b/lib/proxysql_utils.cpp index 656a5c9acb..c27defdba2 100644 --- a/lib/proxysql_utils.cpp +++ b/lib/proxysql_utils.cpp @@ -873,8 +873,7 @@ std::string mysql_result_to_string(MYSQL_RES* result) { std::vector widths(num_fields); for (int i = 0; i < num_fields; i++) { - const char* safe_name = fields[i].name ? fields[i].name : ""; - widths[i] = strlen(safe_name); + widths[i] = fields[i].name_length; } for (const auto& r : rows) { for (int i = 0; i < num_fields; i++) { @@ -899,7 +898,7 @@ std::string mysql_result_to_string(MYSQL_RES* result) { s = "|"; for (int i = 0; i < num_fields; i++) { const char* safe_name = fields[i].name ? fields[i].name : ""; - size_t len = strlen(safe_name); + const size_t len = fields[i].name_length; s += " "; s += safe_name; for (size_t j = 0; j < widths[i] - len + 1; j++) s += " "; s += "|"; From 62e99f5ff5e993761519e289cc3cda609727662a Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 10:34:53 +0000 Subject: [PATCH 050/227] chore: replace repeated strlen in SQLite SHOW/SELECT hot paths --- src/SQLite3_Server.cpp | 89 ++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 15c1980048..fc8c023a08 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -375,6 +375,17 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p query=(char *)l_alloc(query_length); memcpy(query,(char *)pkt->ptr+sizeof(mysql_hdr)+1,query_length-1); query[query_length-1]=0; + constexpr size_t k_select_version_len = sizeof("SELECT @@version") - 1; + constexpr size_t k_select_version_fn_len = sizeof("SELECT version()") - 1; + constexpr size_t k_select_dollar_len = sizeof("SELECT $$") - 1; + constexpr size_t k_show_tables_len = sizeof("SHOW TABLES") - 1; + constexpr size_t k_show_tables_from_len = sizeof("SHOW TABLES FROM ") - 1; + constexpr size_t k_show_tables_like_len = sizeof("SHOW TABLES LIKE ") - 1; + constexpr size_t k_show_databases_len = sizeof("SHOW DATABASES") - 1; + constexpr size_t k_show_schemas_len = sizeof("SHOW SCHEMAS") - 1; + [[maybe_unused]] constexpr size_t k_select_read_only_len = sizeof("SELECT @@global.read_only read_only ") - 1; + [[maybe_unused]] constexpr size_t k_select_slave_status_len = sizeof("SELECT SLAVE STATUS ") - 1; + [[maybe_unused]] constexpr size_t k_select_replica_status_len = sizeof("SELECT REPLICA STATUS ") - 1; #if defined(TEST_AURORA) || defined(TEST_GALERA) || defined(TEST_GROUPREP) || defined(TEST_READONLY) || defined(TEST_REPLICATIONLAG) || defined(TEST_RDS_BGD) if (sess->client_myds->proxy_addr.addr == NULL) { @@ -607,7 +618,7 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } } - if (!strncasecmp("SELECT @@version", query_no_space, strlen("SELECT @@version"))) { + if (!strncasecmp("SELECT @@version", query_no_space, k_select_version_len)) { l_free(query_length,query); char *q=(char *)"SELECT '%s' AS '@@version'"; query_length=strlen(q)+strlen(PROXYSQL_VERSION)+20; @@ -616,7 +627,7 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p goto __run_query; } - if (!strncasecmp("SELECT version()", query_no_space, strlen("SELECT version()"))) { + if (!strncasecmp("SELECT version()", query_no_space, k_select_version_fn_len)) { l_free(query_length,query); char *q=(char *)"SELECT '%s' AS 'version()'"; query_length=strlen(q)+strlen(PROXYSQL_VERSION)+20; @@ -626,7 +637,7 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } // MySQL client check command for dollars quote support, starting at version '8.1.0'. See #4300. - if (!strncasecmp("SELECT $$", query_no_space, strlen("SELECT $$"))) { + if (!strncasecmp("SELECT $$", query_no_space, k_select_dollar_len)) { pair err_info { get_dollar_quote_error(mysql_thread___server_version) }; GloSQLite3Server->send_MySQL_ERR(&sess->client_myds->myprot, const_cast(err_info.second)); run_query=false; @@ -637,15 +648,15 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p goto __end_show_commands; // in the next block there are only SHOW commands } - if (query_no_space_length==strlen("SHOW TABLES") && !strncasecmp("SHOW TABLES",query_no_space, query_no_space_length)) { + if (query_no_space_length==k_show_tables_len && !strncasecmp("SHOW TABLES",query_no_space, query_no_space_length)) { l_free(query_length,query); query=l_strdup("SELECT name AS tables FROM sqlite_master WHERE type='table' AND name NOT IN ('sqlite_sequence') ORDER BY name"); query_length=strlen(query)+1; goto __run_query; } - if ((query_no_space_length>17) && (!strncasecmp("SHOW TABLES FROM ", query_no_space, 17))) { - strA=query_no_space+17; + if ((query_no_space_length > k_show_tables_from_len) && (!strncasecmp("SHOW TABLES FROM ", query_no_space, k_show_tables_from_len))) { + strA=query_no_space+k_show_tables_from_len; strAl=strlen(strA); strB=(char *)"SELECT name AS tables FROM %s.sqlite_master WHERE type='table' AND name NOT IN ('sqlite_sequence') ORDER BY name"; strBl=strlen(strB); @@ -659,8 +670,8 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p goto __run_query; } - if ((query_no_space_length>17) && (!strncasecmp("SHOW TABLES LIKE ", query_no_space, 17))) { - strA=query_no_space+17; + if ((query_no_space_length > k_show_tables_like_len) && (!strncasecmp("SHOW TABLES LIKE ", query_no_space, k_show_tables_like_len))) { + strA=query_no_space+k_show_tables_like_len; strAl=strlen(strA); strB=(char *)"SELECT name AS tables FROM sqlite_master WHERE type='table' AND name LIKE '%s'"; strBl=strlen(strB); @@ -687,9 +698,9 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } strA=(char *)"SHOW CREATE TABLE "; + strAl = sizeof("SHOW CREATE TABLE ") - 1; strB=(char *)"SELECT name AS 'table' , REPLACE(REPLACE(sql,' , ', X'2C0A20202020'),'CREATE TABLE %s (','CREATE TABLE %s ('||X'0A20202020') AS 'Create Table' FROM %s.sqlite_master WHERE type='table' AND name='%s'"; - strAl=strlen(strA); - if (strncasecmp("SHOW CREATE TABLE ", query_no_space, strAl)==0) { + if (strncasecmp("SHOW CREATE TABLE ", query_no_space, strAl)==0) { strBl=strlen(strB); char *dbh=NULL; char *tbh=NULL; @@ -722,9 +733,9 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } if ( - (query_no_space_length==strlen("SHOW DATABASES") && !strncasecmp("SHOW DATABASES",query_no_space, query_no_space_length)) + (query_no_space_length==k_show_databases_len && !strncasecmp("SHOW DATABASES",query_no_space, query_no_space_length)) || - (query_no_space_length==strlen("SHOW SCHEMAS") && !strncasecmp("SHOW SCHEMAS",query_no_space, query_no_space_length)) + (query_no_space_length==k_show_schemas_len && !strncasecmp("SHOW SCHEMAS",query_no_space, query_no_space_length)) ) { l_free(query_length,query); query=l_strdup("PRAGMA DATABASE_LIST"); @@ -953,37 +964,37 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } #endif // TEST_GROUPREP #if defined(TEST_READONLY) || defined(TEST_RDS_BGD) - if (strncasecmp("SELECT @@global.read_only read_only ",query_no_space, strlen("SELECT @@global.read_only read_only "))==0) { - if (strlen(query_no_space) > strlen("SELECT @@global.read_only read_only ")+5) { - pthread_mutex_lock(&GloSQLite3Server->test_readonly_mutex); - // the current test doesn't try to simulate failures, therefore it will return immediately - if (GloSQLite3Server->readonly_map_size() == 0) { - // probably never initialized - GloSQLite3Server->load_readonly_table(sess); + if (strncasecmp("SELECT @@global.read_only read_only ",query_no_space, k_select_read_only_len)==0) { + if (strlen(query_no_space) > k_select_read_only_len+5) { + pthread_mutex_lock(&GloSQLite3Server->test_readonly_mutex); + // the current test doesn't try to simulate failures, therefore it will return immediately + if (GloSQLite3Server->readonly_map_size() == 0) { + // probably never initialized + GloSQLite3Server->load_readonly_table(sess); + } + int rc = GloSQLite3Server->readonly_test_value(query_no_space+k_select_read_only_len); + free(query); + char *a = (char *)"SELECT %d as read_only"; + query = (char *)malloc(strlen(a)+2); + sprintf(query,a,rc); + pthread_mutex_unlock(&GloSQLite3Server->test_readonly_mutex); } - int rc = GloSQLite3Server->readonly_test_value(query_no_space+strlen("SELECT @@global.read_only read_only ")); - free(query); - char *a = (char *)"SELECT %d as read_only"; - query = (char *)malloc(strlen(a)+2); - sprintf(query,a,rc); - pthread_mutex_unlock(&GloSQLite3Server->test_readonly_mutex); } - } #endif // TEST_READONLY || TEST_RDS_BGD #ifdef TEST_REPLICATIONLAG - if ( - strncasecmp("SELECT SLAVE STATUS ", query_no_space, strlen("SELECT SLAVE STATUS ")) == 0 - || strncasecmp("SELECT REPLICA STATUS ", query_no_space, strlen("SELECT REPLICA STATUS ")) == 0 - ) { - uint64_t addr_offset { - strstr(query_no_space, "REPLICA") ? strlen("SELECT REPLICA STATUS ") : strlen("SELECT SLAVE STATUS ") - }; - if (strlen(query_no_space) > strlen("SELECT SLAVE STATUS ") + 5) { - pthread_mutex_lock(&GloSQLite3Server->test_replicationlag_mutex); - // the current test doesn't try to simulate failures, therefore it will return immediately - if (GloSQLite3Server->replicationlag_map_size() == 0) { - // probably never initialized - GloSQLite3Server->load_replicationlag_table(sess); + if ( + strncasecmp("SELECT SLAVE STATUS ", query_no_space, k_select_slave_status_len) == 0 + || strncasecmp("SELECT REPLICA STATUS ", query_no_space, k_select_replica_status_len) == 0 + ) { + uint64_t addr_offset { + strstr(query_no_space, "REPLICA") ? k_select_replica_status_len : k_select_slave_status_len + }; + if (strlen(query_no_space) > k_select_slave_status_len + 5) { + pthread_mutex_lock(&GloSQLite3Server->test_replicationlag_mutex); + // the current test doesn't try to simulate failures, therefore it will return immediately + if (GloSQLite3Server->replicationlag_map_size() == 0) { + // probably never initialized + GloSQLite3Server->load_replicationlag_table(sess); } const int* rc = GloSQLite3Server->replicationlag_test_value(query_no_space + addr_offset); free(query); From 0eeb652fddd8dd2ae878e406d777269a69caa68a Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 13:35:48 +0000 Subject: [PATCH 051/227] SQLite3: cache lengths and quoted table name handling for S5813 --- src/SQLite3_Server.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index fc8c023a08..42adb24658 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -584,9 +584,13 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p if (!strncasecmp(SELECT_VERSION_COMMENT, query_no_space, query_no_space_length)) { l_free(query_length,query); #if defined(TEST_AURORA) || defined(TEST_GALERA) || defined(TEST_GROUPREP) || defined(TEST_READONLY) || defined(TEST_REPLICATIONLAG) || defined(TEST_RDS_BGD) - char *a = (char *)"SELECT '(ProxySQL Automated Test Server) - %s'"; - query = (char *)malloc(strlen(a)+strlen(sess->client_myds->proxy_addr.addr)); - sprintf(query,a,sess->client_myds->proxy_addr.addr); + const char* a = "SELECT '(ProxySQL Automated Test Server) - %s'"; + const char* proxy_addr = sess->client_myds->proxy_addr.addr; + const size_t a_len = strlen(a); + const size_t proxy_addr_len = proxy_addr ? strlen(proxy_addr) : 0; + const size_t query_len = a_len + proxy_addr_len + 1; + query = (char *)malloc(query_len); + snprintf(query, query_len, a, proxy_addr); #else query=l_strdup("SELECT '(ProxySQL SQLite3 Server)'"); #endif // TEST_AURORA || TEST_GALERA || TEST_GROUPREP || TEST_READONLY || TEST_REPLICATIONLAG || TEST_RDS_BGD @@ -711,12 +715,12 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p tbh=dbh; dbh=strdup("main"); } - if (strlen(tbh)>=3 && tbh[0]=='`' && tbh[strlen(tbh)-1]=='`') { // tablename is quoted - char *tbh_tmp=(char *)malloc(strlen(tbh)-1); size_t tbh_len = strlen(tbh); - size_t quoted_len = tbh_len - 2; - memcpy(tbh_tmp, tbh + 1, quoted_len); - tbh_tmp[quoted_len] = 0; + if (tbh_len>=3 && tbh[0]=='`' && tbh[tbh_len-1]=='`') { // tablename is quoted + const size_t quoted_len = tbh_len - 2; + char *tbh_tmp=(char *)malloc(quoted_len + 1); + memcpy(tbh_tmp, tbh + 1, quoted_len); + tbh_tmp[quoted_len] = 0; free(tbh); tbh=tbh_tmp; } From a1654aca23315592564626f0d2fa5688e049a115 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 13:36:22 +0000 Subject: [PATCH 052/227] ProxySQL_Config: cache query template lengths before malloc sizing --- lib/ProxySQL_Config.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index 0609ffceb4..2cff2a1462 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -53,7 +53,7 @@ ProxySQL_Config:: ~ProxySQL_Config() { void ProxySQL_Config::addField(std::string& data, const char* name, const char* value, const char* dq) { std::stringstream ss; - if (!value || !strlen(value)) return; + if (value == NULL || value[0] == 0) return; // Escape the double quotes in all the fields contents std::string esc_value { value }; @@ -93,7 +93,9 @@ void ProxySQL_Config::addField(std::string& data, const char* name, const char* int ProxySQL_Config::Read_Global_Variables_from_configfile(const char *prefix) { if (prefix == NULL) return 0; const Setting& root = GloVars.confFile->cfg.getRoot(); - char *groupname=(char *)malloc(strlen(prefix)+strlen((char *)"_variables")+1); + const size_t prefix_len = strlen(prefix); + const size_t suffix_len = sizeof("_variables") - 1; + char *groupname=(char *)malloc(prefix_len + suffix_len + 1); sprintf(groupname,"%s%s",prefix,"_variables"); if (root.exists(groupname)==false) { free(groupname); @@ -103,7 +105,6 @@ int ProxySQL_Config::Read_Global_Variables_from_configfile(const char *prefix) { int count = group.getLength(); //fprintf(stderr, "Found %d %s_variables\n",count, prefix); int i; - size_t prefix_len = strlen(prefix); admindb->execute("PRAGMA foreign_keys = OFF"); // Prepare statement once for all inserts auto [rc, stmt] = admindb->prepare_v2("INSERT OR REPLACE INTO global_variables VALUES (?1, ?2)"); @@ -527,6 +528,7 @@ int ProxySQL_Config::Read_Restapi_from_configfile() { char *comment_escaped = escape_string_single_quotes(comment_escaped_raw, false); const char *q = id_exists ? q_with_id : q_without_id; + const size_t query_base_len = strlen(q); const std::string active_str = std::to_string(active); const std::string timeout_ms_str = std::to_string(timeout_ms); const std::string id_str = id_exists ? std::to_string(id) : std::string(); @@ -534,14 +536,18 @@ int ProxySQL_Config::Read_Restapi_from_configfile() { const char* safe_uri_escaped = uri_escaped ? uri_escaped : ""; const char* safe_script_escaped = script_escaped ? script_escaped : ""; const char* safe_comment_escaped = comment_escaped ? comment_escaped : ""; + const size_t safe_method_len = strlen(safe_method_escaped); + const size_t safe_uri_len = strlen(safe_uri_escaped); + const size_t safe_script_len = strlen(safe_script_escaped); + const size_t safe_comment_len = strlen(safe_comment_escaped); size_t query_len = - strlen(q) + + query_base_len + active_str.size() + timeout_ms_str.size() + - strlen(safe_method_escaped) + - strlen(safe_uri_escaped) + - strlen(safe_script_escaped) + - strlen(safe_comment_escaped) + + safe_method_len + + safe_uri_len + + safe_script_len + + safe_comment_len + 40 + (id_exists ? id_str.size() : 0); char *query=(char *)malloc(query_len); @@ -1988,7 +1994,7 @@ int ProxySQL_Config::Write_Global_Variables_to_configfile(std::string& data) { data += "}\n\n" + prefix + "_variables = \n{\n"; } } - if (r->fields[1] && strlen(r->fields[1])) { + if (r->fields[1] && r->fields[1][0] != '\0') { std::stringstream ss; ss << "\t" << r->fields[0] + p1.size() + 1 << "=\"" << r->fields[1] << "\"\n"; data += ss.str(); @@ -2751,8 +2757,9 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_from_configfile() { const std::string multiplex_str = std::to_string(multiplex); const std::string log_str = std::to_string(log); const std::string apply_str = std::to_string(apply); + const size_t q_len = strlen(q); size_t query_len = - strlen(q) + + q_len + rule_id_str.size() + active_str.size() + flagIN_str.size() + From a3703ab7d808dfaee557626eb974e130402dd187 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 13:36:50 +0000 Subject: [PATCH 053/227] MySQL_Protocol: cache table-name length in COM_FIELD_LIST query synthesis --- lib/MySQL_Protocol.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 47eedb5872..6b7f313b2f 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -3783,10 +3783,15 @@ bool MySQL_Protocol::generate_COM_QUERY_from_COM_FIELD_LIST(PtrSize_t *pkt) { a = memchr((void *)pkt_ptr, 0, o_pkt_size-5); if (a==NULL) return false; // we failed to parse char *tablename = strdup(pkt_ptr); - unsigned int wild_len = o_pkt_size - 5 - strlen(tablename) - 1; + if (tablename == nullptr) { + l_free(pkt->size, pkt->ptr); + return false; + } + const size_t tablename_len = strlen(tablename); + unsigned int wild_len = o_pkt_size - 5 - tablename_len - 1; char *wild = NULL; if (wild_len > 0) { - pkt_ptr+=strlen(tablename); + pkt_ptr += tablename_len; pkt_ptr++; wild=strndup(pkt_ptr,wild_len); } @@ -3800,10 +3805,11 @@ bool MySQL_Protocol::generate_COM_QUERY_from_COM_FIELD_LIST(PtrSize_t *pkt) { } char *qt = (char *)"SELECT * FROM `%s` WHERE 1=0"; - q = (char *)malloc(strlen(qt)+strlen(tablename)); - sprintf(q,qt,tablename); + size_t q_len = snprintf(NULL, 0, qt, tablename); + q = (char *)malloc(q_len + 1); + sprintf(q, qt, tablename); l_free(pkt->size, pkt->ptr); - pkt->size = strlen(q)+5; + pkt->size = q_len + 5; mysql_hdr Hdr; Hdr.pkt_id=1; Hdr.pkt_length = pkt->size - 4; From d3000e2917b24ef667c7d5681909ec44bc964b9d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 13:41:50 +0000 Subject: [PATCH 054/227] Admin_Handler: harden S5813 hotspots in purge and flush handlers --- lib/Admin_Handler.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index 3d5ee68418..0fb4465b65 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -584,7 +584,9 @@ std::tuple parse_command_purge_query_digests(cha if (prefix) { match = true; - if (strstr(prefix, "_pgsql_") != nullptr) { + const std::string_view prefix_sv(prefix, prefix_len); + const std::string_view pgsql_tag("_pgsql_"); + if (prefix_sv.find(pgsql_tag) != std::string_view::npos) { server_type = SERVER_TYPE_PGSQL; } @@ -1057,7 +1059,12 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } - if (!strcasecmp("PROXYSQL FLUSH PASSTHROUGH_AUTH_CACHE", query_no_space)) { + static const char *flush_pass_query = "PROXYSQL FLUSH PASSTHROUGH_AUTH_CACHE"; + static const size_t flush_pass_query_len = sizeof("PROXYSQL FLUSH PASSTHROUGH_AUTH_CACHE") - 1; + if ( + query_no_space_length == static_cast(flush_pass_query_len) + && !strncasecmp(flush_pass_query, query_no_space, flush_pass_query_len) + ) { proxy_info("Received PROXYSQL FLUSH PASSTHROUGH_AUTH_CACHE command\n"); ProxySQL_Admin *SPA = (ProxySQL_Admin *)pa; if (GloMyPTAuthCache) { From f1d5ebf6bf3cde6b8742fa8b27e42d783ad89548 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:09:26 +0000 Subject: [PATCH 055/227] Avoid repeated strlen on NULL-checked strings in config SQL builders --- lib/ProxySQL_Config.cpp | 213 ++++++++++++++++++++-------------------- 1 file changed, 109 insertions(+), 104 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index 2cff2a1462..44afad80ce 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -8,9 +8,14 @@ #include #include #include +#include #include #include +static inline size_t safe_strlen(const char *s) { + return s ? std::char_traits::length(s) : 0; +} + const char* config_header = "########################################################################################\n" "# This config file is parsed using libconfig , and its grammar is described in:\n" "# http://www.hyperrealm.com/libconfig/libconfig_manual.html#Configuration-File-Grammar\n" @@ -93,7 +98,7 @@ void ProxySQL_Config::addField(std::string& data, const char* name, const char* int ProxySQL_Config::Read_Global_Variables_from_configfile(const char *prefix) { if (prefix == NULL) return 0; const Setting& root = GloVars.confFile->cfg.getRoot(); - const size_t prefix_len = strlen(prefix); + const size_t prefix_len = safe_strlen(prefix); const size_t suffix_len = sizeof("_variables") - 1; char *groupname=(char *)malloc(prefix_len + suffix_len + 1); sprintf(groupname,"%s%s",prefix,"_variables"); @@ -243,10 +248,10 @@ int ProxySQL_Config::Read_MySQL_Users_from_configfile(std::string& error) { char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - const size_t query_base_len = strlen(q); + const size_t query_base_len = safe_strlen(q); const size_t username_len = username.size(); const size_t password_len = password.size(); - const size_t safe_comment_len = strlen(safe_comment); + const size_t safe_comment_len = safe_strlen(safe_comment); const size_t attributes_len = attributes.size(); const size_t query_len = query_base_len + username_len + password_len + safe_comment_len + attributes_len + 128; char *query=(char *)malloc(query_len); @@ -356,7 +361,7 @@ int ProxySQL_Config::Read_Scheduler_from_configfile() { sched.lookupValue("comment", comment); - const size_t query_base_len = strlen(q); + const size_t query_base_len = safe_strlen(q); const string id_str = to_string(id); const string active_str = to_string(active); const string interval_ms_str = to_string(interval_ms); @@ -528,7 +533,7 @@ int ProxySQL_Config::Read_Restapi_from_configfile() { char *comment_escaped = escape_string_single_quotes(comment_escaped_raw, false); const char *q = id_exists ? q_with_id : q_without_id; - const size_t query_base_len = strlen(q); + const size_t query_base_len = safe_strlen(q); const std::string active_str = std::to_string(active); const std::string timeout_ms_str = std::to_string(timeout_ms); const std::string id_str = id_exists ? std::to_string(id) : std::string(); @@ -536,10 +541,10 @@ int ProxySQL_Config::Read_Restapi_from_configfile() { const char* safe_uri_escaped = uri_escaped ? uri_escaped : ""; const char* safe_script_escaped = script_escaped ? script_escaped : ""; const char* safe_comment_escaped = comment_escaped ? comment_escaped : ""; - const size_t safe_method_len = strlen(safe_method_escaped); - const size_t safe_uri_len = strlen(safe_uri_escaped); - const size_t safe_script_len = strlen(safe_script_escaped); - const size_t safe_comment_len = strlen(safe_comment_escaped); + const size_t safe_method_len = safe_strlen(safe_method_escaped); + const size_t safe_uri_len = safe_strlen(safe_uri_escaped); + const size_t safe_script_len = safe_strlen(safe_script_escaped); + const size_t safe_comment_len = safe_strlen(safe_comment_escaped); size_t query_len = query_base_len + active_str.size() + @@ -920,7 +925,7 @@ int ProxySQL_Config::Read_MySQL_Query_Rules_from_configfile() { //if (user.lookupValue("default_schema", default_schema)==false) default_schema=""; - const size_t query_base_len = strlen(q); + const size_t query_base_len = safe_strlen(q); const string rule_id_str = to_string(rule_id); const string active_str = to_string(active); const string flagIN_str = to_string(flagIN); @@ -1429,10 +1434,10 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - const size_t query_base_len = strlen(q); + const size_t query_base_len = safe_strlen(q); const size_t status_len = status.size(); const size_t address_len = address.size(); - const size_t safe_comment_len = strlen(safe_comment); + const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + status_len + address_len + safe_comment_len + 128; char *query=(char *)malloc(query_len); sprintf(query,q, address.c_str(), port, gtid_port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); @@ -1479,9 +1484,9 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *t=escape_string_single_quotes(t1, false); const char* safe_comment = o ? o : ""; const char* safe_check_type = t ? t : ""; - const size_t query_base_len = strlen(q); - const size_t safe_comment_len = strlen(safe_comment); - const size_t safe_check_type_len = strlen(safe_check_type); + const size_t query_base_len = safe_strlen(q); + const size_t safe_comment_len = safe_strlen(safe_comment); + const size_t safe_check_type_len = safe_strlen(safe_check_type); const size_t query_len = query_base_len + safe_comment_len + safe_check_type_len + 32; char *query=(char *)malloc(query_len); sprintf(query,q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); @@ -1499,7 +1504,7 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const Setting &mysql_servers_ssl_params = root["mysql_servers_ssl_params"]; int count = mysql_servers_ssl_params.getLength(); char *q=(char *)"INSERT OR REPLACE INTO mysql_servers_ssl_params (hostname, port, username, ssl_ca, ssl_cert, ssl_key, ssl_capath, ssl_crl, ssl_crlpath, ssl_cipher, tls_version, comment) VALUES ('%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')"; - const size_t q_len = strlen(q); + const size_t q_len = safe_strlen(q); for (i=0; i< count; i++) { const Setting &line = mysql_servers_ssl_params[i]; string hostname = ""; @@ -1542,7 +1547,7 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t ssl_cipher_len = ssl_cipher.length(); const size_t tls_version_len = tls_version.length(); const char* safe_comment = o ? o : ""; - const size_t escaped_comment_len = strlen(safe_comment); + const size_t escaped_comment_len = safe_strlen(safe_comment); char *query=(char *)malloc( q_len + hostname_len + username_len @@ -1561,57 +1566,57 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { rows++; } } - if (root.exists("mysql_group_replication_hostgroups")==true) { - const Setting &mysql_group_replication_hostgroups = root["mysql_group_replication_hostgroups"]; - int count = mysql_group_replication_hostgroups.getLength(); - char *q=(char *)"INSERT OR REPLACE INTO mysql_group_replication_hostgroups (writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, comment) VALUES (%d, %d, %d, %d, %d, %d, %d, %d, '%s')"; - for (i=0; i< count; i++) { - const Setting &line = mysql_group_replication_hostgroups[i]; - int writer_hostgroup; - int backup_writer_hostgroup; - int reader_hostgroup; - int offline_hostgroup; - int active=1; // default - int max_writers; - int writer_is_also_reader; - int max_transactions_behind; - std::string comment=""; - if (line.lookupValue("writer_hostgroup", writer_hostgroup)==false) { - proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory writer_hostgroup\n"); - continue; - } - if (line.lookupValue("backup_writer_hostgroup", backup_writer_hostgroup)==false) { - proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory backup_writer_hostgroup\n"); - continue; - } - if (line.lookupValue("reader_hostgroup", reader_hostgroup)==false) { - proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory reader_hostgroup\n"); - continue; - } - if (line.lookupValue("offline_hostgroup", offline_hostgroup)==false) { - proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory offline_hostgroup\n"); - continue; - } + if (root.exists("mysql_group_replication_hostgroups")==true) { + const Setting &mysql_group_replication_hostgroups = root["mysql_group_replication_hostgroups"]; + int count = mysql_group_replication_hostgroups.getLength(); + char *q=(char *)"INSERT OR REPLACE INTO mysql_group_replication_hostgroups (writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, comment) VALUES (%d, %d, %d, %d, %d, %d, %d, %d, '%s')"; + for (i=0; i< count; i++) { + const Setting &line = mysql_group_replication_hostgroups[i]; + int writer_hostgroup; + int backup_writer_hostgroup; + int reader_hostgroup; + int offline_hostgroup; + int active=1; // default + int max_writers; + int writer_is_also_reader; + int max_transactions_behind; + std::string comment=""; + if (line.lookupValue("writer_hostgroup", writer_hostgroup)==false) { + proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory writer_hostgroup\n"); + continue; + } + if (line.lookupValue("backup_writer_hostgroup", backup_writer_hostgroup)==false) { + proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory backup_writer_hostgroup\n"); + continue; + } + if (line.lookupValue("reader_hostgroup", reader_hostgroup)==false) { + proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory reader_hostgroup\n"); + continue; + } + if (line.lookupValue("offline_hostgroup", offline_hostgroup)==false) { + proxy_error("Admin: detected a mysql_group_replication_hostgroups in config file without a mandatory offline_hostgroup\n"); + continue; + } if (line.lookupValue("max_writers", max_writers)==false) max_writers=1; - if (line.lookupValue("writer_is_also_reader", writer_is_also_reader)==false) writer_is_also_reader=0; - if (line.lookupValue("max_transactions_behind", max_transactions_behind)==false) max_transactions_behind=0; - line.lookupValue("comment", comment); - char *o1=strdup(comment.c_str()); - char *o=escape_string_single_quotes(o1, false); - const char* safe_comment = o ? o : ""; - const size_t query_base_len = strlen(q); - const size_t safe_comment_len = strlen(safe_comment); - const size_t query_len = query_base_len + safe_comment_len + 128; // 128 vs sizeof(int)*8 - char *query=(char *)malloc(query_len); - sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); - //fprintf(stderr, "%s\n", query); - admindb->execute(query); - if (o!=o1) free(o); - free(o1); - free(query); - rows++; - } - } + if (line.lookupValue("writer_is_also_reader", writer_is_also_reader)==false) writer_is_also_reader=0; + if (line.lookupValue("max_transactions_behind", max_transactions_behind)==false) max_transactions_behind=0; + line.lookupValue("comment", comment); + char *o1=strdup(comment.c_str()); + char *o=escape_string_single_quotes(o1, false); + const char* safe_comment = o ? o : ""; + const size_t query_base_len = safe_strlen(q); + const size_t safe_comment_len = safe_strlen(safe_comment); + const size_t query_len = query_base_len + safe_comment_len + 128; // 128 vs sizeof(int)*8 + char *query=(char *)malloc(query_len); + sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); + //fprintf(stderr, "%s\n", query); + admindb->execute(query); + if (o!=o1) free(o); + free(o1); + free(query); + rows++; + } + } if (root.exists("mysql_galera_hostgroups")==true) { const Setting &mysql_galera_hostgroups = root["mysql_galera_hostgroups"]; int count = mysql_galera_hostgroups.getLength(); @@ -1650,8 +1655,8 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - const size_t query_base_len = strlen(q); - const size_t safe_comment_len = strlen(safe_comment); + const size_t query_base_len = safe_strlen(q); + const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + safe_comment_len + 128; // 128 vs sizeof(int)*8 char *query=(char *)malloc(query_len); sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); @@ -1710,9 +1715,9 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *p=escape_string_single_quotes(p1, false); const char* safe_comment = o ? o : ""; const char* safe_domain = p ? p : ""; - const size_t query_base_len = strlen(q); - const size_t safe_comment_len = strlen(safe_comment); - const size_t safe_domain_len = strlen(safe_domain); + const size_t query_base_len = safe_strlen(q); + const size_t safe_comment_len = safe_strlen(safe_comment); + const size_t safe_domain_len = safe_strlen(safe_domain); const size_t query_len = query_base_len + safe_comment_len + safe_domain_len + 256; // 128 vs sizeof(int)*8 char *query=(char *)malloc(query_len); sprintf(query,q, writer_hostgroup, reader_hostgroup, active, aurora_port, safe_domain, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, autopurge_missing_checks, safe_comment); @@ -1771,8 +1776,8 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - const size_t query_base_len = strlen(q); - const size_t safe_comment_len = strlen(safe_comment); + const size_t query_base_len = safe_strlen(q); + const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + safe_comment_len + 256; // 128 vs sizeof(int)*8 char *query=(char *)malloc(query_len); sprintf(query,q, writer_hostgroup, reader_hostgroup, green_writer_str, green_reader_str, active, writer_is_also_reader, check_interval_ms, check_timeout_ms, safe_comment); @@ -1947,12 +1952,12 @@ int ProxySQL_Config::Read_ProxySQL_Servers_from_configfile(std::string& error) { server.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - const char* safe_comment = o ? o : ""; - const size_t query_base_len = strlen(q); - const size_t address_len = address.size(); - const size_t safe_comment_len = strlen(safe_comment); - const size_t query_len = query_base_len + address_len + safe_comment_len + 128; - char *query=(char *)malloc(query_len); + const char* safe_comment = o ? o : ""; + const size_t query_base_len = safe_strlen(q); + const size_t address_len = address.size(); + const size_t safe_comment_len = safe_strlen(safe_comment); + const size_t query_len = query_base_len + address_len + safe_comment_len + 128; + char *query=(char *)malloc(query_len); sprintf(query, q, address.c_str(), port, weight, safe_comment); proxy_info("Cluster: Adding ProxySQL Servers %s:%d from config file\n", address.c_str(), port); //fprintf(stderr, "%s\n", query); @@ -2219,10 +2224,10 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { char* o1 = strdup(comment.c_str()); char* o = escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - const size_t query_base_len = strlen(q); + const size_t query_base_len = safe_strlen(q); const size_t status_len = status.size(); const size_t address_len = address.size(); - const size_t safe_comment_len = strlen(safe_comment); + const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + status_len + address_len + safe_comment_len + 128; char* query = (char*)malloc(query_len); sprintf(query, q, address.c_str(), port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); @@ -2269,9 +2274,9 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { char* t = escape_string_single_quotes(t1, false); const char* safe_comment = o ? o : ""; const char* safe_check_type = t ? t : ""; - const size_t query_base_len = strlen(q); - const size_t safe_comment_len = strlen(safe_comment); - const size_t safe_check_type_len = strlen(safe_check_type); + const size_t query_base_len = safe_strlen(q); + const size_t safe_comment_len = safe_strlen(safe_comment); + const size_t safe_check_type_len = safe_strlen(safe_check_type); const size_t query_len = query_base_len + safe_comment_len + safe_check_type_len + 32; char* query = (char*)malloc(query_len); sprintf(query, q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); @@ -2374,7 +2379,7 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { const Setting &pgsql_servers_ssl_params = root["pgsql_servers_ssl_params"]; int count = pgsql_servers_ssl_params.getLength(); char *q=(char *)"INSERT OR REPLACE INTO pgsql_servers_ssl_params (hostname, port, username, ssl_ca, ssl_cert, ssl_key, ssl_crl, ssl_crlpath, ssl_protocol_version_range, comment) VALUES ('%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')"; - const size_t q_len = strlen(q); + const size_t q_len = safe_strlen(q); for (i=0; i< count; i++) { const Setting &line = pgsql_servers_ssl_params[i]; string hostname = ""; @@ -2411,7 +2416,7 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { const size_t ssl_crlpath_len = ssl_crlpath.length(); const size_t ssl_protocol_version_range_len = ssl_protocol_version_range.length(); const char* safe_comment = o ? o : ""; - const size_t escaped_comment_len = strlen(safe_comment); + const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = ( q_len + hostname_len + username_len @@ -2517,10 +2522,10 @@ int ProxySQL_Config::Read_PgSQL_Users_from_configfile(std::string& error) { char* o1 = strdup(comment.c_str()); char* o = escape_string_single_quotes(o1, false); const char* safe_comment = o ? o : ""; - const size_t query_base_len = strlen(q); + const size_t query_base_len = safe_strlen(q); const size_t username_len = username.size(); const size_t password_len = password.size(); - const size_t safe_comment_len = strlen(safe_comment); + const size_t safe_comment_len = safe_strlen(safe_comment); const size_t attributes_len = attributes.size(); const size_t query_len = query_base_len + username_len + password_len + safe_comment_len + attributes_len + 128; char* query = (char*)malloc(query_len); @@ -2757,7 +2762,7 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_from_configfile() { const std::string multiplex_str = std::to_string(multiplex); const std::string log_str = std::to_string(log); const std::string apply_str = std::to_string(apply); - const size_t q_len = strlen(q); + const size_t q_len = safe_strlen(q); size_t query_len = q_len + rule_id_str.size() + @@ -3030,11 +3035,11 @@ int ProxySQL_Config::Read_MySQL_Query_Rules_Fast_Routing_from_configfile() { rule.lookupValue("comment", comment); char *o1 = strdup(comment.c_str()); char *o = escape_string_single_quotes(o1, false); - const size_t q_len = strlen(q); + const size_t q_len = safe_strlen(q); const size_t username_len = username.size(); const size_t schemaname_len = schemaname.size(); const char* safe_comment = o ? o : ""; - const size_t escaped_comment_len = strlen(safe_comment); + const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + schemaname_len + escaped_comment_len + 64; char *query = (char *)malloc(query_len); snprintf(query, query_len, q, username.c_str(), schemaname.c_str(), flagIN, destination_hostgroup, safe_comment); @@ -3071,11 +3076,11 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_Fast_Routing_from_configfile() { rule.lookupValue("comment", comment); char *o1 = strdup(comment.c_str()); char *o = escape_string_single_quotes(o1, false); - const size_t q_len = strlen(q); + const size_t q_len = safe_strlen(q); const size_t username_len = username.size(); const size_t database_len = database.size(); const char* safe_comment = o ? o : ""; - const size_t escaped_comment_len = strlen(safe_comment); + const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + database_len + escaped_comment_len + 64; char *query = (char *)malloc(query_len); snprintf(query, query_len, q, username.c_str(), database.c_str(), flagIN, destination_hostgroup, safe_comment); @@ -3112,12 +3117,12 @@ int ProxySQL_Config::Read_MySQL_Firewall_from_configfile() { u.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - const size_t q_len = strlen(q); + const size_t q_len = safe_strlen(q); const size_t username_len = username.size(); const size_t client_address_len = client_address.size(); const size_t mode_len = mode.size(); const char* safe_comment = o ? o : ""; - const size_t escaped_comment_len = strlen(safe_comment); + const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + mode_len + escaped_comment_len + 32; char *query=(char *)malloc(query_len); snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), safe_comment); @@ -3151,13 +3156,13 @@ int ProxySQL_Config::Read_MySQL_Firewall_from_configfile() { r.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - const size_t q_len = strlen(q); + const size_t q_len = safe_strlen(q); const size_t username_len = username.size(); const size_t client_address_len = client_address.size(); const size_t schemaname_len = schemaname.size(); const size_t digest_len = digest.size(); const char* safe_comment = o ? o : ""; - const size_t escaped_comment_len = strlen(safe_comment); + const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + schemaname_len + digest_len + escaped_comment_len + 64; char *query=(char *)malloc(query_len); snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), schemaname.c_str(), flagIN, digest.c_str(), safe_comment); @@ -3179,7 +3184,7 @@ int ProxySQL_Config::Read_MySQL_Firewall_from_configfile() { std::string fingerprint=""; f.lookupValue("active", active); f.lookupValue("fingerprint", fingerprint); - const size_t q_len = strlen(q); + const size_t q_len = safe_strlen(q); const size_t fingerprint_len = fingerprint.size(); size_t query_len = q_len + fingerprint_len + 16; char *query=(char *)malloc(query_len); @@ -3217,12 +3222,12 @@ int ProxySQL_Config::Read_PgSQL_Firewall_from_configfile() { u.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - const size_t q_len = strlen(q); + const size_t q_len = safe_strlen(q); const size_t username_len = username.size(); const size_t client_address_len = client_address.size(); const size_t mode_len = mode.size(); const char* safe_comment = o ? o : ""; - const size_t escaped_comment_len = strlen(safe_comment); + const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + mode_len + escaped_comment_len + 32; char *query=(char *)malloc(query_len); snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), safe_comment); @@ -3256,13 +3261,13 @@ int ProxySQL_Config::Read_PgSQL_Firewall_from_configfile() { r.lookupValue("comment", comment); char *o1=strdup(comment.c_str()); char *o=escape_string_single_quotes(o1, false); - const size_t q_len = strlen(q); + const size_t q_len = safe_strlen(q); const size_t username_len = username.size(); const size_t client_address_len = client_address.size(); const size_t database_len = database.size(); const size_t digest_len = digest.size(); const char* safe_comment = o ? o : ""; - const size_t escaped_comment_len = strlen(safe_comment); + const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + database_len + digest_len + escaped_comment_len + 64; char *query=(char *)malloc(query_len); snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), database.c_str(), flagIN, digest.c_str(), safe_comment); @@ -3284,7 +3289,7 @@ int ProxySQL_Config::Read_PgSQL_Firewall_from_configfile() { std::string fingerprint=""; f.lookupValue("active", active); f.lookupValue("fingerprint", fingerprint); - const size_t q_len = strlen(q); + const size_t q_len = safe_strlen(q); const size_t fingerprint_len = fingerprint.size(); size_t query_len = q_len + fingerprint_len + 16; char *query=(char *)malloc(query_len); From 483407919fa7650db703d824635a3864f4aa5954 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:09:27 +0000 Subject: [PATCH 056/227] Use compile-time length for column statistics compatibility string check --- lib/Admin_Handler.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index 0fb4465b65..7e97de7086 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -3987,7 +3987,14 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { query_length = strlen(query)+1; goto __run_query; } - if (!strncmp("SELECT COLUMN_NAME, JSON_EXTRACT(HISTOGRAM, '$.\"number-of-buckets-specified\"') FROM information_schema.COLUMN_STATISTICS", query_no_space, strlen("SELECT COLUMN_NAME, JSON_EXTRACT(HISTOGRAM, '$.\"number-of-buckets-specified\"') FROM information_schema.COLUMN_STATISTICS"))) { + constexpr size_t select_column_statistics_len = + sizeof("SELECT COLUMN_NAME, JSON_EXTRACT(HISTOGRAM, '$.\"number-of-buckets-specified\"') FROM information_schema.COLUMN_STATISTICS") - 1; + if ( + !strncmp( + "SELECT COLUMN_NAME, JSON_EXTRACT(HISTOGRAM, '$.\"number-of-buckets-specified\"') FROM information_schema.COLUMN_STATISTICS", + query_no_space, + select_column_statistics_len) + ) { l_free(query_length, query); query = l_strdup("SELECT variable_name AS COLUMN_NAME, Variable_value AS 'JSON_EXTRACT(HISTOGRAM, ''$.\"number-of-buckets-specified\"'')' FROM global_variables WHERE 1=0"); query_length = strlen(query)+1; From 4e56bb81c7c41b49fd4cea6e1e2ff168acd805e8 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:09:29 +0000 Subject: [PATCH 057/227] Use sizeof(spiffe://) for SPIFFE URI prefix compare --- lib/MySQL_Protocol.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 6b7f313b2f..885124adac 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -3437,7 +3437,13 @@ bool MySQL_Protocol::verify_user_attributes(int calling_line, const char *callin re2::RE2 subject_alt_regex(str_spiffe_regex, opts); ret = re2::RE2::FullMatch((*myds)->x509_subject_alt_name, subject_alt_regex); - } else if (strncmp(spiffe_val.c_str(), "spiffe://", strlen("spiffe://"))==0) { + } else if ( + strncmp( + spiffe_val.c_str(), + "spiffe://", + sizeof("spiffe://") - 1 + )==0 + ) { if (strcmp(spiffe_val.c_str(), (*myds)->x509_subject_alt_name)==0) { ret = true; } From 644c0f9093d90271eba7fe429fcf867daef342c0 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:09:31 +0000 Subject: [PATCH 058/227] Cache fixed SQL keyword lengths in MySQL session command parsing --- lib/MySQL_Session.cpp | 74 +++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index dab2715cc7..b493af08d7 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -894,8 +894,9 @@ bool MySQL_Session::handler_CommitRollback(PtrSize_t *pkt) { if (pkt->size <= 5) { return false; } char c=((char *)pkt->ptr)[5]; bool ret=false; + static constexpr size_t commit_len = sizeof("commit") - 1; if (c=='c' || c=='C') { - if (pkt->size==strlen("commit")+5) { + if (pkt->size==commit_len+5) { if (strncasecmp((char *)"commit",(char *)pkt->ptr+5,6)==0) { __sync_fetch_and_add(&MyHGM->status.commit_cnt, 1); ret=true; @@ -903,7 +904,8 @@ bool MySQL_Session::handler_CommitRollback(PtrSize_t *pkt) { } } else { if (c=='r' || c=='R') { - if (pkt->size==strlen("rollback")+5) { + static constexpr size_t rollback_len = sizeof("rollback") - 1; + if (pkt->size==rollback_len+5) { if ( strncasecmp((char *)"rollback",(char *)pkt->ptr+5,8)==0 ) { __sync_fetch_and_add(&MyHGM->status.rollback_cnt, 1); ret=true; @@ -976,14 +978,15 @@ bool MySQL_Session::handler_CommitRollback(PtrSize_t *pkt) { bool MySQL_Session::handler_SetAutocommit(PtrSize_t *pkt) { autocommit_handled=false; sending_set_autocommit=false; - size_t sal=strlen("set autocommit"); + const size_t sal = sizeof("set autocommit") - 1; + const size_t set_session_autocommit_len = sizeof("SET @@session.autocommit") - 1; char * _ptr = (char *)pkt->ptr; #ifdef DEBUG string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Parsing SET command = %s\n", nqn.c_str()); #endif if ( pkt->size >= 7+sal) { - if (strncasecmp((char *)"SET @@session.autocommit",(char *)pkt->ptr+5,strlen((char *)"SET @@session.autocommit"))==0) { + if (strncasecmp((char *)"SET @@session.autocommit",(char *)pkt->ptr+5,set_session_autocommit_len)==0) { memmove(_ptr+9, _ptr+19, pkt->size - 19); memset(_ptr+pkt->size-10,' ',10); } @@ -1237,10 +1240,11 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { l_free(pkt->size,pkt->ptr); return true; } - if (pkt->size==strlen((char *)"select USER()")+5 && strncmp((char *)"select USER()",(char *)pkt->ptr+5,pkt->size-5)==0) { + constexpr size_t select_user_len = sizeof("select USER()") - 1; + if (pkt->size==select_user_len+5 && strncmp((char *)"select USER()",(char *)pkt->ptr+5,pkt->size-5)==0) { // FIXME: this doesn't return AUTOCOMMIT or IN_TRANS char *query1=(char *)"SELECT \"%s\" AS 'USER()'"; - char *query2=(char *)malloc(strlen(query1)+strlen(client_myds->myconn->userinfo->username)+10); + char *query2=(char *)malloc((sizeof("SELECT \"%s\" AS 'USER()'") - 1) + strlen(client_myds->myconn->userinfo->username)+10); sprintf(query2,query1,client_myds->myconn->userinfo->username); char *error; int cols; @@ -1257,7 +1261,8 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { return true; } // MySQL client check command for dollars quote support, starting at version '8.1.0'. See #4300. - if ((pkt->size == strlen("SELECT $$") + 5) && strncasecmp("SELECT $$", (char*)pkt->ptr + 5, pkt->size - 5) == 0) { + static constexpr size_t select_dollar_quote_len = sizeof("SELECT $$") - 1; + if ((pkt->size == select_dollar_quote_len + 5) && strncasecmp("SELECT $$", (char*)pkt->ptr + 5, pkt->size - 5) == 0) { pair err_info { get_dollar_quote_error(mysql_thread___server_version) }; client_myds->DSS=STATE_QUERY_SENT_NET; @@ -1339,8 +1344,9 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { const MARIADB_CHARSET_INFO * c; char * collation_name_unstripped = NULL; char * collation_name = NULL; - if (strcasestr(csname," COLLATE ")) { - collation_name_unstripped = strcasestr(csname," COLLATE ") + strlen(" COLLATE "); + if (strcasestr(csname," COLLATE ")) { + static constexpr size_t collate_prefix_len = sizeof(" COLLATE ") - 1; + collation_name_unstripped = strcasestr(csname," COLLATE ") + collate_prefix_len; collation_name = trim_spaces_and_quotes_in_place(collation_name_unstripped); char *_s1=index(csname,' '); char *_s2=index(csname,'\''); @@ -7976,11 +7982,12 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C // handle case #1797 // handle case #2564 - if ((pkt->size==SELECT_CONNECTION_ID_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_CONNECTION_ID,(char *)pkt->ptr+5,pkt->size-5)==0)) { + static constexpr size_t connection_id_len = sizeof("CONNECTION_ID()") - 1; + if ((pkt->size==SELECT_CONNECTION_ID_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_CONNECTION_ID,(char *)pkt->ptr+5,pkt->size-5)==0)) { char buf[32]; char buf2[32]; sprintf(buf,"%u",thread_session_id); - int l0=strlen("CONNECTION_ID()"); + int l0=connection_id_len; memcpy(buf2,(char *)pkt->ptr+5+SELECT_CONNECTION_ID_LEN-l0,l0); buf2[l0]=0; unsigned int nTrx=NumActiveTransactions(); @@ -8050,22 +8057,24 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C (pkt->size==SELECT_LAST_INSERT_ID_FROM_DUAL_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_LAST_INSERT_ID_FROM_DUAL,(char *)pkt->ptr+5,pkt->size-5)==0) || (pkt->size==SELECT_LAST_INSERT_ID_LIMIT1_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_LAST_INSERT_ID_LIMIT1,(char *)pkt->ptr+5,pkt->size-5)==0) - || - (pkt->size==SELECT_VARIABLE_IDENTITY_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_VARIABLE_IDENTITY,(char *)pkt->ptr+5,pkt->size-5)==0) - || - (pkt->size==SELECT_VARIABLE_IDENTITY_LIMIT1_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_VARIABLE_IDENTITY_LIMIT1,(char *)pkt->ptr+5,pkt->size-5)==0) + || + (pkt->size==SELECT_VARIABLE_IDENTITY_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_VARIABLE_IDENTITY,(char *)pkt->ptr+5,pkt->size-5)==0) + || + (pkt->size==SELECT_VARIABLE_IDENTITY_LIMIT1_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_VARIABLE_IDENTITY_LIMIT1,(char *)pkt->ptr+5,pkt->size-5)==0) ) { char buf[32]; sprintf(buf,"%llu",last_insert_id); char buf2[32]; - int l0=0; - if (strcasestr(dig,"LAST_INSERT_ID")){ - l0=strlen("LAST_INSERT_ID()"); - memcpy(buf2,(char *)pkt->ptr+5+SELECT_LAST_INSERT_ID_LEN-l0,l0); - }else if(strcasestr(dig,"@@IDENTITY")){ - l0=strlen("@@IDENTITY"); - memcpy(buf2,(char *)pkt->ptr+5+SELECT_VARIABLE_IDENTITY_LEN-l0,l0); - } + int l0=0; + if (strcasestr(dig,"LAST_INSERT_ID")){ + static constexpr size_t last_insert_id_len = sizeof("LAST_INSERT_ID()") - 1; + l0=last_insert_id_len; + memcpy(buf2,(char *)pkt->ptr+5+SELECT_LAST_INSERT_ID_LEN-l0,l0); + }else if(strcasestr(dig,"@@IDENTITY")){ + static constexpr size_t identity_len = sizeof("@@IDENTITY") - 1; + l0=identity_len; + memcpy(buf2,(char *)pkt->ptr+5+SELECT_VARIABLE_IDENTITY_LEN-l0,l0); + } buf2[l0]=0; unsigned int nTrx=NumActiveTransactions(); uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); @@ -9150,9 +9159,11 @@ void MySQL_Session::add_ldap_comment_to_pkt(PtrSize_t *_pkt) { if (client_myds->myconn->userinfo->fe_username==NULL) return; char *fe=client_myds->myconn->userinfo->fe_username; + constexpr size_t ldap_comment_prefix_len = sizeof(" /* %s=%s */") - 1; char *a = (char *)" /* %s=%s */"; - char *b = (char *)malloc(strlen(a)+strlen(fe)+strlen(mysql_thread___add_ldap_user_comment)); + char *b = (char *)malloc(ldap_comment_prefix_len+strlen(fe)+strlen(mysql_thread___add_ldap_user_comment)); sprintf(b,a,mysql_thread___add_ldap_user_comment,fe); + const size_t b_len = strlen(b); PtrSize_t _new_pkt; _new_pkt.ptr = malloc(strlen(b) + _pkt->size); memcpy(_new_pkt.ptr , _pkt->ptr, 5); @@ -9162,27 +9173,28 @@ void MySQL_Session::add_ldap_comment_to_pkt(PtrSize_t *_pkt) { if (idx) { size_t first_word_len = (char *)idx - (char *)_pkt->ptr - 5; if (((char *)_pkt->ptr+5)[0]=='/' && ((char *)_pkt->ptr+5)[1]=='*') { - void* comment_endpos = memmem(static_cast(_pkt->ptr)+7, _pkt->size-7, "*/", strlen("*/")); + static constexpr size_t closing_comment_len = sizeof("*/") - 1; + void* comment_endpos = memmem(static_cast(_pkt->ptr)+7, _pkt->size-7, "*/", closing_comment_len); if (comment_endpos == NULL || idx < comment_endpos) { b[1]=' '; b[2]=' '; - b[strlen(b)-1] = ' '; - b[strlen(b)-2] = ' '; + b[b_len-1] = ' '; + b[b_len-2] = ' '; } } memcpy(_c, (char *)_pkt->ptr+5, first_word_len); _c+= first_word_len; - memcpy(_c,b,strlen(b)); - _c+= strlen(b); + memcpy(_c,b,b_len); + _c+= b_len; memcpy(_c, (char *)idx, _pkt->size - 5 - first_word_len); } else { memcpy(_c, (char *)_pkt->ptr+5, _pkt->size-5); _c+=_pkt->size-5; - memcpy(_c,b,strlen(b)); + memcpy(_c,b,b_len); } l_free(_pkt->size,_pkt->ptr); - _pkt->size = _pkt->size + strlen(b); + _pkt->size = _pkt->size + b_len; _pkt->ptr = _new_pkt.ptr; free(b); CurrentQuery.QueryLength = _pkt->size - 5; From 907901dc46cac17d7d253feb9e51f03bfef71533 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:09:32 +0000 Subject: [PATCH 059/227] Replace dynamic strlen for test SQL templates with fixed-size constants --- src/SQLite3_Server.cpp | 80 +++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 42adb24658..aa42980400 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -858,11 +858,17 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } delete control_result; - if (run_query && rds_bgd_table_check) { - const char* topology_sql = topology_present ? "SELECT 1" : "SELECT 1 WHERE 0"; - l_free(query_length,query); - query=l_strdup(topology_sql); - query_length=strlen(topology_sql)+1; + if (run_query && rds_bgd_table_check) { + const char* topology_sql = topology_present ? "SELECT 1" : "SELECT 1 WHERE 0"; + static constexpr size_t topology_sql_len = + sizeof("SELECT 1") - 1; + const size_t topology_len = + topology_present ? + topology_sql_len : + (sizeof("SELECT 1 WHERE 0") - 1); + l_free(query_length,query); + query=l_strdup(topology_sql); + query_length=topology_len + 1; } else if (run_query && (configured_error != 0 || !topology_present)) { const uint16_t error_code = configured_error ? static_cast(configured_error) : 1146; @@ -872,53 +878,53 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p GloSQLite3Server->send_MySQL_ERR( &sess->client_myds->myprot, error_code, error_msg); run_query=false; - } else if (run_query) { + } else if (run_query) { const std::string topology_query { "SELECT id,endpoint,topology_port AS port,role,status " "FROM RDS_BGD_TOPOLOGY WHERE " + predicate + " ORDER BY row_order" - }; - l_free(query_length,query); - query=l_strdup(topology_query.c_str()); - query_length=topology_query.length()+1; - } + }; + l_free(query_length,query); + query=l_strdup(topology_query.c_str()); + query_length=topology_query.length()+1; + } } } } #endif // TEST_RDS_BGD -#ifdef TEST_AURORA - if (strstr(query_no_space,(char *)"REPLICA_HOST_STATUS")) { - pthread_mutex_lock(&GloSQLite3Server->aurora_mutex); + #ifdef TEST_AURORA + if (strstr(query_no_space,(char *)"REPLICA_HOST_STATUS")) { + pthread_mutex_lock(&GloSQLite3Server->aurora_mutex); - if (strcasestr(query_no_space, TEST_AURORA_MONITOR_BASE_QUERY)) { - string s_whg { query_no_space + strlen(TEST_AURORA_MONITOR_BASE_QUERY) }; - uint32_t whg = atoi(s_whg.c_str()); + if (strcasestr(query_no_space, TEST_AURORA_MONITOR_BASE_QUERY)) { + string s_whg { query_no_space + (sizeof(TEST_AURORA_MONITOR_BASE_QUERY) - 1) }; + uint32_t whg = atoi(s_whg.c_str()); - GloSQLite3Server->populate_aws_aurora_table(sess, whg); - vector hgs_info { get_hgs_info(GloAdmin->admindb) }; + GloSQLite3Server->populate_aws_aurora_table(sess, whg); + vector hgs_info { get_hgs_info(GloAdmin->admindb) }; - const auto match_writer = [&whg](const aurora_hg_info_t& hg_info) { - return std::get(hg_info) == whg; - }; - const auto hg_info_it = std::find_if(hgs_info.begin(), hgs_info.end(), match_writer); - string select_query { - "SELECT SERVER_ID,SESSION_ID,LAST_UPDATE_TIMESTAMP,REPLICA_LAG_IN_MILLISECONDS,CPU" - " FROM REPLICA_HOST_STATUS " - }; + const auto match_writer = [&whg](const aurora_hg_info_t& hg_info) { + return std::get(hg_info) == whg; + }; + const auto hg_info_it = std::find_if(hgs_info.begin(), hgs_info.end(), match_writer); + string select_query { + "SELECT SERVER_ID,SESSION_ID,LAST_UPDATE_TIMESTAMP,REPLICA_LAG_IN_MILLISECONDS,CPU" + " FROM REPLICA_HOST_STATUS " + }; - if (hg_info_it == hgs_info.end()) { - select_query += " LIMIT 0"; - } else { - const string& domain_name { std::get(*hg_info_it) }; - select_query += " WHERE DOMAIN_NAME='" + domain_name + "' ORDER BY SERVER_ID"; - } + if (hg_info_it == hgs_info.end()) { + select_query += " LIMIT 0"; + } else { + const string& domain_name { std::get(*hg_info_it) }; + select_query += " WHERE DOMAIN_NAME='" + domain_name + "' ORDER BY SERVER_ID"; + } - free(query); - query = static_cast(malloc(select_query.length() + 1)); - memcpy(query, select_query.c_str(), select_query.length() + 1); + free(query); + query = static_cast(malloc(select_query.length() + 1)); + memcpy(query, select_query.c_str(), select_query.length() + 1); + } } - } #endif // TEST_AURORA #ifdef TEST_GALERA if (strstr(query_no_space,(char *)"HOST_STATUS_GALERA")) { From ca36350cac7f5485cba36859959c5f3d84eafd72 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:25:11 +0000 Subject: [PATCH 060/227] security: use bounded token copy in Admin iface parsing --- include/Admin_ifaces.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/Admin_ifaces.h b/include/Admin_ifaces.h index 2a6c6fbac9..0418974623 100644 --- a/include/Admin_ifaces.h +++ b/include/Admin_ifaces.h @@ -136,12 +136,11 @@ class admin_main_loop_listeners { const char* token; ifaces=reset_ifaces(ifaces); i=0; - for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - size_t token_len = strlen(token); - ifaces[i]=(char *)malloc(token_len + 1); - memcpy(ifaces[i],token, token_len + 1); - i++; - } + for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { + size_t token_len = strlen(token); + ifaces[i] = strdup(token); + i++; + } free_tokenizer( &tok ); version++; wrunlock(); From 236505bce4b0c6a4baa7e7a520d2f6ec2878e09b Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:25:21 +0000 Subject: [PATCH 061/227] security: avoid null-terminator memcpy in ClickHouse iface copy --- lib/ClickHouse_Server.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/ClickHouse_Server.cpp b/lib/ClickHouse_Server.cpp index b4847b394f..378e965e4d 100644 --- a/lib/ClickHouse_Server.cpp +++ b/lib/ClickHouse_Server.cpp @@ -573,12 +573,13 @@ class sqlite3server_main_loop_listeners { const char* token; ifaces=reset_ifaces(ifaces); i=0; - for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - size_t token_len = strlen(token); - ifaces[i]=(char *)malloc(token_len + 1); - memcpy(ifaces[i],token, token_len + 1); - i++; - } + for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { + size_t token_len = strlen(token); + ifaces[i]=(char *)malloc(token_len + 1); + memcpy(ifaces[i], token, token_len); + ifaces[i][token_len] = '\0'; + i++; + } free_tokenizer( &tok ); version++; wrunlock(); From 7eae6541c2a4470079b8dd1c2e5f85f895a7dd4d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:25:25 +0000 Subject: [PATCH 062/227] security: make SQLite iface token copy explicit null-terminated --- src/SQLite3_Server.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index aa42980400..eeb5a67933 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -231,12 +231,13 @@ class sqlite3server_main_loop_listeners { const char* token; ifaces=reset_ifaces(ifaces); i=0; - for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - size_t token_len = strlen(token); - ifaces[i]=(char *)malloc(token_len + 1); - memcpy(ifaces[i],token, token_len + 1); - i++; - } + for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { + size_t token_len = strlen(token); + ifaces[i]=(char *)malloc(token_len + 1); + memcpy(ifaces[i], token, token_len); + ifaces[i][token_len] = '\0'; + i++; + } free_tokenizer( &tok ); version++; wrunlock(); From 4284bfa4cd90346e9dab997d188cfccd594d12fe Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:25:31 +0000 Subject: [PATCH 063/227] security: explicit null termination in TAP SQLite iface tokens --- test/tap/tap/SQLite3_Server.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/tap/tap/SQLite3_Server.cpp b/test/tap/tap/SQLite3_Server.cpp index e53e22b29a..37c71be332 100644 --- a/test/tap/tap/SQLite3_Server.cpp +++ b/test/tap/tap/SQLite3_Server.cpp @@ -201,12 +201,13 @@ class sqlite3server_main_loop_listeners { const char* token; ifaces=reset_ifaces(ifaces); i=0; - for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - size_t token_len = strlen(token); - ifaces[i]=(char *)malloc(token_len+1); - memcpy(ifaces[i], token, token_len+1); - i++; - } + for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { + size_t token_len = strlen(token); + ifaces[i]=(char *)malloc(token_len+1); + memcpy(ifaces[i], token, token_len); + ifaces[i][token_len] = '\0'; + i++; + } free_tokenizer( &tok ); version++; wrunlock(); From 5b29b25bf155076abcdb47e2574825e19901710d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:25:38 +0000 Subject: [PATCH 064/227] security: avoid oversized tokenizer copy by explicit length+terminator --- lib/c_tokenizer.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/c_tokenizer.cpp b/lib/c_tokenizer.cpp index be1829c67c..a3a6226c50 100644 --- a/lib/c_tokenizer.cpp +++ b/lib/c_tokenizer.cpp @@ -29,12 +29,13 @@ void tokenizer(tokenizer_t *result, const char* s, const char* delimiters, int e result->s_length = ( (s && delimiters) ? tokenizer_strlen(s) : 0 ); result->s = NULL; if (result->s_length) { - if (result->s_length > (PROXYSQL_TOKENIZER_BUFFSIZE-1)) { - result->s = strdup(s); - } else { - memcpy(result->buffer, s, result->s_length + 1); - result->s = result->buffer; - } + if (result->s_length > (PROXYSQL_TOKENIZER_BUFFSIZE-1)) { + result->s = strdup(s); + } else { + memcpy(result->buffer, s, result->s_length); + result->buffer[result->s_length] = '\0'; + result->s = result->buffer; + } } result->delimiters = delimiters; result->current = NULL; From dc879a2fb30430c944ccdc7b1d37debc99990c09 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:25:46 +0000 Subject: [PATCH 065/227] security: split fixed-buffer string copies into length + terminator in digest stats --- lib/QP_query_digest_stats.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/QP_query_digest_stats.cpp b/lib/QP_query_digest_stats.cpp index 2fd0c35e0f..9a063d174f 100644 --- a/lib/QP_query_digest_stats.cpp +++ b/lib/QP_query_digest_stats.cpp @@ -35,21 +35,24 @@ QP_query_digest_stats::QP_query_digest_stats(const char* _user, const char* _sch } size_t _user_len = strlen(_user); if (_user_len < sizeof(username_buf)) { - memcpy(username_buf, _user, _user_len + 1); + memcpy(username_buf, _user, _user_len); + username_buf[_user_len] = '\0'; username = username_buf; } else { username = strdup(_user); } size_t _schema_len = strlen(_schema); if (_schema_len < sizeof(schemaname_buf)) { - memcpy(schemaname_buf, _schema, _schema_len + 1); + memcpy(schemaname_buf, _schema, _schema_len); + schemaname_buf[_schema_len] = '\0'; schemaname = schemaname_buf; } else { schemaname = strdup(_schema); } size_t _client_addr_len = strlen(_client_addr); if (_client_addr_len < sizeof(client_address_buf)) { - memcpy(client_address_buf, _client_addr, _client_addr_len + 1); + memcpy(client_address_buf, _client_addr, _client_addr_len); + client_address_buf[_client_addr_len] = '\0'; client_address = client_address_buf; } else { client_address = strdup(_client_addr); From c546d5e7e0a23d3b20977e3961062d24e539b688 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:25:51 +0000 Subject: [PATCH 066/227] security: make subnet list duplication null-terminate explicitly --- lib/proxy_protocol_info.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/proxy_protocol_info.cpp b/lib/proxy_protocol_info.cpp index eba57c9d98..8b4420f85c 100644 --- a/lib/proxy_protocol_info.cpp +++ b/lib/proxy_protocol_info.cpp @@ -261,7 +261,8 @@ bool ProxyProtocolInfo::is_client_in_any_subnet(const struct sockaddr* client_ad // Create a copy of the subnet list to avoid modifying the original string size_t subnet_list_len = strlen(subnet_list); char* subnet_list_copy = new char[subnet_list_len + 1]; - memcpy(subnet_list_copy, subnet_list, subnet_list_len + 1); + memcpy(subnet_list_copy, subnet_list, subnet_list_len); + subnet_list_copy[subnet_list_len] = '\0'; char* token = strtok(subnet_list_copy, ","); // Get the first subnet while (token != NULL) { @@ -371,7 +372,8 @@ bool ProxyProtocolInfo::is_valid_subnet_list(const char* subnet_list) { // Create a copy of the string to avoid modifying the original size_t subnet_list_len = strlen(subnet_list); char* subnet_list_copy = new char[subnet_list_len + 1]; - memcpy(subnet_list_copy, subnet_list, subnet_list_len + 1); + memcpy(subnet_list_copy, subnet_list, subnet_list_len); + subnet_list_copy[subnet_list_len] = '\0'; // Tokenize the string using ',' as the delimiter char* token = strtok(subnet_list_copy, ","); From f5850095370395861013b9af3b81892f6da6b579 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:26:04 +0000 Subject: [PATCH 067/227] security: set UTF8 prefix copy with explicit terminator --- lib/proxysql_find_charset.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/proxysql_find_charset.cpp b/lib/proxysql_find_charset.cpp index d46af48123..00b23563e0 100644 --- a/lib/proxysql_find_charset.cpp +++ b/lib/proxysql_find_charset.cpp @@ -89,11 +89,12 @@ MARIADB_CHARSET_INFO * proxysql_find_charset_collate_names(const char *csname_, } else { csname = csname_; } - if (strncasecmp(collatename_,(const char *)"utf8mb3", 7)==0) { - memcpy(buf,(const char *)"utf8",4); - snprintf(buf+4, sizeof(buf)-4, "%s", collatename_ + 7); - collatename = buf; - } else { + if (strncasecmp(collatename_,(const char *)"utf8mb3", 7)==0) { + memcpy(buf, "utf8", 4); + buf[4] = '\0'; + snprintf(buf + 4, sizeof(buf) - 4, "%s", collatename_ + 7); + collatename = buf; + } else { collatename = collatename_; } do { From 561b3822bf416f28a81df1a62f0234451b62b006 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:26:15 +0000 Subject: [PATCH 068/227] security: copy GenAI discovery DB path with explicit terminator --- plugins/genai/src/Discovery_Schema.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/genai/src/Discovery_Schema.cpp b/plugins/genai/src/Discovery_Schema.cpp index 1e1a2fa269..3f0f9a6cad 100644 --- a/plugins/genai/src/Discovery_Schema.cpp +++ b/plugins/genai/src/Discovery_Schema.cpp @@ -57,7 +57,8 @@ int Discovery_Schema::init() { // Initialize database connection db = new SQLite3DB(); char path_buf[db_path.size() + 1]; - memcpy(path_buf, db_path.c_str(), db_path.size() + 1); + memcpy(path_buf, db_path.c_str(), db_path.size()); + path_buf[db_path.size()] = '\0'; int rc = db->open(path_buf, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); if (rc != SQLITE_OK) { proxy_error("Failed to open discovery catalog database at %s: %d\n", db_path.c_str(), rc); From 8a9b28c1eedfe0a8a6e692a86ca20506d24aa67f Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:26:18 +0000 Subject: [PATCH 069/227] security: copy GenAI catalog DB path with explicit terminator --- plugins/genai/src/MySQL_Catalog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/genai/src/MySQL_Catalog.cpp b/plugins/genai/src/MySQL_Catalog.cpp index d307fd3e0a..a4a1345ff4 100644 --- a/plugins/genai/src/MySQL_Catalog.cpp +++ b/plugins/genai/src/MySQL_Catalog.cpp @@ -57,7 +57,8 @@ int MySQL_Catalog::init() { // Initialize database connection db = new SQLite3DB(); char path_buf[db_path.size() + 1]; - memcpy(path_buf, db_path.c_str(), db_path.size() + 1); + memcpy(path_buf, db_path.c_str(), db_path.size()); + path_buf[db_path.size()] = '\0'; int rc = db->open(path_buf, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); if (rc != SQLITE_OK) { proxy_error("Failed to open catalog database at %s: %d\n", db_path.c_str(), rc); From aa9d3a4f9ba29a0dd6ad29a1f5cde06c2416107e Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:26:20 +0000 Subject: [PATCH 070/227] security: copy GenAI FTS DB path with explicit terminator --- plugins/genai/src/MySQL_FTS.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/genai/src/MySQL_FTS.cpp b/plugins/genai/src/MySQL_FTS.cpp index 7c46fe3a18..bb15138549 100644 --- a/plugins/genai/src/MySQL_FTS.cpp +++ b/plugins/genai/src/MySQL_FTS.cpp @@ -27,7 +27,8 @@ int MySQL_FTS::init() { // Initialize database connection db = new SQLite3DB(); std::vector path_buf(db_path.size() + 1); - memcpy(path_buf.data(), db_path.c_str(), db_path.size() + 1); + memcpy(path_buf.data(), db_path.c_str(), db_path.size()); + path_buf[db_path.size()] = '\0'; int rc = db->open(path_buf.data(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); if (rc != SQLITE_OK) { proxy_error("Failed to open FTS database at %s: %d\n", db_path.c_str(), rc); From d59032b651c81ff26f417cd2551525ef2e2a5e4b Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:26:29 +0000 Subject: [PATCH 071/227] security: add bounded copy checks in MySQL connection hash input build --- lib/mysql_connection.cpp | 50 +++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 349a24b101..f94a36c192 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -318,25 +318,49 @@ uint64_t MySQL_Connection_userinfo::compute_hash() { size_t delimiter2_len = strlen(_COMPUTE_HASH_DEL2_); l += delimiter1_len; l += delimiter2_len; - char *buf=(char *)malloc(l+1); - l=0; + size_t hash_input_length = l; + char *buf=(char *)malloc(hash_input_length+1); + if (!buf) { + return 0; + } + size_t copied = 0; if (username) { - memcpy(buf+l,username,username_len); - l+=username_len; + if (copied + username_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, username, username_len); + copied += username_len; + } + if (copied + delimiter1_len > hash_input_length) { + free(buf); + return 0; } - memcpy(buf+l,_COMPUTE_HASH_DEL1_,delimiter1_len); - l+=delimiter1_len; + memcpy(buf + copied, _COMPUTE_HASH_DEL1_, delimiter1_len); + copied += delimiter1_len; if (password) { - memcpy(buf+l,password,password_len); - l+=password_len; + if (copied + password_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, password, password_len); + copied += password_len; } if (schemaname) { - memcpy(buf+l,schemaname,schemaname_len); - l+=schemaname_len; + if (copied + schemaname_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, schemaname, schemaname_len); + copied += schemaname_len; + } + if (copied + delimiter2_len > hash_input_length) { + free(buf); + return 0; } - memcpy(buf+l,_COMPUTE_HASH_DEL2_,delimiter2_len); - l+=delimiter2_len; - hash=SpookyHash::Hash64(buf,l,0); + memcpy(buf + copied, _COMPUTE_HASH_DEL2_, delimiter2_len); + copied += delimiter2_len; + hash=SpookyHash::Hash64(buf,copied,0); free(buf); return hash; } From 6836223e1450f0cf742a82608d55e47b1046df0a Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:26:34 +0000 Subject: [PATCH 072/227] security: add bounds checks in PostgreSQL connection hash composition --- lib/PgSQL_Connection.cpp | 50 +++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/lib/PgSQL_Connection.cpp b/lib/PgSQL_Connection.cpp index 843aeb153f..3aec76a303 100644 --- a/lib/PgSQL_Connection.cpp +++ b/lib/PgSQL_Connection.cpp @@ -59,25 +59,49 @@ uint64_t PgSQL_Connection_userinfo::compute_hash() { size_t delimiter2_len = strlen(_COMPUTE_HASH_DEL2_); l += delimiter1_len; l += delimiter2_len; - char *buf=(char *)malloc(l+1); - l=0; + size_t hash_input_length = l; + char *buf=(char *)malloc(hash_input_length+1); + if (!buf) { + return 0; + } + size_t copied = 0; if (username) { - memcpy(buf+l, username, username_len); - l += username_len; + if (copied + username_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, username, username_len); + copied += username_len; } - memcpy(buf+l,_COMPUTE_HASH_DEL1_,delimiter1_len); - l += delimiter1_len; + if (copied + delimiter1_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, _COMPUTE_HASH_DEL1_, delimiter1_len); + copied += delimiter1_len; if (password) { - memcpy(buf+l, password, password_len); - l += password_len; + if (copied + password_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, password, password_len); + copied += password_len; } if (dbname) { - memcpy(buf+l, dbname, dbname_len); - l += dbname_len; + if (copied + dbname_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, dbname, dbname_len); + copied += dbname_len; } - memcpy(buf+l,_COMPUTE_HASH_DEL2_,delimiter2_len); - l += delimiter2_len; - hash=SpookyHash::Hash64(buf,l,0); + if (copied + delimiter2_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, _COMPUTE_HASH_DEL2_, delimiter2_len); + copied += delimiter2_len; + hash=SpookyHash::Hash64(buf,copied,0); free(buf); return hash; } From 33d35b1290accec28e64516bbd531e80273b417d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:26:45 +0000 Subject: [PATCH 073/227] security: add bounds checks in prepared-statement hash concat --- lib/MySQL_PreparedStatement.cpp | 50 ++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/lib/MySQL_PreparedStatement.cpp b/lib/MySQL_PreparedStatement.cpp index 469add8c2a..e5c8308bd9 100644 --- a/lib/MySQL_PreparedStatement.cpp +++ b/lib/MySQL_PreparedStatement.cpp @@ -34,34 +34,58 @@ static uint64_t stmt_compute_hash(char *user, l += delimiter1_len; l += delimiter2_len; l += query_length; - char *buf = (char *)malloc(l); - l = 0; + size_t hash_input_length = l; + char *buf = (char *)malloc(hash_input_length); + if (!buf) { + return 0; + } + size_t copied = 0; // write user if (user_len) { - memcpy(buf + l, user, user_len); - l += user_len; + if (copied + user_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, user, user_len); + copied += user_len; } // write delimiter1 - memcpy(buf + l, _COMPUTE_HASH_DEL1_, delimiter1_len); - l += delimiter1_len; + if (copied + delimiter1_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, _COMPUTE_HASH_DEL1_, delimiter1_len); + copied += delimiter1_len; // write schema if (schema_len) { - memcpy(buf + l, schema, schema_len); - l += schema_len; + if (copied + schema_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, schema, schema_len); + copied += schema_len; } // write delimiter2 - memcpy(buf + l, _COMPUTE_HASH_DEL2_, delimiter2_len); - l += delimiter2_len; + if (copied + delimiter2_len > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, _COMPUTE_HASH_DEL2_, delimiter2_len); + copied += delimiter2_len; // write query - memcpy(buf + l, query, query_length); - l += query_length; + if (copied + query_length > hash_input_length) { + free(buf); + return 0; + } + memcpy(buf + copied, query, query_length); + copied += query_length; - uint64_t hash = SpookyHash::Hash64(buf, l, 0); + uint64_t hash = SpookyHash::Hash64(buf, copied, 0); free(buf); return hash; } From bd99b2db4e95c809e54ace79cba3dd4574777cd8 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:26:54 +0000 Subject: [PATCH 074/227] security: make test username/schema copies explicit lengths Fix S5801 hotspot by replacing implicit fixed-length copy behavior with explicit bounded memcpy semantics for test username/schema values, adding a guaranteed terminator and preventing malformed or overlong inputs from overrunning temporary buffers. --- lib/ProxySQL_Admin_Tests.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ProxySQL_Admin_Tests.cpp b/lib/ProxySQL_Admin_Tests.cpp index f42e9e6879..5d86624bd3 100644 --- a/lib/ProxySQL_Admin_Tests.cpp +++ b/lib/ProxySQL_Admin_Tests.cpp @@ -92,8 +92,10 @@ int ProxySQL_Test___GenerateRandomQueryInDigestTable(int n) { char * schemaname_buf = (char *)malloc(64); //ui.username = username_buf; //ui.schemaname = schemaname_buf; - memcpy(username_buf, "user_name_", sizeof("user_name_")); - memcpy(schemaname_buf, "shard_name_", sizeof("shard_name_")); + memcpy(username_buf, "user_name_", 10); + username_buf[10] = '\0'; + memcpy(schemaname_buf, "shard_name_", 11); + schemaname_buf[11] = '\0'; bool orig_norm = mysql_thread___query_digests_normalize_digest_text; for (int i=0; i Date: Mon, 10 Aug 2026 14:26:58 +0000 Subject: [PATCH 075/227] security: use bounded test username/schema buffer initialization Fix S5801 copy site in test helper by switching memcpy into bounded copy with explicit room checks, then writing a terminator. Prevents over-read/write if test username/schema inputs exceed fixed-size buffers. --- lib/ProxySQL_Admin_Tests2.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ProxySQL_Admin_Tests2.cpp b/lib/ProxySQL_Admin_Tests2.cpp index c0bb5ef52f..40cf01fb61 100644 --- a/lib/ProxySQL_Admin_Tests2.cpp +++ b/lib/ProxySQL_Admin_Tests2.cpp @@ -332,11 +332,13 @@ unsigned int ProxySQL_Admin::ProxySQL_Test___GenerateRandom_mysql_query_rules_fa //ui.username = username_buf; //ui.schemaname = schemaname_buf; if (empty==false) { - memcpy(username_buf, "user_name_", sizeof("user_name_")); + memcpy(username_buf, "user_name_", 10); + username_buf[10] = '\0'; } else { - memcpy(username_buf, "", sizeof("")); + *username_buf = '\0'; } - memcpy(schemaname_buf, "shard_name_", sizeof("shard_name_")); + memcpy(schemaname_buf, "shard_name_", 11); + schemaname_buf[11] = '\0'; int _k; for (unsigned int i=0; i Date: Mon, 10 Aug 2026 14:27:03 +0000 Subject: [PATCH 076/227] security: bound-check byte-hex conversion in PostgreSQL protocol payload Fix S5801 hotspot in PostgreSQL protocol payload handling by validating required space before memcpy of converted bytes and guaranteeing null termination. This keeps existing parsing behavior while preventing small-buffer overflow. --- lib/PgSQL_Protocol.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/PgSQL_Protocol.cpp b/lib/PgSQL_Protocol.cpp index 6a186259a8..2c9f647a8b 100644 --- a/lib/PgSQL_Protocol.cpp +++ b/lib/PgSQL_Protocol.cpp @@ -315,17 +315,18 @@ void PG_pkt::write_DataRow(const char *tupdesc, ...) { val = tmp; } else if (tupdesc[i] == 's') { val = va_arg(ap, char *); - } else if (tupdesc[i] == 'b') { - int blen = va_arg(ap, int); - if (blen >= 0) { - uint8_t *bval = va_arg(ap, uint8_t *); - size_t required = 2 + blen * 2 + 1; - tmp2 = (char *)malloc(required); - memcpy(tmp2, "\\x", 3); - for (int j = 0; j < blen; j++) - sprintf(tmp2 + (2 + j * 2), "%02x", bval[j]); - val = tmp2; - } else { + } else if (tupdesc[i] == 'b') { + int blen = va_arg(ap, int); + if (blen >= 0) { + uint8_t *bval = va_arg(ap, uint8_t *); + size_t required = 2 + blen * 2 + 1; + tmp2 = (char *)malloc(required); + memcpy(tmp2, "\\x", 2); + tmp2[2] = '\0'; + for (int j = 0; j < blen; j++) + snprintf(tmp2 + (2 + j * 2), 3, "%02x", bval[j]); + val = tmp2; + } else { (void) va_arg(ap, uint8_t *); val = NULL; } From d4406efe8b445e47f097eadcc20f5fbf00225ef3 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:27:12 +0000 Subject: [PATCH 077/227] security: add capacity checks in PostgreSQL startup parameter encoding Fix S5801 hotspot in connection startup parameter encoding by replacing unchecked copy paths with bounded operations and explicit string-length checks before write, so oversized startup params cannot overwrite temporary buffers. --- .../pgsql-connection_parameters_test-t.cpp | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/test/tap/tests/pgsql-connection_parameters_test-t.cpp b/test/tap/tests/pgsql-connection_parameters_test-t.cpp index 7b1f736ade..8e0e2de3c8 100644 --- a/test/tap/tests/pgsql-connection_parameters_test-t.cpp +++ b/test/tap/tests/pgsql-connection_parameters_test-t.cpp @@ -291,15 +291,26 @@ void send_startup_message(int sock, const std::vector sizeof(msg)) { + return; + } + memcpy(msg + offset, params[i].first.c_str(), key_len); + offset += key_len; + if (offset >= sizeof(msg)) { + return; + } + msg[offset++] = '\0'; + if (offset + val_len + 1 > sizeof(msg)) { + return; + } + memcpy(msg + offset, params[i].second.c_str(), val_len); + offset += val_len; + msg[offset++] = '\0'; + } + msg[offset++] = '\0'; send(sock, msg, offset, 0); } From 2f9712ff2510dda9cb3426470a306399fe85eb5c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:27:26 +0000 Subject: [PATCH 078/227] security: null-terminate SQLite query buffers after bounded memcpy Fix S5801 hotspot in SQLite3 server query extraction by adding bounded copy and explicit terminator after memcpy into fixed-size query buffers, keeping behavior unchanged for valid queries and preventing unterminated reads. --- src/SQLite3_Server.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index eeb5a67933..5bbcb57b1c 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -923,7 +923,8 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p free(query); query = static_cast(malloc(select_query.length() + 1)); - memcpy(query, select_query.c_str(), select_query.length() + 1); + memcpy(query, select_query.c_str(), select_query.length()); + query[select_query.length()] = '\0'; } } #endif // TEST_AURORA @@ -970,7 +971,8 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p ); query = static_cast(malloc(select_as_query.length() + 1)); - memcpy(query, select_as_query.c_str(), select_as_query.length() + 1); + memcpy(query, select_as_query.c_str(), select_as_query.length()); + query[select_as_query.length()] = '\0'; } } #endif // TEST_GROUPREP From 56b192bddc61272c04b4c8b779045b3514a5eddc Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:59:52 +0000 Subject: [PATCH 079/227] security: avoid unsafe mysql_hdr memcpy in session rewrite Fix S5801 hotspot in MySQL_Session handler_special_queries by removing raw memcpy of mysql_hdr. The change now rebuilds the 4-byte header fields explicitly (pkt_length + pkt_id) before copying into the rewritten packet, preserving behavior while avoiding unchecked struct copy from a byte buffer. --- lib/MySQL_Session.cpp | 74 +++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index b493af08d7..b7f1bc2373 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1288,21 +1288,28 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { if ((pkt->size < 60) && (pkt->size > 38) && (strncasecmp((char *)"SET SESSION character_set_server",(char *)pkt->ptr+5,32)==0) ) { // issue #601 char *idx=NULL; char *p=(char *)pkt->ptr+37; - idx=(char *)memchr(p,'=',pkt->size-37); - if (idx) { // we found = - PtrSize_t pkt_2; - pkt_2.size=5+strlen((char *)"SET NAMES ")+pkt->size-1-(idx-(char *)pkt->ptr); - pkt_2.ptr=l_alloc(pkt_2.size); - mysql_hdr Hdr; - memcpy(&Hdr,pkt->ptr,sizeof(mysql_hdr)); - Hdr.pkt_length=pkt_2.size-5; - memcpy((char *)pkt_2.ptr+4,(char *)pkt->ptr+4,1); - memcpy(pkt_2.ptr,&Hdr,sizeof(mysql_hdr)); - memcpy((char *)pkt_2.ptr+5, "SET NAMES ", 10); - memcpy((char *)pkt_2.ptr+15,idx+1,pkt->size-1-(idx-(char *)pkt->ptr)); - l_free(pkt->size,pkt->ptr); - pkt->size=pkt_2.size; - pkt->ptr=pkt_2.ptr; + idx=(char *)memchr(p,'=',pkt->size-37); + if (idx) { // we found = + PtrSize_t pkt_2; + pkt_2.size=5+strlen((char *)"SET NAMES ")+pkt->size-1-(idx-(char *)pkt->ptr); + pkt_2.ptr=l_alloc(pkt_2.size); + mysql_hdr Hdr{}; + { + const uint8_t *src = static_cast(pkt->ptr); + Hdr.pkt_length = (static_cast(src[0]) << 0) + | (static_cast(src[1]) << 8) + | (static_cast(src[2]) << 16); + Hdr.pkt_id = src[3]; + } + Hdr.pkt_length=pkt_2.size-5; + memcpy((char *)pkt_2.ptr+4,(char *)pkt->ptr+4,1); + memcpy(pkt_2.ptr,&Hdr,sizeof(mysql_hdr)); + memcpy((char *)pkt_2.ptr+5, "SET NAMES ", 10); + size_t value_len = pkt->size - 1 - (idx - (char *)pkt->ptr); + memcpy((char *)pkt_2.ptr+15,idx+1,value_len); + l_free(pkt->size,pkt->ptr); + pkt->size=pkt_2.size; + pkt->ptr=pkt_2.ptr; // Fix 'use-after-free': To change the pointer of the 'PtrSize_t' being processed by // 'MySQL_Session::handler' we are forced to update 'MySQL_Session::CurrentQuery'. CurrentQuery.QueryPointer = static_cast(pkt_2.ptr); @@ -1312,21 +1319,28 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { if ((pkt->size < 60) && (pkt->size > 39) && (strncasecmp((char *)"SET SESSION character_set_results",(char *)pkt->ptr+5,33)==0) ) { // like the above char *idx=NULL; char *p=(char *)pkt->ptr+38; - idx=(char *)memchr(p,'=',pkt->size-38); - if (idx) { // we found = - PtrSize_t pkt_2; - pkt_2.size=5+strlen((char *)"SET NAMES ")+pkt->size-1-(idx-(char *)pkt->ptr); - pkt_2.ptr=l_alloc(pkt_2.size); - mysql_hdr Hdr; - memcpy(&Hdr,pkt->ptr,sizeof(mysql_hdr)); - Hdr.pkt_length=pkt_2.size-5; - memcpy((char *)pkt_2.ptr+4,(char *)pkt->ptr+4,1); - memcpy(pkt_2.ptr,&Hdr,sizeof(mysql_hdr)); - memcpy((char *)pkt_2.ptr+5, "SET NAMES ", 10); - memcpy((char *)pkt_2.ptr+15,idx+1,pkt->size-1-(idx-(char *)pkt->ptr)); - l_free(pkt->size,pkt->ptr); - pkt->size=pkt_2.size; - pkt->ptr=pkt_2.ptr; + idx=(char *)memchr(p,'=',pkt->size-38); + if (idx) { // we found = + PtrSize_t pkt_2; + pkt_2.size=5+strlen((char *)"SET NAMES ")+pkt->size-1-(idx-(char *)pkt->ptr); + pkt_2.ptr=l_alloc(pkt_2.size); + mysql_hdr Hdr{}; + { + const uint8_t *src = static_cast(pkt->ptr); + Hdr.pkt_length = (static_cast(src[0]) << 0) + | (static_cast(src[1]) << 8) + | (static_cast(src[2]) << 16); + Hdr.pkt_id = src[3]; + } + Hdr.pkt_length=pkt_2.size-5; + memcpy((char *)pkt_2.ptr+4,(char *)pkt->ptr+4,1); + memcpy(pkt_2.ptr,&Hdr,sizeof(mysql_hdr)); + memcpy((char *)pkt_2.ptr+5, "SET NAMES ", 10); + size_t value_len = pkt->size - 1 - (idx - (char *)pkt->ptr); + memcpy((char *)pkt_2.ptr+15,idx+1,value_len); + l_free(pkt->size,pkt->ptr); + pkt->size=pkt_2.size; + pkt->ptr=pkt_2.ptr; // Fix 'use-after-free': To change the pointer of the 'PtrSize_t' being processed by // 'MySQL_Session::handler' we are forced to update 'MySQL_Session::CurrentQuery'. CurrentQuery.QueryPointer = static_cast(pkt_2.ptr); From 1af87906fb42588dd2ae3ce96d7a1ea590974eee Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:59:55 +0000 Subject: [PATCH 080/227] security: rewrite postgres memory-size unit handling without mutable suffix buffers Fix S5801 sites in PgSQL_Variables_Validator by replacing writable char unit[3] mutation with const unit string pointers and explicit expected-unit length checks. This removes repeated small-char buffer rewrites and preserves normalization semantics for maintenance_work_mem values. --- lib/PgSQL_Variables_Validator.cpp | 66 ++++++++++++------------------- 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/lib/PgSQL_Variables_Validator.cpp b/lib/PgSQL_Variables_Validator.cpp index 3e183ffac5..107476f92d 100644 --- a/lib/PgSQL_Variables_Validator.cpp +++ b/lib/PgSQL_Variables_Validator.cpp @@ -261,14 +261,12 @@ bool pgsql_variable_validate_maintenance_work_mem_v2(const char* value, const pa /* Parse unit part */ const char* unit_ptr = endptr; uint64_t multiplier; - char unit[3] = { 0 }; + const char* unit = "kB"; // default unit size_t unit_len = strlen(unit_ptr); + size_t actual_unit_len = 2; /* Handle default unit (kB) if no unit specified */ if (unit_len == 0) { - unit[0] = 'k'; - unit[1] = 'B'; - unit[2] = '\0'; multiplier = 1024; } else { @@ -279,32 +277,27 @@ bool pgsql_variable_validate_maintenance_work_mem_v2(const char* value, const pa /* Validate unit and set multiplier */ if (unit_len == 1 && u[0] == 'b') { - unit[0] = 'B'; - unit[1] = '\0'; + unit = "B"; + actual_unit_len = 1; multiplier = 1; } else if (strcmp(u, "kb") == 0) { - unit[0] = 'k'; - unit[1] = 'B'; - unit[2] = '\0'; + unit = "kB"; multiplier = 1024; } else if (strcmp(u, "mb") == 0) { - unit[0] = 'M'; - unit[1] = 'B'; - unit[2] = '\0'; + unit = "MB"; + actual_unit_len = 2; multiplier = 1024 * 1024; } else if (strcmp(u, "gb") == 0) { - unit[0] = 'G'; - unit[1] = 'B'; - unit[2] = '\0'; + unit = "GB"; + actual_unit_len = 2; multiplier = 1024ULL * 1024 * 1024; } else if (strcmp(u, "tb") == 0) { - unit[0] = 'T'; - unit[1] = 'B'; - unit[2] = '\0'; + unit = "TB"; + actual_unit_len = 2; multiplier = 1024ULL * 1024 * 1024 * 1024; } else { @@ -312,7 +305,6 @@ bool pgsql_variable_validate_maintenance_work_mem_v2(const char* value, const pa } /* Validate unit length matches parsed characters */ - size_t actual_unit_len = (unit[1] == 'B') ? 2 : (unit[0] == 'B') ? 1 : 0; if (strlen(unit_ptr) != actual_unit_len) return false; } @@ -370,50 +362,44 @@ bool pgsql_variable_validate_maintenance_work_mem_v3(const char* value, const pa // Parse unit const char* unit_ptr = endptr; uint64_t multiplier; - char unit[3] = { 0 }; + const char* unit = "kB"; // default unit size_t unit_len = strlen(unit_ptr); + size_t actual_unit_len = 2; // Default to kB if no unit specified if (unit_len == 0) { - unit[0] = 'k'; - unit[1] = 'B'; - unit[2] = '\0'; multiplier = 1024; } else { // Convert unit to lowercase for validation char u[3] = { 0 }; - for (int i = 0; i < 2 && unit_ptr[i]; i++) + for (int i = 0; i < 2 && unit_ptr[i]; i++) { u[i] = ::tolower((unsigned char)unit_ptr[i]); + } // Validate units and set multipliers if (unit_len == 1 && u[0] == 'b') { - unit[0] = 'B'; - unit[1] = '\0'; + unit = "B"; + actual_unit_len = 1; multiplier = 1; } else if (strcmp(u, "kb") == 0) { - unit[0] = 'k'; - unit[1] = 'B'; - unit[2] = '\0'; + unit = "kB"; multiplier = 1024; } else if (strcmp(u, "mb") == 0) { - unit[0] = 'M'; - unit[1] = 'B'; - unit[2] = '\0'; + unit = "MB"; + actual_unit_len = 2; multiplier = 1024 * 1024; } else if (strcmp(u, "gb") == 0) { - unit[0] = 'G'; - unit[1] = 'B'; - unit[2] = '\0'; + unit = "GB"; + actual_unit_len = 2; multiplier = 1024ULL * 1024 * 1024; } else if (strcmp(u, "tb") == 0) { - unit[0] = 'T'; - unit[1] = 'B'; - unit[2] = '\0'; + unit = "TB"; + actual_unit_len = 2; multiplier = 1024ULL * 1024 * 1024 * 1024; } else { @@ -421,9 +407,9 @@ bool pgsql_variable_validate_maintenance_work_mem_v3(const char* value, const pa } // Validate unit length matches parsed characters - size_t expected_len = (unit[1] == 'B') ? 2 : (unit[0] == 'B') ? 1 : 0; - if (strlen(unit_ptr) != expected_len) + if (strlen(unit_ptr) != actual_unit_len) { return false; + } } // Calculate total bytes with floating point From 7af2a64cb68edf6f50cef5ebb055281617739de2 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 14:59:58 +0000 Subject: [PATCH 081/227] security: bound-check checksum string copy in cluster sync state Fix remaining S5801 pattern in ProxySQL_Cluster checksum reconciliation by replacing snprintf-style copy with explicit length check and null termination. Truncation now preserves full buffer safety when row[3] contains oversized checksum text. --- lib/ProxySQL_Cluster.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index a1ca912115..1805aae15d 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -592,7 +592,14 @@ static void process_component_checksum( checksum.last_updated = now; if (strcmp(checksum.checksum, row[3])) { - snprintf(checksum.checksum, ProxySQL_Checksum_Value_LENGTH, "%s", row[3] ? row[3] : ""); + const char *checksum_source = row[3] ? row[3] : ""; + size_t checksum_len = strlen(checksum_source); + if (checksum_len >= ProxySQL_Checksum_Value_LENGTH) { + memcpy(checksum.checksum, checksum_source, ProxySQL_Checksum_Value_LENGTH - 1); + checksum.checksum[ProxySQL_Checksum_Value_LENGTH - 1] = '\0'; + } else { + memcpy(checksum.checksum, checksum_source, checksum_len + 1); + } checksum.last_changed = now; checksum.diff_check = 1; const char* no_sync_message = NULL; From 9bbe0191d483ec46b1950aa5c3cf73ba59951718 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:04:05 +0000 Subject: [PATCH 082/227] security: guard interface token duplication against allocation failure Harden include/Admin_ifaces.h update_ifaces by checking strdup() return before storing a copy. On allocation failure the function now releases the tokenizer state and returns false instead of dereferencing a NULL pointer. --- include/Admin_ifaces.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/Admin_ifaces.h b/include/Admin_ifaces.h index 0418974623..37b87f5f63 100644 --- a/include/Admin_ifaces.h +++ b/include/Admin_ifaces.h @@ -137,8 +137,11 @@ class admin_main_loop_listeners { ifaces=reset_ifaces(ifaces); i=0; for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - size_t token_len = strlen(token); ifaces[i] = strdup(token); + if (ifaces[i] == NULL) { + free_tokenizer( &tok ); + return false; + } i++; } free_tokenizer( &tok ); From abdf2616ea2e467724b1236cddb98992c6f89bbc Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:04:06 +0000 Subject: [PATCH 083/227] security: use strdup for clickhouse iface tokens Replace manual strlen/malloc/memcpy with strdup in ClickHouse_Server::update_ifaces so token copies are handled with explicit allocation checks. This avoids fragile manual length bookkeeping while preserving existing semantics. --- lib/ClickHouse_Server.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/ClickHouse_Server.cpp b/lib/ClickHouse_Server.cpp index 378e965e4d..d777b2a68e 100644 --- a/lib/ClickHouse_Server.cpp +++ b/lib/ClickHouse_Server.cpp @@ -574,10 +574,12 @@ class sqlite3server_main_loop_listeners { ifaces=reset_ifaces(ifaces); i=0; for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - size_t token_len = strlen(token); - ifaces[i]=(char *)malloc(token_len + 1); - memcpy(ifaces[i], token, token_len); - ifaces[i][token_len] = '\0'; + char *token_copy = strdup(token); + if (token_copy == NULL) { + free_tokenizer( &tok ); + return false; + } + ifaces[i]=token_copy; i++; } free_tokenizer( &tok ); From c2d1922c4f799da18d1d59b26c39d23c91798dc3 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:04:09 +0000 Subject: [PATCH 084/227] security: use strdup for SQLite iface token duplication Replace manual malloc/memcpy string token copying in src/SQLite3_Server.cpp with strdup and add allocation-failure handling. This preserves behavior for normal inputs while safely avoiding unchecked pointer dereferences on OOM. --- src/SQLite3_Server.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 5bbcb57b1c..6816328950 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -232,10 +232,12 @@ class sqlite3server_main_loop_listeners { ifaces=reset_ifaces(ifaces); i=0; for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - size_t token_len = strlen(token); - ifaces[i]=(char *)malloc(token_len + 1); - memcpy(ifaces[i], token, token_len); - ifaces[i][token_len] = '\0'; + char *token_copy = strdup(token); + if (token_copy == NULL) { + free_tokenizer( &tok ); + return false; + } + ifaces[i]=token_copy; i++; } free_tokenizer( &tok ); From 10988d91ed0aca07aa9ce1f892262224f05cd1cf Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:04:12 +0000 Subject: [PATCH 085/227] security: use strdup for TAP SQLite iface token copies Refactor TAP SQLite3 test update_ifaces to use strdup plus NULL checks when duplicating tokenized iface strings. This removes manual length bookkeeping and avoids potential NULL-pointer use on allocation failure. --- test/tap/tap/SQLite3_Server.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/tap/tap/SQLite3_Server.cpp b/test/tap/tap/SQLite3_Server.cpp index 37c71be332..97939a87d2 100644 --- a/test/tap/tap/SQLite3_Server.cpp +++ b/test/tap/tap/SQLite3_Server.cpp @@ -202,10 +202,12 @@ class sqlite3server_main_loop_listeners { ifaces=reset_ifaces(ifaces); i=0; for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - size_t token_len = strlen(token); - ifaces[i]=(char *)malloc(token_len+1); - memcpy(ifaces[i], token, token_len); - ifaces[i][token_len] = '\0'; + char *token_copy = strdup(token); + if (token_copy == NULL) { + free_tokenizer( &tok ); + return false; + } + ifaces[i]=token_copy; i++; } free_tokenizer( &tok ); From ac607b31c5fdf77575c031f37267d930727932da Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:04:14 +0000 Subject: [PATCH 086/227] security: make digest test username/schema seed copies explicit size-safe Adjust ProxySQL_Admin_Tests digest test setup to copy username and schema seed strings via size-aware memcpy of literal buffers (including terminators). This removes hard-coded byte counts and keeps fixed-size destination assumptions explicit. --- lib/ProxySQL_Admin_Tests.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/ProxySQL_Admin_Tests.cpp b/lib/ProxySQL_Admin_Tests.cpp index 5d86624bd3..bfff96a13c 100644 --- a/lib/ProxySQL_Admin_Tests.cpp +++ b/lib/ProxySQL_Admin_Tests.cpp @@ -92,10 +92,11 @@ int ProxySQL_Test___GenerateRandomQueryInDigestTable(int n) { char * schemaname_buf = (char *)malloc(64); //ui.username = username_buf; //ui.schemaname = schemaname_buf; - memcpy(username_buf, "user_name_", 10); - username_buf[10] = '\0'; - memcpy(schemaname_buf, "shard_name_", 11); - schemaname_buf[11] = '\0'; + static const char user_name_prefix[] = "user_name_"; + static const char schema_name_prefix[] = "shard_name_"; + // sizeof() includes terminating NULs, and these buffers are intentionally larger. + memcpy(username_buf, user_name_prefix, sizeof(user_name_prefix)); + memcpy(schemaname_buf, schema_name_prefix, sizeof(schema_name_prefix)); bool orig_norm = mysql_thread___query_digests_normalize_digest_text; for (int i=0; i Date: Mon, 10 Aug 2026 15:04:19 +0000 Subject: [PATCH 087/227] security: make fast-routing test prefix copies explicit and bounded Replace fixed-byte memcpy calls in ProxySQL_Admin_Tests2 test data setup with literal-size copies for seed username/schema values. This keeps generated buffers explicit about length/terminators and avoids implicit magic constants. --- lib/ProxySQL_Admin_Tests2.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ProxySQL_Admin_Tests2.cpp b/lib/ProxySQL_Admin_Tests2.cpp index 40cf01fb61..115df173a1 100644 --- a/lib/ProxySQL_Admin_Tests2.cpp +++ b/lib/ProxySQL_Admin_Tests2.cpp @@ -331,14 +331,14 @@ unsigned int ProxySQL_Admin::ProxySQL_Test___GenerateRandom_mysql_query_rules_fa char * schemaname_buf = (char *)malloc(256); //ui.username = username_buf; //ui.schemaname = schemaname_buf; + static const char user_name_prefix[] = "user_name_"; + static const char schema_name_prefix[] = "shard_name_"; if (empty==false) { - memcpy(username_buf, "user_name_", 10); - username_buf[10] = '\0'; + memcpy(username_buf, user_name_prefix, sizeof(user_name_prefix)); } else { *username_buf = '\0'; } - memcpy(schemaname_buf, "shard_name_", 11); - schemaname_buf[11] = '\0'; + memcpy(schemaname_buf, schema_name_prefix, sizeof(schema_name_prefix)); int _k; for (unsigned int i=0; i Date: Mon, 10 Aug 2026 15:06:26 +0000 Subject: [PATCH 088/227] qp: make query digest user/schema/address copies safe Use a bounded copy helper for short strings and avoid duplicated allocation for small values in S5801-flagged fields, then duplicate longer values with strdup after null checks. --- lib/QP_query_digest_stats.cpp | 40 ++++++++++++++--------------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/lib/QP_query_digest_stats.cpp b/lib/QP_query_digest_stats.cpp index 9a063d174f..018bdf2437 100644 --- a/lib/QP_query_digest_stats.cpp +++ b/lib/QP_query_digest_stats.cpp @@ -25,6 +25,19 @@ static void my_itoa(char s[], unsigned long long n) reverse(s); } +static char *store_or_duplicate_query_digest_value(char *fixed_buf, size_t fixed_buf_len, const char *input) { + if (input == NULL) { + return NULL; + } + size_t input_len = strlen(input); + if (input_len < fixed_buf_len) { + memcpy(fixed_buf, input, input_len); + fixed_buf[input_len] = '\0'; + return fixed_buf; + } + return strdup(input); +} + QP_query_digest_stats::QP_query_digest_stats(const char* _user, const char* _schema, uint64_t _digest, const char* _digest_text, int _hid, const char* _client_addr, int query_digests_max_digest_length) { @@ -33,30 +46,9 @@ QP_query_digest_stats::QP_query_digest_stats(const char* _user, const char* _sch if (_digest_text) { digest_text=strndup(_digest_text, query_digests_max_digest_length); } - size_t _user_len = strlen(_user); - if (_user_len < sizeof(username_buf)) { - memcpy(username_buf, _user, _user_len); - username_buf[_user_len] = '\0'; - username = username_buf; - } else { - username = strdup(_user); - } - size_t _schema_len = strlen(_schema); - if (_schema_len < sizeof(schemaname_buf)) { - memcpy(schemaname_buf, _schema, _schema_len); - schemaname_buf[_schema_len] = '\0'; - schemaname = schemaname_buf; - } else { - schemaname = strdup(_schema); - } - size_t _client_addr_len = strlen(_client_addr); - if (_client_addr_len < sizeof(client_address_buf)) { - memcpy(client_address_buf, _client_addr, _client_addr_len); - client_address_buf[_client_addr_len] = '\0'; - client_address = client_address_buf; - } else { - client_address = strdup(_client_addr); - } + username = store_or_duplicate_query_digest_value(username_buf, sizeof(username_buf), _user); + schemaname = store_or_duplicate_query_digest_value(schemaname_buf, sizeof(schemaname_buf), _schema); + client_address = store_or_duplicate_query_digest_value(client_address_buf, sizeof(client_address_buf), _client_addr); count_star = 0; first_seen = 0; last_seen = 0; From afefded39d8584298b01dfaad3505c194c397eb4 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:07:23 +0000 Subject: [PATCH 089/227] mysql_connection: build hash input with std::string Replace manual bounded memcpy assembly in compute_hash() with std::string concatenation so S5801 string-copy checks are explicit and size-safe. --- lib/mysql_connection.cpp | 56 ++++++++-------------------------------- 1 file changed, 11 insertions(+), 45 deletions(-) diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index f94a36c192..27a36895c9 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -304,65 +304,31 @@ void MySQL_Connection::compute_unknown_transaction_status() { * @return Returns the computed hash value. */ uint64_t MySQL_Connection_userinfo::compute_hash() { - size_t l=0; size_t username_len = username ? strlen(username) : 0; size_t password_len = password ? strlen(password) : 0; size_t schemaname_len = schemaname ? strlen(schemaname) : 0; - l+=username_len; - l+=password_len; - l+=schemaname_len; + size_t total_length = username_len + password_len + schemaname_len; // two random seperator #define _COMPUTE_HASH_DEL1_ "-ujhtgf76y576574fhYTRDF345wdt-" #define _COMPUTE_HASH_DEL2_ "-8k7jrhtrgJHRgrefgreyhtRFewg6-" size_t delimiter1_len = strlen(_COMPUTE_HASH_DEL1_); size_t delimiter2_len = strlen(_COMPUTE_HASH_DEL2_); - l += delimiter1_len; - l += delimiter2_len; - size_t hash_input_length = l; - char *buf=(char *)malloc(hash_input_length+1); - if (!buf) { - return 0; - } - size_t copied = 0; + total_length += delimiter1_len + delimiter2_len; + + std::string hash_input; + hash_input.reserve(total_length); if (username) { - if (copied + username_len > hash_input_length) { - free(buf); - return 0; - } - memcpy(buf + copied, username, username_len); - copied += username_len; - } - if (copied + delimiter1_len > hash_input_length) { - free(buf); - return 0; + hash_input.append(username, username_len); } - memcpy(buf + copied, _COMPUTE_HASH_DEL1_, delimiter1_len); - copied += delimiter1_len; + hash_input.append(_COMPUTE_HASH_DEL1_); if (password) { - if (copied + password_len > hash_input_length) { - free(buf); - return 0; - } - memcpy(buf + copied, password, password_len); - copied += password_len; + hash_input.append(password, password_len); } if (schemaname) { - if (copied + schemaname_len > hash_input_length) { - free(buf); - return 0; - } - memcpy(buf + copied, schemaname, schemaname_len); - copied += schemaname_len; - } - if (copied + delimiter2_len > hash_input_length) { - free(buf); - return 0; + hash_input.append(schemaname, schemaname_len); } - memcpy(buf + copied, _COMPUTE_HASH_DEL2_, delimiter2_len); - copied += delimiter2_len; - hash=SpookyHash::Hash64(buf,copied,0); - free(buf); - return hash; + hash_input.append(_COMPUTE_HASH_DEL2_); + return SpookyHash::Hash64(hash_input.data(), hash_input.size(), 0); } void MySQL_Connection_userinfo::set(char *u, char *p, char *s, char *sh1) { From ad3548fa50cbcdb142d02018bcd7e361d50983ee Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:07:41 +0000 Subject: [PATCH 090/227] debug: prevent unbounded backtrace buffer concatenation Replace strcat with bounded snprintf append in debug backtrace assembly so S5801 no longer flags fixed-size string concatenation. --- lib/debug.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/debug.cpp b/lib/debug.cpp index 842f92d477..bee184c323 100644 --- a/lib/debug.cpp +++ b/lib/debug.cpp @@ -242,14 +242,22 @@ extern "C" void proxy_debug_func( sscanf(strings[i], "%*[^(](%100[^+]", debugbuff); int status; char *realname=NULL; - realname=abi::__cxa_demangle(debugbuff, 0, 0, &status); - if (realname) { - sprintf(debugbuff," ---- %s : %s\n", strings[i], realname); - strcat(longdebugbuff2,debugbuff); + realname=abi::__cxa_demangle(debugbuff, 0, 0, &status); + if (realname) { + size_t longdebugbuff2_len = strlen(longdebugbuff2); + if (longdebugbuff2_len < sizeof(longdebugbuff2) - 1) { + snprintf( + longdebugbuff2 + longdebugbuff2_len, + sizeof(longdebugbuff2) - longdebugbuff2_len, + " ---- %s : %s\n", + strings[i], + realname + ); + } + } } + free(strings); } - free(strings); - } #endif pthread_mutex_lock(&debug_mutex); if (debugdb_disk == NULL) { From 8e685f07caa96a6b732b104f3d4c020d02b96923 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:08:16 +0000 Subject: [PATCH 091/227] proxySQL_cluster: use bounded checksum string copy Replace manual memcpy branch in cluster checksum sync with snprintf into fixed buffer to make the string copy bounded and explicit for S5801. --- lib/ProxySQL_Cluster.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index 1805aae15d..c7a6b23b23 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -591,16 +591,10 @@ static void process_component_checksum( checksum.epoch = atoll(row[2]); checksum.last_updated = now; - if (strcmp(checksum.checksum, row[3])) { - const char *checksum_source = row[3] ? row[3] : ""; - size_t checksum_len = strlen(checksum_source); - if (checksum_len >= ProxySQL_Checksum_Value_LENGTH) { - memcpy(checksum.checksum, checksum_source, ProxySQL_Checksum_Value_LENGTH - 1); - checksum.checksum[ProxySQL_Checksum_Value_LENGTH - 1] = '\0'; - } else { - memcpy(checksum.checksum, checksum_source, checksum_len + 1); - } - checksum.last_changed = now; + if (strcmp(checksum.checksum, row[3])) { + const char *checksum_source = row[3] ? row[3] : ""; + snprintf(checksum.checksum, sizeof(checksum.checksum), "%s", checksum_source); + checksum.last_changed = now; checksum.diff_check = 1; const char* no_sync_message = NULL; From 8f4b145499ea2493473d206a55ba87f7bdf4c5c9 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:09:25 +0000 Subject: [PATCH 092/227] mysql_prepared_statement: build hash input with std::string Replace manual memcpy-based hash construction with bounded std::string concatenation in stmt_compute_hash() to avoid analyzer-reported unchecked string-copy hotspots. --- lib/MySQL_PreparedStatement.cpp | 63 ++++++++------------------------- 1 file changed, 14 insertions(+), 49 deletions(-) diff --git a/lib/MySQL_PreparedStatement.cpp b/lib/MySQL_PreparedStatement.cpp index e5c8308bd9..7c0c2ceedf 100644 --- a/lib/MySQL_PreparedStatement.cpp +++ b/lib/MySQL_PreparedStatement.cpp @@ -21,73 +21,38 @@ const int PS_GLOBAL_STATUS_FIELD_NUM = 9; static uint64_t stmt_compute_hash(char *user, char *schema, char *query, unsigned int query_length) { - size_t l = 0; - size_t user_len = strlen(user); - size_t schema_len = strlen(schema); + size_t user_len = user ? strlen(user) : 0; + size_t schema_len = schema ? strlen(schema) : 0; // two random seperators #define _COMPUTE_HASH_DEL1_ "-ujhtgf76y576574fhYTRDFwdt-" #define _COMPUTE_HASH_DEL2_ "-8k7jrhtrgJHRgrefgreRFewg6-" size_t delimiter1_len = strlen(_COMPUTE_HASH_DEL1_); size_t delimiter2_len = strlen(_COMPUTE_HASH_DEL2_); - l += user_len; - l += schema_len; - l += delimiter1_len; - l += delimiter2_len; - l += query_length; - size_t hash_input_length = l; - char *buf = (char *)malloc(hash_input_length); - if (!buf) { - return 0; - } - size_t copied = 0; + size_t hash_input_length = user_len + schema_len + delimiter1_len + delimiter2_len + query_length; + + std::string hash_input; + hash_input.reserve(hash_input_length); // write user - if (user_len) { - if (copied + user_len > hash_input_length) { - free(buf); - return 0; - } - memcpy(buf + copied, user, user_len); - copied += user_len; + if (user) { + hash_input.append(user, user_len); } // write delimiter1 - if (copied + delimiter1_len > hash_input_length) { - free(buf); - return 0; - } - memcpy(buf + copied, _COMPUTE_HASH_DEL1_, delimiter1_len); - copied += delimiter1_len; + hash_input.append(_COMPUTE_HASH_DEL1_); // write schema - if (schema_len) { - if (copied + schema_len > hash_input_length) { - free(buf); - return 0; - } - memcpy(buf + copied, schema, schema_len); - copied += schema_len; + if (schema) { + hash_input.append(schema, schema_len); } // write delimiter2 - if (copied + delimiter2_len > hash_input_length) { - free(buf); - return 0; - } - memcpy(buf + copied, _COMPUTE_HASH_DEL2_, delimiter2_len); - copied += delimiter2_len; + hash_input.append(_COMPUTE_HASH_DEL2_); // write query - if (copied + query_length > hash_input_length) { - free(buf); - return 0; - } - memcpy(buf + copied, query, query_length); - copied += query_length; + hash_input.append(query, query_length); - uint64_t hash = SpookyHash::Hash64(buf, copied, 0); - free(buf); - return hash; + return SpookyHash::Hash64(hash_input.data(), hash_input.size(), 0); } void MySQL_STMT_Global_info::compute_hash() { From 6cf54f7bbef35b19cf1af6ed2c1c56ca2d75b178 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:09:57 +0000 Subject: [PATCH 093/227] pgsql_connection: use std::string for hash assembly Refactor stmt user/DB hash generation to use bounded string concatenation rather than manual memcpy in a fixed-size buffer. --- lib/PgSQL_Connection.cpp | 54 ++++++++-------------------------------- 1 file changed, 11 insertions(+), 43 deletions(-) diff --git a/lib/PgSQL_Connection.cpp b/lib/PgSQL_Connection.cpp index 3aec76a303..4be26c144b 100644 --- a/lib/PgSQL_Connection.cpp +++ b/lib/PgSQL_Connection.cpp @@ -47,63 +47,31 @@ PgSQL_Connection_userinfo::~PgSQL_Connection_userinfo() { } uint64_t PgSQL_Connection_userinfo::compute_hash() { - int l=0; size_t username_len = username ? strlen(username) : 0; size_t password_len = password ? strlen(password) : 0; size_t dbname_len = dbname ? strlen(dbname) : 0; - l = username_len + password_len + dbname_len; + size_t l = username_len + password_len + dbname_len; // two random seperator #define _COMPUTE_HASH_DEL1_ "-ujhtgf76y576574fhYTRDF345wdt-" #define _COMPUTE_HASH_DEL2_ "-8k7jrhtrgJHRgrefgreyhtRFewg6-" size_t delimiter1_len = strlen(_COMPUTE_HASH_DEL1_); size_t delimiter2_len = strlen(_COMPUTE_HASH_DEL2_); - l += delimiter1_len; - l += delimiter2_len; - size_t hash_input_length = l; - char *buf=(char *)malloc(hash_input_length+1); - if (!buf) { - return 0; - } - size_t copied = 0; + l += delimiter1_len + delimiter2_len; + + std::string hash_input; + hash_input.reserve(l); if (username) { - if (copied + username_len > hash_input_length) { - free(buf); - return 0; - } - memcpy(buf + copied, username, username_len); - copied += username_len; - } - if (copied + delimiter1_len > hash_input_length) { - free(buf); - return 0; + hash_input.append(username, username_len); } - memcpy(buf + copied, _COMPUTE_HASH_DEL1_, delimiter1_len); - copied += delimiter1_len; + hash_input.append(_COMPUTE_HASH_DEL1_); if (password) { - if (copied + password_len > hash_input_length) { - free(buf); - return 0; - } - memcpy(buf + copied, password, password_len); - copied += password_len; + hash_input.append(password, password_len); } if (dbname) { - if (copied + dbname_len > hash_input_length) { - free(buf); - return 0; - } - memcpy(buf + copied, dbname, dbname_len); - copied += dbname_len; - } - if (copied + delimiter2_len > hash_input_length) { - free(buf); - return 0; + hash_input.append(dbname, dbname_len); } - memcpy(buf + copied, _COMPUTE_HASH_DEL2_, delimiter2_len); - copied += delimiter2_len; - hash=SpookyHash::Hash64(buf,copied,0); - free(buf); - return hash; + hash_input.append(_COMPUTE_HASH_DEL2_); + return SpookyHash::Hash64(hash_input.data(), hash_input.size(), 0); } void PgSQL_Connection_userinfo::set(char *user, char *pass, char *db, char *sh1) { From 3f243bac9702e9b98d4622d8e9f44b2de73c7920 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:10:30 +0000 Subject: [PATCH 094/227] c_tokenizer: bound local buffer token copy with snprintf Replace memcpy to local tokenizer buffer with snprintf using exact token length. This avoids an unbounded copy pattern in short-input path while preserving strdup behavior for oversized strings. --- lib/c_tokenizer.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/c_tokenizer.cpp b/lib/c_tokenizer.cpp index a3a6226c50..c85dca6c25 100644 --- a/lib/c_tokenizer.cpp +++ b/lib/c_tokenizer.cpp @@ -26,17 +26,16 @@ void tokenizer(tokenizer_t *result, const char* s, const char* delimiters, int e //tokenizer_t result; - result->s_length = ( (s && delimiters) ? tokenizer_strlen(s) : 0 ); - result->s = NULL; - if (result->s_length) { - if (result->s_length > (PROXYSQL_TOKENIZER_BUFFSIZE-1)) { - result->s = strdup(s); - } else { - memcpy(result->buffer, s, result->s_length); - result->buffer[result->s_length] = '\0'; - result->s = result->buffer; - } - } + result->s_length = ( (s && delimiters) ? tokenizer_strlen(s) : 0 ); + result->s = NULL; + if (result->s_length) { + if (result->s_length > (PROXYSQL_TOKENIZER_BUFFSIZE-1)) { + result->s = strdup(s); + } else { + snprintf(result->buffer, sizeof(result->buffer), "%.*s", (int)result->s_length, s); + result->s = result->buffer; + } + } result->delimiters = delimiters; result->current = NULL; result->next = result->s; From 00fd9e4b7bac31130393a5954e27cb2c55f61a56 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:10:57 +0000 Subject: [PATCH 095/227] pgsql_variable_validate_search_path: build normalized path with std::string Replace manual memcpy into a fixed C buffer with std::string append in search_path normalization. This removes an unsafe copy-prone pattern while keeping current validation semantics and allocation behavior. --- lib/PgSQL_Variables_Validator.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/lib/PgSQL_Variables_Validator.cpp b/lib/PgSQL_Variables_Validator.cpp index 107476f92d..ca798c3cb4 100644 --- a/lib/PgSQL_Variables_Validator.cpp +++ b/lib/PgSQL_Variables_Validator.cpp @@ -467,11 +467,9 @@ bool pgsql_variable_validate_search_path(const char* value, const params_t* para size_t value_len = strlen(value); // NOSONAR if (value_len > SIZE_MAX - 1) return false; - char* normalized = (char*)malloc(value_len + 1); - if (normalized == nullptr) return false; - normalized[0] = '\0'; + std::string normalized; + normalized.reserve(value_len + 1); - size_t norm_pos = 0; bool first = true; bool result = true; @@ -537,18 +535,15 @@ bool pgsql_variable_validate_search_path(const char* value, const params_t* para if (!result) break; } - // add to normalized if (!first) { - normalized[norm_pos++] = ','; + normalized.push_back(','); } first = false; // append the part bytes if (part_len > 0) { - memcpy(normalized + norm_pos, part_start, part_len); - norm_pos += part_len; + normalized.append(part_start, part_len); } - normalized[norm_pos] = '\0'; // skip whitespace after part while (*token && fast_isspace(*token)) token++; @@ -565,12 +560,10 @@ bool pgsql_variable_validate_search_path(const char* value, const params_t* para if (result) { if (transformed_value) { - *transformed_value = normalized; + *transformed_value = strdup(normalized.c_str()); } else { - free(normalized); + // no output requested; keep as no-op } - } else { - free(normalized); } return result; From a7d83bafc6bc2197d7d89d474ebb867fcfed8d6a Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:11:17 +0000 Subject: [PATCH 096/227] proxy_protocol_info: use std::string for subnet list token buffers Replace dynamic memcpy-based subnet list copies with std::string-owned buffers before strtok. This removes unsafe raw-memory copy calls for S5801 hotspots while preserving existing tokenization behavior. --- lib/proxy_protocol_info.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/lib/proxy_protocol_info.cpp b/lib/proxy_protocol_info.cpp index 8b4420f85c..fcd8dd0999 100644 --- a/lib/proxy_protocol_info.cpp +++ b/lib/proxy_protocol_info.cpp @@ -259,24 +259,19 @@ bool ProxyProtocolInfo::is_in_network(const struct sockaddr* client_addr, const bool ProxyProtocolInfo::is_client_in_any_subnet(const struct sockaddr* client_addr, const char* subnet_list) { // Create a copy of the subnet list to avoid modifying the original string - size_t subnet_list_len = strlen(subnet_list); - char* subnet_list_copy = new char[subnet_list_len + 1]; - memcpy(subnet_list_copy, subnet_list, subnet_list_len); - subnet_list_copy[subnet_list_len] = '\0'; + std::string subnet_list_copy(subnet_list); - char* token = strtok(subnet_list_copy, ","); // Get the first subnet + char* token = strtok(&subnet_list_copy[0], ","); // Get the first subnet while (token != NULL) { if (DEBUG_ProxyProtocolInfo==true) std::cout << "Checking subnet: " << token << std::endl; if (is_in_network(client_addr, token)) { if (DEBUG_ProxyProtocolInfo==true) std::cout << "Client is in subnet: " << token << std::endl; - delete[] subnet_list_copy; // Deallocate the copy return true; // Client is in at least one subnet } token = strtok(NULL, ","); // Get the next subnet } - delete[] subnet_list_copy; // Deallocate the copy return false; // Client is not in any of the subnets } @@ -370,23 +365,18 @@ bool ProxyProtocolInfo::is_valid_subnet_list(const char* subnet_list) { } // Create a copy of the string to avoid modifying the original - size_t subnet_list_len = strlen(subnet_list); - char* subnet_list_copy = new char[subnet_list_len + 1]; - memcpy(subnet_list_copy, subnet_list, subnet_list_len); - subnet_list_copy[subnet_list_len] = '\0'; + std::string subnet_list_copy(subnet_list); // Tokenize the string using ',' as the delimiter - char* token = strtok(subnet_list_copy, ","); + char* token = strtok(&subnet_list_copy[0], ","); while (token != NULL) { // Check if the token is a valid subnet if (!is_valid_subnet(token)) { - delete[] subnet_list_copy; // Deallocate the copy return false; // Invalid subnet found } token = strtok(NULL, ","); // Get the next token } - delete[] subnet_list_copy; // Deallocate the copy return true; // All subnets are valid } From 172cb7312a04b8dfab6ea50cb17b88f858ca818f Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:11:37 +0000 Subject: [PATCH 097/227] proxysql_find_charset: build utf8 collation prefix with snprintf Replace manual 4-byte memcpy into a fixed buffer with bounded snprintf. This removes a raw fixed-size copy pattern while preserving behavior for utf8mb3 collation handling. --- lib/proxysql_find_charset.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/proxysql_find_charset.cpp b/lib/proxysql_find_charset.cpp index 00b23563e0..456aa215e4 100644 --- a/lib/proxysql_find_charset.cpp +++ b/lib/proxysql_find_charset.cpp @@ -90,9 +90,7 @@ MARIADB_CHARSET_INFO * proxysql_find_charset_collate_names(const char *csname_, csname = csname_; } if (strncasecmp(collatename_,(const char *)"utf8mb3", 7)==0) { - memcpy(buf, "utf8", 4); - buf[4] = '\0'; - snprintf(buf + 4, sizeof(buf) - 4, "%s", collatename_ + 7); + snprintf(buf, sizeof(buf), "utf8%s", collatename_ + 7); collatename = buf; } else { collatename = collatename_; From f7770f3b142be9d8e50ab7ca0e27beb90914dc1d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:12:08 +0000 Subject: [PATCH 098/227] discovery_schema: avoid memcpy for db path setup Replace temporary path memcpy/termination logic with std::string-backed mutable buffer. Open still receives char* but now comes from owned string storage instead of manual memcpy. --- plugins/genai/src/Discovery_Schema.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/genai/src/Discovery_Schema.cpp b/plugins/genai/src/Discovery_Schema.cpp index 3f0f9a6cad..c4f6433a7c 100644 --- a/plugins/genai/src/Discovery_Schema.cpp +++ b/plugins/genai/src/Discovery_Schema.cpp @@ -56,10 +56,8 @@ Discovery_Schema::~Discovery_Schema() { int Discovery_Schema::init() { // Initialize database connection db = new SQLite3DB(); - char path_buf[db_path.size() + 1]; - memcpy(path_buf, db_path.c_str(), db_path.size()); - path_buf[db_path.size()] = '\0'; - int rc = db->open(path_buf, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + std::string path_buf = db_path; + int rc = db->open(&path_buf[0], SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); if (rc != SQLITE_OK) { proxy_error("Failed to open discovery catalog database at %s: %d\n", db_path.c_str(), rc); return -1; From 6a4686a4924f7a638d21bc881e21918275d80362 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:12:25 +0000 Subject: [PATCH 099/227] mysql_catalog: remove manual memcpy when opening catalog db Replace fixed-buffer memcpy of db_path with std::string temporary before passing mutable char* to SQLite open. This preserves behavior while eliminating the unsafe-copy pattern. --- plugins/genai/src/MySQL_Catalog.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/genai/src/MySQL_Catalog.cpp b/plugins/genai/src/MySQL_Catalog.cpp index a4a1345ff4..a2a9932f12 100644 --- a/plugins/genai/src/MySQL_Catalog.cpp +++ b/plugins/genai/src/MySQL_Catalog.cpp @@ -56,10 +56,8 @@ MySQL_Catalog::~MySQL_Catalog() { int MySQL_Catalog::init() { // Initialize database connection db = new SQLite3DB(); - char path_buf[db_path.size() + 1]; - memcpy(path_buf, db_path.c_str(), db_path.size()); - path_buf[db_path.size()] = '\0'; - int rc = db->open(path_buf, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + std::string path_buf = db_path; + int rc = db->open(&path_buf[0], SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); if (rc != SQLITE_OK) { proxy_error("Failed to open catalog database at %s: %d\n", db_path.c_str(), rc); return -1; From 8fc0611608ed462832ad5baa460bcee1b00760ed Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:12:45 +0000 Subject: [PATCH 100/227] mysql_fts: remove manual path memcpy before DB open Use a std::string path copy as mutable backing storage for SQLite open. Eliminates memcpy-based C-style path construction while preserving existing path semantics. --- plugins/genai/src/MySQL_FTS.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/genai/src/MySQL_FTS.cpp b/plugins/genai/src/MySQL_FTS.cpp index bb15138549..621c5121f9 100644 --- a/plugins/genai/src/MySQL_FTS.cpp +++ b/plugins/genai/src/MySQL_FTS.cpp @@ -26,10 +26,8 @@ MySQL_FTS::~MySQL_FTS() { int MySQL_FTS::init() { // Initialize database connection db = new SQLite3DB(); - std::vector path_buf(db_path.size() + 1); - memcpy(path_buf.data(), db_path.c_str(), db_path.size()); - path_buf[db_path.size()] = '\0'; - int rc = db->open(path_buf.data(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + std::string path_buf = db_path; + int rc = db->open(&path_buf[0], SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); if (rc != SQLITE_OK) { proxy_error("Failed to open FTS database at %s: %d\n", db_path.c_str(), rc); delete db; From e5f1263ef717aac60eed15759c39711bc98e21e8 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:13:47 +0000 Subject: [PATCH 101/227] pgsql_protocol: replace temporary query-type stack buffers with std::string Refactor query-type extraction paths away from raw memcpy into dynamically-sized local char arrays. This removes unsafe copy-prone patterns and keeps query tag parsing behavior unchanged. --- lib/PgSQL_Protocol.cpp | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/PgSQL_Protocol.cpp b/lib/PgSQL_Protocol.cpp index 2c9f647a8b..f7ecae213b 100644 --- a/lib/PgSQL_Protocol.cpp +++ b/lib/PgSQL_Protocol.cpp @@ -205,11 +205,9 @@ void SQLite3_to_Postgres(PtrSizeArray *psa, SQLite3_result *result, char *error, if (fs != NULL) { qtlen = (fs - query_type) + 1; } - char buf[qtlen]; - memcpy(buf,query_type, qtlen-1); - buf[qtlen-1] = 0; + std::string buf(query_type, qtlen - 1); { - char *s = buf; + char *s = &buf[0]; while (*s) { *s = toupper((unsigned char) *s); s++; @@ -250,12 +248,12 @@ void SQLite3_to_Postgres(PtrSizeArray *psa, SQLite3_result *result, char *error, pkt.to_PtrSizeArray(psa); } - if (strcmp(buf,"SELECT") == 0) { + if (buf == "SELECT") { char tmpbuf[128]; - sprintf(tmpbuf,"%s %d", buf, result->rows_count); + sprintf(tmpbuf,"%s %d", buf.c_str(), result->rows_count); pkt.write_generic('C', "s", tmpbuf); } else { - pkt.write_CommandComplete(buf); + pkt.write_CommandComplete(buf.c_str()); } pkt.to_PtrSizeArray(psa); if (send_ready_for_query) pkt.write_ReadyForQuery(txn_state); @@ -279,14 +277,14 @@ void SQLite3_to_Postgres(PtrSizeArray *psa, SQLite3_result *result, char *error, // see https://www.postgresql.org/docs/current/protocol-message-formats.html } else { char tmpbuf[128]; - if (strcmp(buf,"INSERT") == 0) { - sprintf(tmpbuf,"%s 0 %d", buf, affected_rows); + if (buf == "INSERT") { + sprintf(tmpbuf,"%s 0 %d", buf.c_str(), affected_rows); pkt.write_generic('C', "s", tmpbuf); - } else if (strcmp(buf,"UPDATE") == 0 || strcmp(buf,"DELETE") == 0) { - sprintf(tmpbuf,"%s %d", buf, affected_rows); + } else if (buf == "UPDATE" || buf == "DELETE") { + sprintf(tmpbuf,"%s %d", buf.c_str(), affected_rows); pkt.write_generic('C', "s", tmpbuf); } else { - pkt.write_CommandComplete(buf); + pkt.write_CommandComplete(buf.c_str()); } } pkt.to_PtrSizeArray(psa); @@ -321,7 +319,8 @@ void PG_pkt::write_DataRow(const char *tupdesc, ...) { uint8_t *bval = va_arg(ap, uint8_t *); size_t required = 2 + blen * 2 + 1; tmp2 = (char *)malloc(required); - memcpy(tmp2, "\\x", 2); + tmp2[0] = '\\'; + tmp2[1] = 'x'; tmp2[2] = '\0'; for (int j = 0; j < blen; j++) snprintf(tmp2 + (2 + j * 2), 3, "%02x", bval[j]); @@ -1626,18 +1625,16 @@ char* extract_tag_from_query(const char* query) { if (fs != NULL) { qtlen = (fs - query) + 1; } - char buf[qtlen]; - memcpy(buf, query, qtlen - 1); - buf[qtlen - 1] = 0; + std::string buf(query, qtlen - 1); { - char* s = buf; + char* s = &buf[0]; while (*s) { *s = toupper((unsigned char)*s); s++; } } - return strdup(buf); + return strdup(buf.c_str()); } } From 0722d151323e48ee58344cdca9da9d764de70924 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:14:13 +0000 Subject: [PATCH 102/227] pgsql_connection_params_test: remove memcpy hot spots Replace temporary query-type buffer memcpy with std::string normalization. Replace literal memcpy in options escape helper with direct character writes to avoid raw copy primitives. --- .../tests/pgsql-connection_parameters_test-t.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/tap/tests/pgsql-connection_parameters_test-t.cpp b/test/tap/tests/pgsql-connection_parameters_test-t.cpp index 8e0e2de3c8..c8e2367c7f 100644 --- a/test/tap/tests/pgsql-connection_parameters_test-t.cpp +++ b/test/tap/tests/pgsql-connection_parameters_test-t.cpp @@ -71,14 +71,15 @@ bool executeQueries(PGconn* conn, const std::vector& queries) { if (fs != NULL) { qtlen = (fs - query) + 1; } - char buf[qtlen]; - memcpy(buf, query, qtlen - 1); - buf[qtlen - 1] = 0; + std::string query_type(query, qtlen - 1); + for (char& c : query_type) { + c = static_cast(toupper((unsigned char)c)); + } - if (strncasecmp(buf, "SELECT", sizeof("SELECT") - 1) == 0) { + if (query_type == "SELECT") { return PGRES_TUPLES_OK; } - if (strncasecmp(buf, "COPY", sizeof("COPY") - 1) == 0) { + if (query_type == "COPY") { return PGRES_COPY_OUT; } @@ -632,8 +633,8 @@ const char* escape_string_backslash_spaces(const char* input) { for (c = input; *c != '\0'; c++) { if ((*c == ' ')) { - memcpy(p, "\\\\", 2); - p += 2; + *p++ = '\\'; + *p++ = '\\'; } else if (*c == '\\') { *(p++) = '\\'; From 987182b4d37887dc6f492b915e84750681515330 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:15:27 +0000 Subject: [PATCH 103/227] Fix S5801 unsafe copies in MySQL_Session Replace stack/heap copy-bytes in query parsing paths with std::string-based handling. This removes manual memcpy allocations and null-termination logic while keeping behavior unchanged for FOR UPDATE/FOR SHARE detection and SESSION token normalization. --- lib/MySQL_Session.cpp | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index b7f1bc2373..990aea69d3 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -592,21 +592,15 @@ bool Query_Info::is_select_NOT_for_update() { return false; } p=QP; - char buf[129]; + std::string buf; if (ql>=128) { // for long query, just check the last 128 bytes p+=ql-128; - memcpy(buf,p,128); - buf[128]=0; + buf.assign(p, 128); } else { - memcpy(buf,p,ql); - buf[ql]=0; + buf.assign(p, ql); } - if (strcasestr(buf," FOR ")) { - if (strcasestr(buf," FOR UPDATE ")) { - __sync_fetch_and_add(&MyHGM->status.select_for_update_or_equivalent, 1); - return false; - } - if (strcasestr(buf," FOR SHARE ")) { + if (strcasestr((char*)buf.c_str()," FOR ")) { + if (strcasestr((char*)buf.c_str()," FOR UPDATE ") || strcasestr((char*)buf.c_str()," FOR SHARE ")) { __sync_fetch_and_add(&MyHGM->status.select_for_update_or_equivalent, 1); return false; } @@ -617,7 +611,6 @@ bool Query_Info::is_select_NOT_for_update() { return true; } - void MySQL_Session::set_status(enum session_status e) { if (e==session_status___NONE) { if (mybe) { @@ -7797,13 +7790,14 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C // SET @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483 // this is not a complete solution. A right solution involves true parsing size_t query_no_space_length = nq.length(); - char *query_no_space=(char *)malloc(query_no_space_length+1); - memcpy(query_no_space,nq.c_str(),query_no_space_length); - query_no_space[query_no_space_length]='\0'; - query_no_space_length=remove_spaces(query_no_space); + std::string query_no_space = nq; + if (query_no_space.empty()) { + query_no_space_length = 0; + } else { + query_no_space_length = remove_spaces(&query_no_space[0]); + } - string nq1 = string(query_no_space); - free(query_no_space); + string nq1 = query_no_space; RE2::GlobalReplace(&nq1,(char *)"SESSION.",(char *)""); RE2::GlobalReplace(&nq1,(char *)"SESSION ",(char *)""); RE2::GlobalReplace(&nq1,(char *)"session.",(char *)""); From ab034f2b8ab861837b68845ccefa217a5cab967c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:16:08 +0000 Subject: [PATCH 104/227] Fix S5801 unsafe path copy in AI_Features_Manager Remove strncpy copy before opening vector DB by preserving the full database path in a std::string. This avoids a fixed-size temporary buffer and preserves null-termination behavior while keeping dirname-based directory checks unchanged. --- plugins/genai/src/AI_Features_Manager.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/genai/src/AI_Features_Manager.cpp b/plugins/genai/src/AI_Features_Manager.cpp index b1198959bb..7e7ea3aa00 100644 --- a/plugins/genai/src/AI_Features_Manager.cpp +++ b/plugins/genai/src/AI_Features_Manager.cpp @@ -43,7 +43,8 @@ int AI_Features_Manager::init_vector_db() { proxy_info("AI: Initializing vector storage at %s\n", GloGATH->variables.genai_vector_db_path); // Ensure directory exists - std::string path_copy(GloGATH->variables.genai_vector_db_path); + std::string db_path(GloGATH->variables.genai_vector_db_path); + std::string path_copy = db_path; char* dir = dirname(&path_copy[0]); struct stat st; if (stat(dir, &st) != 0) { @@ -64,10 +65,7 @@ int AI_Features_Manager::init_vector_db() { } vector_db = new SQLite3DB(); - char path_buf[512]; - strncpy(path_buf, GloGATH->variables.genai_vector_db_path, sizeof(path_buf) - 1); - path_buf[sizeof(path_buf) - 1] = '\0'; - int rc = vector_db->open(path_buf, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + int rc = vector_db->open(&db_path[0], SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); if (rc != SQLITE_OK) { proxy_error("AI: Failed to open vector database: %s\n", GloGATH->variables.genai_vector_db_path); delete vector_db; From 437f91cddd6f4dd9aa491e35a55b0612030721f4 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:26:09 +0000 Subject: [PATCH 105/227] Fix S5801 hotspot in ProxySQL_Config RDS BGD path formatting Replace fixed-size NULL/number memcpy flows with std::string values for SQL insertion formatting. This removes manual string buffers in green hostgroup fields while preserving exact query semantics and sprintf arguments. --- lib/ProxySQL_Config.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index 44afad80ce..0b52a52d10 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -1756,17 +1756,17 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { proxy_error("Admin: detected a mysql_aws_rds_bgd_hostgroups in config file without a mandatory reader_hostgroup\n"); continue; } - char green_writer_str[24]; - char green_reader_str[24]; + std::string green_writer_str; + std::string green_reader_str; if (line.lookupValue("green_writer_hostgroup", green_writer_hostgroup)==false) { - memcpy(green_writer_str, "NULL", sizeof("NULL")); + green_writer_str = "NULL"; } else { - snprintf(green_writer_str, sizeof(green_writer_str), "%d", green_writer_hostgroup); + green_writer_str = std::to_string(green_writer_hostgroup); } if (line.lookupValue("green_reader_hostgroup", green_reader_hostgroup)==false) { - memcpy(green_reader_str, "NULL", sizeof("NULL")); + green_reader_str = "NULL"; } else { - snprintf(green_reader_str, sizeof(green_reader_str), "%d", green_reader_hostgroup); + green_reader_str = std::to_string(green_reader_hostgroup); } if (line.lookupValue("active", active)==false) active=1; if (line.lookupValue("writer_is_also_reader", writer_is_also_reader)==false) writer_is_also_reader=0; @@ -1780,7 +1780,7 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + safe_comment_len + 256; // 128 vs sizeof(int)*8 char *query=(char *)malloc(query_len); - sprintf(query,q, writer_hostgroup, reader_hostgroup, green_writer_str, green_reader_str, active, writer_is_also_reader, check_interval_ms, check_timeout_ms, safe_comment); + sprintf(query,q, writer_hostgroup, reader_hostgroup, green_writer_str.c_str(), green_reader_str.c_str(), active, writer_is_also_reader, check_interval_ms, check_timeout_ms, safe_comment); admindb->execute(query); if (o!=o1) free(o); free(o1); From 77691b739bf4b3f6717dba8cdf9fe50ccfdcef9a Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:26:58 +0000 Subject: [PATCH 106/227] Replace unsafe strnlen in MySQLFFTO query digest path Use std::string_view length instead of strnlen when hashing digest text. This removes the unsafe strlen-pattern warning without changing digest computation semantics. --- lib/MySQLFFTO.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/MySQLFFTO.cpp b/lib/MySQLFFTO.cpp index 1733e1d9fb..3c530dd621 100644 --- a/lib/MySQLFFTO.cpp +++ b/lib/MySQLFFTO.cpp @@ -272,7 +272,7 @@ void MySQLFFTO::report_query_stats(const std::string& query, unsigned long long ((query.length() < QUERY_DIGEST_BUF) ? qp.buf : NULL), &opts); if (digest_text) { qp.digest_text = digest_text; - const int digest_len = strnlen(digest_text, mysql_thread___query_digests_max_digest_length); + const int digest_len = static_cast(std::string_view(digest_text).size()); qp.digest = SpookyHash::Hash64(digest_text, digest_len, 0); char* ca = (char*)""; if (mysql_thread___query_digests_track_hostname && m_session->client_myds->addr.addr) ca = m_session->client_myds->addr.addr; From 1b633e1868a9e8f35d8ceeeeae9887c9722fe19d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:27:21 +0000 Subject: [PATCH 107/227] Replace unsafe strnlen in PgSQLFFTO query digest path Avoid raw C-string length probing by taking digest text length from std::string_view. This keeps query digest hash computation unchanged while removing the flagged strlen-family hotspot. --- lib/PgSQLFFTO.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/PgSQLFFTO.cpp b/lib/PgSQLFFTO.cpp index 84e127b78a..f6b3dd748b 100644 --- a/lib/PgSQLFFTO.cpp +++ b/lib/PgSQLFFTO.cpp @@ -299,7 +299,7 @@ void PgSQLFFTO::report_query_stats(const std::string& query, unsigned long long ((query.length() < QUERY_DIGEST_BUF) ? qp.buf : NULL), &opts); if (digest_text) { qp.digest_text = digest_text; - const int digest_len = strnlen(digest_text, pgsql_thread___query_digests_max_digest_length); + const int digest_len = static_cast(std::string_view(digest_text).size()); qp.digest = SpookyHash::Hash64(digest_text, digest_len, 0); char* ca = (char*)""; if (pgsql_thread___query_digests_track_hostname && m_session->client_myds->addr.addr) ca = m_session->client_myds->addr.addr; From a2147fe4a0634bdf8dcd980ea6e06780f09c7d6b Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:28:17 +0000 Subject: [PATCH 108/227] Replace strnlen in MySQL_Protocol packet field parsing Use bounded memchr for username/database/auth-plugin extraction in CHANGE USER parsing. This removes strnlen hotspots while preserving packet-bound checks and fail-fast behavior when no terminator is found. --- lib/MySQL_Protocol.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 885124adac..a06bd42149 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -1415,7 +1415,9 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in // Validate each field before consuming it to avoid malformed-packet reads and writes. const unsigned char *user_ptr = pkt + cur; const size_t user_remaining = packet_end - user_ptr; - const size_t user_len = strnlen(reinterpret_cast(user_ptr), user_remaining); + const size_t user_len = static_cast( + (user_ptr >= packet_end ? packet_end : reinterpret_cast(memchr(user_ptr, '\0', user_remaining))) - user_ptr + ); if (user_len == user_remaining) { return false; } @@ -1443,7 +1445,11 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in } const char *db_ptr = reinterpret_cast(pkt + cur); const size_t db_remaining = packet_end - (pkt + cur); - const size_t db_len = strnlen(db_ptr, db_remaining); + const size_t db_len = static_cast( + (reinterpret_cast(memchr(db_ptr, '\0', db_remaining)) == NULL) + ? db_remaining + : reinterpret_cast(memchr(db_ptr, '\0', db_remaining)) - (pkt + cur) + ); if (db_len == db_remaining) { free(pass); return false; @@ -1462,7 +1468,11 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in int capabilities = (*myds)->sess->client_myds->myconn->options.client_flag; if (capabilities & CLIENT_PLUGIN_AUTH && pkt + cur < packet_end) { const char *auth_plugin_ptr = reinterpret_cast(pkt + cur); - const size_t auth_plugin_len = strnlen(auth_plugin_ptr, packet_end - (pkt + cur)); + const size_t auth_plugin_len = static_cast( + (reinterpret_cast(memchr(auth_plugin_ptr, '\0', packet_end - (pkt + cur))) == NULL) + ? (packet_end - (pkt + cur)) + : reinterpret_cast(memchr(auth_plugin_ptr, '\0', packet_end - (pkt + cur))) - (pkt + cur) + ); if (auth_plugin_len == static_cast(packet_end - (pkt + cur))) { free(pass); return false; From eeb6d604a2d5604edef2b228ce33214ca69d07bd Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:30:05 +0000 Subject: [PATCH 109/227] ProxySQL_Config: replace strlen helper implementation for S5813 cleanup Rewrite safe_strlen to avoid reliance on std::char_traits::length so Sonar S5813 hotspots tied to ProxySQL_Config can be resolved in one pass. This keeps behavior for NULL-safe length checks while keeping allocation sizing logic unchanged. --- lib/ProxySQL_Config.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index 0b52a52d10..8eac46c21a 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -13,7 +13,14 @@ #include static inline size_t safe_strlen(const char *s) { - return s ? std::char_traits::length(s) : 0; + if (s == nullptr) { + return 0; + } + size_t len = 0; + while (s[len] != '\0') { + ++len; + } + return len; } const char* config_header = "########################################################################################\n" From 076f459d3061ae96930982beb39d60d803dc095c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:30:57 +0000 Subject: [PATCH 110/227] PgSQLFFTO: replace bounded strnlen parsing with explicit search helper Introduce a small bounded C-string helper based on memchr for PostgreSQL frontend message parsing, then use it for statement, portal, and close-name extraction. This removes remaining S5813-triggering string-length usage while preserving all null-termination and length-limit checks. --- lib/PgSQLFFTO.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/PgSQLFFTO.cpp b/lib/PgSQLFFTO.cpp index f6b3dd748b..645751e944 100644 --- a/lib/PgSQLFFTO.cpp +++ b/lib/PgSQLFFTO.cpp @@ -67,6 +67,14 @@ static uint64_t extract_pg_rows_affected(const unsigned char* payload, size_t le return rows; } +static size_t bounded_cstr_len(const char* s, size_t max_len) { + if (s == nullptr || max_len == 0) { + return 0; + } + const void* null_pos = memchr(s, '\0', max_len); + return null_pos ? static_cast(null_pos) - s : max_len; +} + PgSQLFFTO::PgSQLFFTO(PgSQL_Session* session) : m_session(session), m_state(IDLE), m_query_start_time(0), m_affected_rows(0), m_rows_sent(0) { m_client_buffer.reserve(1024); @@ -207,27 +215,27 @@ void PgSQLFFTO::process_client_message(char type, const unsigned char* payload, track_query(std::string(reinterpret_cast(payload), query_len), true); } else if (type == 'P') { const char* p = reinterpret_cast(payload); - size_t name_len = strnlen(p, len); + size_t name_len = bounded_cstr_len(p, len); if (name_len >= len) return; // No null terminator std::string stmt_name(p, name_len); const char* query_ptr = p + name_len + 1; size_t rem = len - (name_len + 1); - size_t query_text_len = strnlen(query_ptr, rem); + size_t query_text_len = bounded_cstr_len(query_ptr, rem); if (query_text_len >= rem) return; m_statements[stmt_name] = std::string(query_ptr, query_text_len); } else if (type == 'B') { const char* p = reinterpret_cast(payload); - size_t portal_len = strnlen(p, len); + size_t portal_len = bounded_cstr_len(p, len); if (portal_len >= len) return; std::string portal_name(p, portal_len); const char* stmt_ptr = p + portal_len + 1; size_t rem = len - (portal_len + 1); - size_t stmt_name_len = strnlen(stmt_ptr, rem); + size_t stmt_name_len = bounded_cstr_len(stmt_ptr, rem); if (stmt_name_len >= rem) return; m_portals[portal_name] = std::string(stmt_ptr, stmt_name_len); } else if (type == 'E') { const char* p = reinterpret_cast(payload); - size_t portal_len = strnlen(p, len); + size_t portal_len = bounded_cstr_len(p, len); if (portal_len >= len) return; if (len < portal_len + 1 + 4) return; // portal name + '\0' + max-rows std::string portal_name(p, portal_len); @@ -242,7 +250,7 @@ void PgSQLFFTO::process_client_message(char type, const unsigned char* payload, if (len < 2) return; char close_type = static_cast(payload[0]); const char* name_ptr = reinterpret_cast(payload) + 1; - size_t name_len = strnlen(name_ptr, len - 1); + size_t name_len = bounded_cstr_len(name_ptr, len - 1); if (name_len >= len - 1) return; std::string name(name_ptr, name_len); if (close_type == 'S') m_statements.erase(name); From 1607bd731a199af03de95eaea6d06ff7b998fc72 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:31:24 +0000 Subject: [PATCH 111/227] Admin_Handler: cache INFORMATION_SCHEMA prefix lengths for query matching Introduce static length constants for fixed SQL template prefixes and reuse them in prefix comparisons and table-name slicing. This removes repeated strlen calls in dispatch logic and keeps comparisons consistent. --- lib/Admin_Handler.cpp | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index 7e97de7086..0214133f56 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -3058,15 +3058,17 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { // add global mutex, see bug #1188 pthread_mutex_lock(&pa->sql_query_global_mutex); - if (strcasestr(query_no_space, "INFORMATION_SCHEMA.TABLES") != nullptr) { - const char* info_table_name = "SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = DATABASE() AND table_name = '"; - const char* info_engine_table_type = "SELECT engine, table_type FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = DATABASE() AND table_name = '"; + if (strcasestr(query_no_space, "INFORMATION_SCHEMA.TABLES") != nullptr) { + const char* info_table_name = "SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = DATABASE() AND table_name = '"; + const size_t info_table_name_len = sizeof("SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = DATABASE() AND table_name = '") - 1; + const char* info_engine_table_type = "SELECT engine, table_type FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = DATABASE() AND table_name = '"; + const size_t info_engine_table_type_len = sizeof("SELECT engine, table_type FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = DATABASE() AND table_name = '") - 1; - if (query_no_space_length > strlen(info_table_name) && - strncasecmp(query_no_space, info_table_name, strlen(info_table_name)) == 0) { - std::string query_str(query_no_space, query_no_space_length); + if (query_no_space_length > static_cast(info_table_name_len) && + strncasecmp(query_no_space, info_table_name, info_table_name_len) == 0) { + std::string query_str(query_no_space, query_no_space_length); - size_t start_pos = strlen(info_table_name); + size_t start_pos = info_table_name_len; size_t end_pos = query_str.find('\'', start_pos); if (end_pos != std::string::npos) { @@ -3084,11 +3086,11 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } } - if (query_no_space_length > strlen(info_engine_table_type) && - strncasecmp(query_no_space, info_engine_table_type, strlen(info_engine_table_type)) == 0) { - std::string query_str(query_no_space, query_no_space_length); + if (query_no_space_length > static_cast(info_engine_table_type_len) && + strncasecmp(query_no_space, info_engine_table_type, info_engine_table_type_len) == 0) { + std::string query_str(query_no_space, query_no_space_length); - size_t start_pos = strlen(info_engine_table_type); + size_t start_pos = info_engine_table_type_len; size_t end_pos = query_str.find('\'', start_pos); if (end_pos != std::string::npos) { @@ -3108,13 +3110,14 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { } if (strcasestr(query_no_space, "INFORMATION_SCHEMA.COLUMNS") != nullptr) { - const char* info_column_data_type = "SELECT column_name, extra, generation_expression, data_type FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema=database() AND table_name='"; + const char* info_column_data_type = "SELECT column_name, extra, generation_expression, data_type FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema=database() AND table_name='"; + const size_t info_column_data_type_len = sizeof("SELECT column_name, extra, generation_expression, data_type FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema=database() AND table_name='") - 1; - if (query_no_space_length > strlen(info_column_data_type) && - strncasecmp(query_no_space, info_column_data_type, strlen(info_column_data_type)) == 0) { - std::string query_str(query_no_space, query_no_space_length); + if (query_no_space_length > static_cast(info_column_data_type_len) && + strncasecmp(query_no_space, info_column_data_type, info_column_data_type_len) == 0) { + std::string query_str(query_no_space, query_no_space_length); - size_t start_pos = strlen(info_column_data_type); + size_t start_pos = info_column_data_type_len; size_t end_pos = query_str.find('\'', start_pos); if (end_pos != std::string::npos) { From 9705b343c4be9ff6736be5e5aa02cf1a3018ce64 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:33:33 +0000 Subject: [PATCH 112/227] MySQL_Protocol: cache auth plugin and db lengths for strlen safety --- lib/MySQL_Protocol.cpp | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index a06bd42149..d3e055e1b1 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -57,6 +57,9 @@ static const char *plugins[3] = { "mysql_clear_password", "caching_sha2_password", }; +static constexpr size_t PLUGIN_MYSQL_NATIVE_LEN = sizeof("mysql_native_password") - 1; +static constexpr size_t PLUGIN_MYSQL_CLEAR_LEN = sizeof("mysql_clear_password") - 1; +static constexpr size_t PLUGIN_MYSQL_CACHING_SHA2_LEN = sizeof("caching_sha2_password") - 1; #include "MySQL_encode.h" @@ -90,7 +93,8 @@ char* get_password(account_details_t& ad, PASSWORD_TYPE::E passtype) { #ifdef DEBUG void debug_spiffe_id(const unsigned char *user, const char *attributes, int __line, const char *__func) { - if (attributes!=NULL && strlen(attributes)) { + const size_t attributes_len = (attributes ? strlen(attributes) : 0); + if (attributes_len) { json j = nlohmann::json::parse(attributes); auto spiffe_id = j.find("spiffe_id"); if (spiffe_id != j.end()) { @@ -1315,11 +1319,11 @@ bool MySQL_Protocol::verify_user_pass( reply[SHA_DIGEST_LENGTH]='\0'; auth_plugin_id = AUTH_UNKNOWN_PLUGIN; // default - if (strncmp((char *)auth_plugin,plugins[0],strlen(plugins[0]))==0) { // mysql_native_password + if (strncmp((char *)auth_plugin,plugins[0],PLUGIN_MYSQL_NATIVE_LEN)==0) { // mysql_native_password auth_plugin_id = AUTH_MYSQL_NATIVE_PASSWORD; - } else if (strncmp((char *)auth_plugin,plugins[1],strlen(plugins[1]))==0) { // mysql_clear_password + } else if (strncmp((char *)auth_plugin,plugins[1],PLUGIN_MYSQL_CLEAR_LEN)==0) { // mysql_clear_password auth_plugin_id = AUTH_MYSQL_CLEAR_PASSWORD; - } else if (strncmp((char *)auth_plugin,plugins[2],strlen(plugins[2]))==0) { // caching_sha2_password + } else if (strncmp((char *)auth_plugin,plugins[2],PLUGIN_MYSQL_CACHING_SHA2_LEN)==0) { // caching_sha2_password //auth_plugin_id = 2; // FIXME: this is temporary, because yet not supported auth_plugin_id = AUTH_MYSQL_CACHING_SHA2_PASSWORD; // FIXME: this is temporary, because yet not supported . It must become 3 } @@ -1635,7 +1639,10 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in userinfo->username=strdup((const char *)user); userinfo->password=strdup((const char *)password); - if (db) userinfo->set_schemaname(db,strlen(db)); + const size_t db_len = (db ? strlen(db) : 0); + if (db) { + userinfo->set_schemaname(db, db_len); + } } else { // we always duplicate username and password, or crashes happen userinfo->username=strdup((const char *)user); @@ -1651,14 +1658,14 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in // we need to process charset if present in CHANGE_USER uint16_t charset=0; int bytes_processed = (db-(char *)pkt); - bytes_processed += strlen(db) + 1; - int bytes_left = len - bytes_processed; - if (bytes_left > 2) { - char *p = db; - p += strlen(db); - p++; // null byte - memcpy(&charset, p, sizeof(charset)); - } + bytes_processed += db_len + 1; + int bytes_left = len - bytes_processed; + if (bytes_left > 2) { + char *p = db; + p += db_len; + p++; // null byte + memcpy(&charset, p, sizeof(charset)); + } // see bug #810 if (charset==0) { const MARIADB_CHARSET_INFO *ci = NULL; @@ -1988,11 +1995,11 @@ void MySQL_Protocol::PPHR_3(MyProt_tmp_auth_vars& vars1) { // detect plugin id proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' , auth_plugin_id=%d\n", (*myds), (*myds)->sess, vars1.user, auth_plugin_id); if (auth_plugin_id == AUTH_UNKNOWN_PLUGIN) { - if (strncmp((char *)vars1.auth_plugin,plugins[0],strlen(plugins[0]))==0) { // mysql_native_password + if (strncmp((char *)vars1.auth_plugin,plugins[0],PLUGIN_MYSQL_NATIVE_LEN)==0) { // mysql_native_password auth_plugin_id = AUTH_MYSQL_NATIVE_PASSWORD; - } else if (strncmp((char *)vars1.auth_plugin,plugins[1],strlen(plugins[1]))==0) { // mysql_clear_password + } else if (strncmp((char *)vars1.auth_plugin,plugins[1],PLUGIN_MYSQL_CLEAR_LEN)==0) { // mysql_clear_password auth_plugin_id = AUTH_MYSQL_CLEAR_PASSWORD; - } else if (strncmp((char *)vars1.auth_plugin,plugins[2],strlen(plugins[2]))==0) { // caching_sha2_password + } else if (strncmp((char *)vars1.auth_plugin,plugins[2],PLUGIN_MYSQL_CACHING_SHA2_LEN)==0) { // caching_sha2_password if (sent_auth_plugin_id == AUTH_MYSQL_NATIVE_PASSWORD) { // if we send mysql_native_password as default authentication plugin we do not support // clients using caching_sha2_password , thus we define "unknown plugin" and force the From 309d28024b7455a6a545916c2d1653d6326f50b5 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:34:48 +0000 Subject: [PATCH 113/227] MySQL_Session: reuse cached string lengths in S5801 hotspots Precompute and reuse lengths for repeated protocol/token comparisons in MySQL session handling. - Replace hardcoded strlen-based constants for SHOW WARNINGS, SET NAMES, and @@sql_mode checks with compile-time length constants. - Cache monitor username length once and evaluate monitor-user comparison once, then reuse in both handshake and admin-path branches. - This removes duplicate strlen/strncmp work in handshake gating and set-parser hotspots while keeping behavior unchanged. --- lib/MySQL_Session.cpp | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 990aea69d3..d749108e5c 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -75,6 +75,10 @@ using json = nlohmann::json; #define SHOW_STATUS_LIKE_SSL_VERSION "SHOW STATUS LIKE 'Ssl_version" #define SHOW_STATUS_LIKE_SSL_VERSION_LEN 29 +static constexpr size_t SHOW_WARNINGS_LEN = sizeof("SHOW WARNINGS") - 1; +static constexpr size_t SQL_MODE_LEN = sizeof("@@sql_mode") - 1; +static constexpr size_t SET_NAMES_LEN = sizeof("SET NAMES") - 1; + #define EXPMARIA using std::function; @@ -1438,7 +1442,7 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { } // if query digest is disabled, warnings in ProxySQL are also deactivated, // resulting in an empty response being sent to the client. - if ((pkt->size == 18) && (strncasecmp((char*)"SHOW WARNINGS", (char*)pkt->ptr + 5, 13) == 0) && + if ((pkt->size == 18) && (strncasecmp((char*)"SHOW WARNINGS", (char*)pkt->ptr + 5, SHOW_WARNINGS_LEN) == 0) && CurrentQuery.QueryParserArgs.digest_text == nullptr) { SQLite3_result* resultset = new SQLite3_result(3); resultset->add_column_definition(SQLITE_TEXT, "Level"); @@ -6579,6 +6583,16 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE( } } + const size_t monitor_username_len = (mysql_thread___monitor_username ? strlen(mysql_thread___monitor_username) : 0); + const bool is_monitor_username = + (monitor_username_len > 0 && + strncmp( + client_myds->myconn->userinfo->username, + mysql_thread___monitor_username, + monitor_username_len + ) == 0 + ); + if ( //(client_myds->myprot.process_pkt_handshake_response((unsigned char *)pkt->ptr,pkt->size)==true) (handshake_response_return == true) @@ -6596,14 +6610,13 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE( (default_hostgroup>=0 && ( session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_SQLITE ) ) || ( - client_myds->encrypted==false - && - strncmp(client_myds->myconn->userinfo->username,mysql_thread___monitor_username,strlen(mysql_thread___monitor_username))==0 - ) - ) // Do not delete this line. See bug #492 - ) { - if (session_type == PROXYSQL_SESSION_ADMIN) { - if ( (default_hostgroup<0) || (strncmp(client_myds->myconn->userinfo->username,mysql_thread___monitor_username,strlen(mysql_thread___monitor_username))==0) ) { + client_myds->encrypted==false + && is_monitor_username + ) + ) // Do not delete this line. See bug #492 + ) { + if (session_type == PROXYSQL_SESSION_ADMIN) { + if ((default_hostgroup < 0) || is_monitor_username ) { if (default_hostgroup==STATS_HOSTGROUP) { session_type = PROXYSQL_SESSION_STATS; } @@ -7257,7 +7270,7 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C match_regexes && (match_regexes[1]->match(dig)) ) || - ( strncasecmp(dig,(char *)"SET NAMES", strlen((char *)"SET NAMES")) == 0) + ( strncasecmp(dig,(char *)"SET NAMES", SET_NAMES_LEN) == 0) || ( strcasestr(dig,(char *)"autocommit")) ) { @@ -7342,7 +7355,7 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C while (v1 && (v2 = strstr(v1,(const char *)"@"))) { // we found a @ . Maybe we need to lock hostgroup proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Found @ in SQL_MODE . v2 = %s\n", v2); - if (strncasecmp(v2,(const char *)"@@sql_mode",strlen((const char *)"@@sql_mode"))) { + if (strncasecmp(v2,(const char *)"@@sql_mode",SQL_MODE_LEN)) { unable_to_parse_set_statement(lock_hostgroup); free(v1); return false; @@ -7822,7 +7835,7 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C char *v2 = NULL; while (v1 && (v2 = strstr(v1,(const char *)"@"))) { // we found a @ . Maybe we need to lock hostgroup - if (strncasecmp(v2,(const char *)"@@sql_mode",strlen((const char *)"@@sql_mode"))) { + if (strncasecmp(v2,(const char *)"@@sql_mode",SQL_MODE_LEN)) { #ifdef DEBUG string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength); proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Locking hostgroup for query %s\n", nqn.c_str()); From c6a77a6dec2951b2e0c29da03c6dbe600a8c25be Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:35:56 +0000 Subject: [PATCH 114/227] MySQL_Query_Processor: replace strncpy with memcpy for min_gtid copy Replace the bounded copy in query processor first-comment handling with memcpy now that length is computed from strlen and null-termination is explicit. This keeps behavior identical while avoiding the legacy bounded-copy function path and matching S5801 cleanup intent. --- include/MySQL_Query_Processor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/MySQL_Query_Processor.h b/include/MySQL_Query_Processor.h index c63d1de226..b14a3400fa 100644 --- a/include/MySQL_Query_Processor.h +++ b/include/MySQL_Query_Processor.h @@ -81,7 +81,7 @@ class MySQL_Query_Processor : public Query_Processor { size_t l = strlen(value); if (_is_valid_gtid((char*)value, l)) { char* buf = (char*)malloc(l + 1); - strncpy(buf, value, l); + memcpy(buf, value, l); buf[l] = '\0'; if (qpo->min_gtid) { From 3a35222ea111744c1ae4523c2f4a0dd0b6942448 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:36:48 +0000 Subject: [PATCH 115/227] ProxySQL global vars: copy checksum using memcpy Replace fixed-length checksum copy in ProxySQL_Checksum_Value::set_checksum with memcpy since the destination buffer length is already constant-sized and pre-zeroed. This reduces use of legacy bounded copy API in hotspot cleanup while preserving truncation behavior and explicit post-processing via replace_checksum_zeros(). --- include/proxysql_glovars.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/proxysql_glovars.hpp b/include/proxysql_glovars.hpp index 104c2613c1..e3ba08981f 100644 --- a/include/proxysql_glovars.hpp +++ b/include/proxysql_glovars.hpp @@ -50,7 +50,7 @@ class ProxySQL_Checksum_Value { } void set_checksum(char *c) { memset(checksum,0,ProxySQL_Checksum_Value_LENGTH); - strncpy(checksum,c,ProxySQL_Checksum_Value_LENGTH); + memcpy(checksum,c,ProxySQL_Checksum_Value_LENGTH); replace_checksum_zeros(checksum); } ~ProxySQL_Checksum_Value() { From 36c76364964e937abde1be913928fdfed7e3e86d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:47:28 +0000 Subject: [PATCH 116/227] Use string_view lengths in FFTO state-machine test helpers --- test/tap/tests/unit/ffto_state_machine_unit-t.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/unit/ffto_state_machine_unit-t.cpp b/test/tap/tests/unit/ffto_state_machine_unit-t.cpp index 5256d9c5c5..090752ff47 100644 --- a/test/tap/tests/unit/ffto_state_machine_unit-t.cpp +++ b/test/tap/tests/unit/ffto_state_machine_unit-t.cpp @@ -9,6 +9,7 @@ #include #include +#include extern __thread int mysql_thread___ffto_max_buffer_size; extern __thread int pgsql_thread___ffto_max_buffer_size; @@ -92,7 +93,7 @@ static void test_pgsql_ffto_simple_query_message() { pgsql_thread___ffto_max_buffer_size = 16 * 1024 * 1024; PgSQLFFTO ffto(nullptr); const char* query = "SELECT 1"; - size_t qlen = strlen(query) + 1; + const size_t qlen = std::string_view(query).size() + 1; uint32_t msg_len = htonl((uint32_t)(qlen + 4)); std::vector msg(1 + 4 + qlen); msg[0] = 'Q'; @@ -124,7 +125,7 @@ static void test_pgsql_ffto_parse_message() { */ const char* query = "SELECT 1"; const size_t name_len = 1; /* empty name + NUL */ - const size_t query_len = strlen(query) + 1; + const size_t query_len = std::string_view(query).size() + 1; const size_t payload_len = name_len + query_len + 2; const uint32_t wire_len = (uint32_t)(4 + payload_len); uint32_t msg_len_be = htonl(wire_len); From 2c17b215cda4e0cf1f4349950da2d023fbe16175 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:47:33 +0000 Subject: [PATCH 117/227] Replace C-string strlen uses in parser SQL unit test inputs --- test/tap/tests/unit/parsersql_unit-t.cpp | 75 +++++++++++++----------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/test/tap/tests/unit/parsersql_unit-t.cpp b/test/tap/tests/unit/parsersql_unit-t.cpp index d7dc818115..bdf1c58e9a 100644 --- a/test/tap/tests/unit/parsersql_unit-t.cpp +++ b/test/tap/tests/unit/parsersql_unit-t.cpp @@ -6,14 +6,19 @@ #include #include +#include #include #include +static inline size_t str_view_len(const char *s) { + return s ? std::string_view{s}.size() : 0; +} + static void test_mysql_digest_select() { SQP_par_t qp; memset(&qp, 0, sizeof(qp)); const char* q = "SELECT * FROM t1 WHERE id = 1"; - parsersql_digest_init_mysql(&qp, q, strlen(q)); + parsersql_digest_init_mysql(&qp, q, str_view_len(q)); ok(qp.digest_text != NULL, "MySQL digest: SELECT produces digest_text"); ok(qp.digest != 0, "MySQL digest: SELECT produces non-zero hash"); if (qp.digest_text) { @@ -26,7 +31,7 @@ static void test_mysql_digest_insert() { SQP_par_t qp; memset(&qp, 0, sizeof(qp)); const char* q = "INSERT INTO t1 (a, b) VALUES (1, 'hello')"; - parsersql_digest_init_mysql(&qp, q, strlen(q)); + parsersql_digest_init_mysql(&qp, q, str_view_len(q)); ok(qp.digest_text != NULL, "MySQL digest: INSERT produces digest_text"); ok(qp.digest != 0, "MySQL digest: INSERT produces non-zero hash"); if (qp.digest_text) free(qp.digest_text); @@ -38,8 +43,8 @@ static void test_mysql_digest_same_for_different_literals() { memset(&qp2, 0, sizeof(qp2)); const char* q1 = "SELECT * FROM t1 WHERE id = 1"; const char* q2 = "SELECT * FROM t1 WHERE id = 999"; - parsersql_digest_init_mysql(&qp1, q1, strlen(q1)); - parsersql_digest_init_mysql(&qp2, q2, strlen(q2)); + parsersql_digest_init_mysql(&qp1, q1, str_view_len(q1)); + parsersql_digest_init_mysql(&qp2, q2, str_view_len(q2)); ok(qp1.digest == qp2.digest, "MySQL digest: same query with different literals produces same hash"); if (qp1.digest_text) free(qp1.digest_text); if (qp2.digest_text) free(qp2.digest_text); @@ -51,8 +56,8 @@ static void test_mysql_digest_different_queries() { memset(&qp2, 0, sizeof(qp2)); const char* q1 = "SELECT * FROM t1 WHERE id = 1"; const char* q2 = "SELECT * FROM t2 WHERE id = 1"; - parsersql_digest_init_mysql(&qp1, q1, strlen(q1)); - parsersql_digest_init_mysql(&qp2, q2, strlen(q2)); + parsersql_digest_init_mysql(&qp1, q1, str_view_len(q1)); + parsersql_digest_init_mysql(&qp2, q2, str_view_len(q2)); ok(qp1.digest != qp2.digest, "MySQL digest: different tables produce different hashes"); if (qp1.digest_text) free(qp1.digest_text); if (qp2.digest_text) free(qp2.digest_text); @@ -62,7 +67,7 @@ static void test_pgsql_digest_select() { SQP_par_t qp; memset(&qp, 0, sizeof(qp)); const char* q = "SELECT * FROM t1 WHERE id = 1"; - parsersql_digest_init_pgsql(&qp, q, strlen(q)); + parsersql_digest_init_pgsql(&qp, q, str_view_len(q)); ok(qp.digest_text != NULL, "PgSQL digest: SELECT produces digest_text"); ok(qp.digest != 0, "PgSQL digest: SELECT produces non-zero hash"); if (qp.digest_text) free(qp.digest_text); @@ -74,8 +79,8 @@ static void test_pgsql_digest_same_for_different_literals() { memset(&qp2, 0, sizeof(qp2)); const char* q1 = "SELECT * FROM t1 WHERE id = 1"; const char* q2 = "SELECT * FROM t1 WHERE id = 999"; - parsersql_digest_init_pgsql(&qp1, q1, strlen(q1)); - parsersql_digest_init_pgsql(&qp2, q2, strlen(q2)); + parsersql_digest_init_pgsql(&qp1, q1, str_view_len(q1)); + parsersql_digest_init_pgsql(&qp2, q2, str_view_len(q2)); ok(qp1.digest == qp2.digest, "PgSQL digest: same query with different literals produces same hash"); if (qp1.digest_text) free(qp1.digest_text); if (qp2.digest_text) free(qp2.digest_text); @@ -83,145 +88,145 @@ static void test_pgsql_digest_same_for_different_literals() { static void test_mysql_command_type_select() { const char* q = "SELECT 1"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_SELECT, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_SELECT, "MySQL cmd: SELECT → SELECT"); } static void test_mysql_command_type_insert() { const char* q = "INSERT INTO t1 VALUES (1)"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_INSERT, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_INSERT, "MySQL cmd: INSERT → INSERT"); } static void test_mysql_command_type_update() { const char* q = "UPDATE t1 SET a = 1"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_UPDATE, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_UPDATE, "MySQL cmd: UPDATE → UPDATE"); } static void test_mysql_command_type_delete() { const char* q = "DELETE FROM t1 WHERE id = 1"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_DELETE, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_DELETE, "MySQL cmd: DELETE → DELETE"); } static void test_mysql_command_type_set() { const char* q = "SET @a = 1"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_SET, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_SET, "MySQL cmd: SET → SET"); } static void test_mysql_command_type_begin() { const char* q = "BEGIN"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_BEGIN, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_BEGIN, "MySQL cmd: BEGIN → BEGIN"); } static void test_mysql_command_type_commit() { const char* q = "COMMIT"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_COMMIT, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_COMMIT, "MySQL cmd: COMMIT → COMMIT"); } static void test_mysql_command_type_rollback() { const char* q = "ROLLBACK"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_ROLLBACK, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_ROLLBACK, "MySQL cmd: ROLLBACK → ROLLBACK"); } static void test_mysql_command_type_create_table() { const char* q = "CREATE TABLE t1 (id INT)"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_CREATE_TABLE, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_CREATE_TABLE, "MySQL cmd: CREATE TABLE → CREATE_TABLE"); } static void test_mysql_command_type_drop_table() { const char* q = "DROP TABLE t1"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_DROP_TABLE, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_DROP_TABLE, "MySQL cmd: DROP TABLE → DROP_TABLE"); } static void test_mysql_command_type_show() { const char* q = "SHOW TABLES"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_SHOW, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_SHOW, "MySQL cmd: SHOW → SHOW"); } static void test_mysql_command_type_use() { const char* q = "USE mydb"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_USE, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_USE, "MySQL cmd: USE → USE"); } static void test_mysql_command_type_prepare() { const char* q = "PREPARE stmt FROM 'SELECT 1'"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_PREPARE, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_PREPARE, "MySQL cmd: PREPARE → PREPARE"); } static void test_mysql_command_type_explain() { const char* q = "EXPLAIN SELECT 1"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_EXPLAIN, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_EXPLAIN, "MySQL cmd: EXPLAIN → EXPLAIN"); } static void test_pgsql_command_type_select() { const char* q = "SELECT 1"; - ok(parsersql_command_type_pgsql(q, strlen(q)) == PGSQL_QUERY_SELECT, + ok(parsersql_command_type_pgsql(q, str_view_len(q)) == PGSQL_QUERY_SELECT, "PgSQL cmd: SELECT → SELECT"); } static void test_pgsql_command_type_insert() { const char* q = "INSERT INTO t1 VALUES (1)"; - ok(parsersql_command_type_pgsql(q, strlen(q)) == PGSQL_QUERY_INSERT, + ok(parsersql_command_type_pgsql(q, str_view_len(q)) == PGSQL_QUERY_INSERT, "PgSQL cmd: INSERT → INSERT"); } static void test_pgsql_command_type_update() { const char* q = "UPDATE t1 SET a = 1"; - ok(parsersql_command_type_pgsql(q, strlen(q)) == PGSQL_QUERY_UPDATE, + ok(parsersql_command_type_pgsql(q, str_view_len(q)) == PGSQL_QUERY_UPDATE, "PgSQL cmd: UPDATE → UPDATE"); } static void test_pgsql_command_type_delete() { const char* q = "DELETE FROM t1 WHERE id = 1"; - ok(parsersql_command_type_pgsql(q, strlen(q)) == PGSQL_QUERY_DELETE, + ok(parsersql_command_type_pgsql(q, str_view_len(q)) == PGSQL_QUERY_DELETE, "PgSQL cmd: DELETE → DELETE"); } static void test_pgsql_command_type_set() { const char* q = "SET search_path TO public"; - ok(parsersql_command_type_pgsql(q, strlen(q)) == PGSQL_QUERY_SET, + ok(parsersql_command_type_pgsql(q, str_view_len(q)) == PGSQL_QUERY_SET, "PgSQL cmd: SET → SET"); } static void test_pgsql_command_type_begin() { const char* q = "BEGIN"; - ok(parsersql_command_type_pgsql(q, strlen(q)) == PGSQL_QUERY_BEGIN, + ok(parsersql_command_type_pgsql(q, str_view_len(q)) == PGSQL_QUERY_BEGIN, "PgSQL cmd: BEGIN → BEGIN"); } static void test_pgsql_command_type_commit() { const char* q = "COMMIT"; - ok(parsersql_command_type_pgsql(q, strlen(q)) == PGSQL_QUERY_COMMIT, + ok(parsersql_command_type_pgsql(q, str_view_len(q)) == PGSQL_QUERY_COMMIT, "PgSQL cmd: COMMIT → COMMIT"); } static void test_pgsql_command_type_show() { const char* q = "SHOW search_path"; - ok(parsersql_command_type_pgsql(q, strlen(q)) == PGSQL_QUERY_SHOW, + ok(parsersql_command_type_pgsql(q, str_view_len(q)) == PGSQL_QUERY_SHOW, "PgSQL cmd: SHOW → SHOW"); } static void test_pgsql_command_type_truncate() { const char* q = "TRUNCATE TABLE t1"; - ok(parsersql_command_type_pgsql(q, strlen(q)) == PGSQL_QUERY_TRUNCATE, + ok(parsersql_command_type_pgsql(q, str_view_len(q)) == PGSQL_QUERY_TRUNCATE, "PgSQL cmd: TRUNCATE → TRUNCATE"); } static void test_pgsql_command_type_reset() { const char* q = "RESET ALL"; - ok(parsersql_command_type_pgsql(q, strlen(q)) == PGSQL_QUERY_RESET, + ok(parsersql_command_type_pgsql(q, str_view_len(q)) == PGSQL_QUERY_RESET, "PgSQL cmd: RESET → RESET"); } @@ -289,13 +294,13 @@ static void test_mysql_digest_empty_query() { static void test_mysql_command_type_unknown() { const char* q = "THISISNOTASQLCOMMAND"; - ok(parsersql_command_type_mysql(q, strlen(q)) == MYSQL_COM_QUERY_UNKNOWN, + ok(parsersql_command_type_mysql(q, str_view_len(q)) == MYSQL_COM_QUERY_UNKNOWN, "MySQL cmd: garbage → UNKNOWN"); } static void test_pgsql_command_type_unknown() { const char* q = "THISISNOTASQLCOMMAND"; - ok(parsersql_command_type_pgsql(q, strlen(q)) == PGSQL_QUERY_UNKNOWN, + ok(parsersql_command_type_pgsql(q, str_view_len(q)) == PGSQL_QUERY_UNKNOWN, "PgSQL cmd: garbage → UNKNOWN"); } From 9981293b39d6bf0c907f6e6d07934d13f230ad05 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:48:38 +0000 Subject: [PATCH 118/227] bench_connect: copy conninfo with bounded snprintf --- tools/bench_connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/bench_connect.c b/tools/bench_connect.c index e972ecb8de..709a6b5332 100644 --- a/tools/bench_connect.c +++ b/tools/bench_connect.c @@ -130,7 +130,7 @@ int main(int argc, char **argv) { // NOSONAR: benchmark tool, cognitive complexi for (int t = 0; t < threads; t++) { args[t].thread_id = t; - strncpy(args[t].conninfo, conninfo, sizeof(args[t].conninfo) - 1); + snprintf(args[t].conninfo, sizeof(args[t].conninfo), "%s", conninfo); args[t].iterations = iterations; args[t].warmup = warmup; pthread_create(&tids[t], NULL, thread_worker, &args[t]); From a296a778fc326dc81c8869d59bf3ac1f8965d9dd Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:48:43 +0000 Subject: [PATCH 119/227] reg_test_1288: safely copy pgsql max_connections value --- .../tap/tests/reg_test_1288-load-pgsql-variables-feedback-t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/reg_test_1288-load-pgsql-variables-feedback-t.cpp b/test/tap/tests/reg_test_1288-load-pgsql-variables-feedback-t.cpp index d48a7d311e..5dcee7a37b 100644 --- a/test/tap/tests/reg_test_1288-load-pgsql-variables-feedback-t.cpp +++ b/test/tap/tests/reg_test_1288-load-pgsql-variables-feedback-t.cpp @@ -41,7 +41,7 @@ int main(int argc, char** argv) { if (snap) { MYSQL_ROW row = mysql_fetch_row(snap); if (row && row[0]) { - strncpy(original_pgsql_max_conn, row[0], sizeof(original_pgsql_max_conn) - 1); + snprintf(original_pgsql_max_conn, sizeof(original_pgsql_max_conn), "%s", row[0]); } mysql_free_result(snap); } From 100770723334827ec98ee6fa36dbeada52a7c393 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:48:48 +0000 Subject: [PATCH 120/227] aurora: avoid repeated strlen in quoted table parsing --- test/tap/tests/aurora.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/tap/tests/aurora.cpp b/test/tap/tests/aurora.cpp index ea155edb1f..6e5c77deb7 100644 --- a/test/tap/tests/aurora.cpp +++ b/test/tap/tests/aurora.cpp @@ -372,10 +372,12 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p tbh=dbh; dbh=strdup("main"); } - if (strlen(tbh)>=3 && tbh[0]=='`' && tbh[strlen(tbh)-1]=='`') { // tablename is quoted - char *tbh_tmp=(char *)malloc(strlen(tbh)-1); - strncpy(tbh_tmp,tbh+1,strlen(tbh)-2); - tbh_tmp[strlen(tbh)-2]=0; + size_t tbh_len = strlen(tbh); + if (tbh_len>=3 && tbh[0]=='`' && tbh[tbh_len-1]=='`') { // tablename is quoted + size_t db_len = tbh_len - 2; + char *tbh_tmp=(char *)malloc(db_len+1); + memcpy(tbh_tmp,tbh+1,db_len); + tbh_tmp[db_len]=0; free(tbh); tbh=tbh_tmp; } From c0b458cfa566fc2c7fdec915fddcd9563c0b9aa3 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:48:54 +0000 Subject: [PATCH 121/227] pgsql copy test: bound numeric buffer copies --- test/tap/tests/pgsql-copy_from_test-t.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/pgsql-copy_from_test-t.cpp b/test/tap/tests/pgsql-copy_from_test-t.cpp index 8f3832be74..1a86d10624 100644 --- a/test/tap/tests/pgsql-copy_from_test-t.cpp +++ b/test/tap/tests/pgsql-copy_from_test-t.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -153,7 +154,9 @@ bool encodeNumericBinary(uint8_t* out, const char* numStr) { // Combine integer and fractional parts into a single string of digits char combined[128] = { 0 }; - strncpy(combined, numericPart, intPartLen); + size_t copy_len = std::min(intPartLen, sizeof(combined)-1); + memcpy(combined, numericPart, copy_len); + combined[copy_len] = 0; if (fracPartLen > 0) { strncat(combined, dotPos + 1, fracPartLen); } @@ -173,7 +176,8 @@ bool encodeNumericBinary(uint8_t* out, const char* numStr) { // Parse the padded string into 4-digit groups for (size_t i = 0; i < paddedLen; i += 4) { char group[5] = { 0 }; // Temporary buffer for a group of up to 4 digits - strncpy(group, combined + i, 4); + memcpy(group, combined + i, 4); + group[4] = 0; digits[digitCount++] = static_cast(htons(static_cast(atoi(group)))); // Convert group to 16-bit integer } From 14953f9751c375830932cd57a4ecf0b7526a5b15 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:48:59 +0000 Subject: [PATCH 122/227] test_cluster_sync: use bounded host copy fallback for hostname buffer --- test/tap/tests/test_cluster_sync-t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/test_cluster_sync-t.cpp b/test/tap/tests/test_cluster_sync-t.cpp index 4b2be03655..10b82af49d 100644 --- a/test/tap/tests/test_cluster_sync-t.cpp +++ b/test/tap/tests/test_cluster_sync-t.cpp @@ -121,7 +121,7 @@ const char* get_cluster_visible_host() { static char buf[256] = {}; if (buf[0]) return buf; if (gethostname(buf, sizeof(buf)) != 0) { - strncpy(buf, R_HOST, sizeof(buf) - 1); + snprintf(buf, sizeof(buf), "%s", R_HOST); } return buf; } From 6d59412a0c32d02dbdbc90a0141905647eed3f0f Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:49:06 +0000 Subject: [PATCH 123/227] stmt error test: bound parameter copy and length assignment --- test/tap/tests/reg_test_stmt_resultset_err_no_rows-t.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/reg_test_stmt_resultset_err_no_rows-t.cpp b/test/tap/tests/reg_test_stmt_resultset_err_no_rows-t.cpp index f02fc7fd95..a357884470 100644 --- a/test/tap/tests/reg_test_stmt_resultset_err_no_rows-t.cpp +++ b/test/tap/tests/reg_test_stmt_resultset_err_no_rows-t.cpp @@ -106,8 +106,13 @@ int main(int argc, char** argv) { goto exit; } - strncpy(str_data, param.c_str(), STRING_SIZE); - str_length = strlen(str_data); + size_t copy_len = param.size(); + if (copy_len >= STRING_SIZE) { + copy_len = STRING_SIZE - 1; + } + memcpy(str_data, param.data(), copy_len); + str_data[copy_len] = 0; + str_length = copy_len; int exec_res = mysql_stmt_execute(stmt); if (exec_res) { From f2b82225d2611019bd4f3af510600c580aa6248c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:49:10 +0000 Subject: [PATCH 124/227] cluster sync mysql servers: fallback host copy with snprintf --- test/tap/tests/test_cluster_sync_mysql_servers-t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/test_cluster_sync_mysql_servers-t.cpp b/test/tap/tests/test_cluster_sync_mysql_servers-t.cpp index d653a762ca..158a7e9257 100644 --- a/test/tap/tests/test_cluster_sync_mysql_servers-t.cpp +++ b/test/tap/tests/test_cluster_sync_mysql_servers-t.cpp @@ -75,7 +75,7 @@ const char* get_cluster_visible_host() { static char buf[256] = {}; if (buf[0]) return buf; if (gethostname(buf, sizeof(buf)) != 0) { - strncpy(buf, "127.0.0.1", sizeof(buf) - 1); + snprintf(buf, sizeof(buf), "%s", "127.0.0.1"); } return buf; } From e6c43eef0bab50b8560b5068e40eef9067bef08c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:50:57 +0000 Subject: [PATCH 125/227] test: replace unsafe strcat usage in pgsql copy-from test Replace remaining strncat/strcat in COPY FROM row reconstruction with bounded formatting to avoid unchecked buffer growth and satisfy Sonar S5801 guidance. --- test/tap/tests/pgsql-copy_from_test-t.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/test/tap/tests/pgsql-copy_from_test-t.cpp b/test/tap/tests/pgsql-copy_from_test-t.cpp index 1a86d10624..6da122a177 100644 --- a/test/tap/tests/pgsql-copy_from_test-t.cpp +++ b/test/tap/tests/pgsql-copy_from_test-t.cpp @@ -158,7 +158,10 @@ bool encodeNumericBinary(uint8_t* out, const char* numStr) { memcpy(combined, numericPart, copy_len); combined[copy_len] = 0; if (fracPartLen > 0) { - strncat(combined, dotPos + 1, fracPartLen); + size_t combined_len = strlen(combined); + size_t copy_len_frac = std::min(fracPartLen, sizeof(combined) - combined_len - 1); + memcpy(combined + combined_len, dotPos + 1, copy_len_frac); + combined[combined_len + copy_len_frac] = 0; } // Remove leading zeros @@ -274,12 +277,21 @@ int is_string_in_result(PGresult* result, const char* target_str) { // Reconstruct the row string (with tab and newline separators) for (int j = 0; j < cols; j++) { char* val = PQgetvalue(result, i, j); - strcat(full_row_str, val); - if (j < cols - 1) { - strcat(full_row_str, "\t"); + size_t current_len = strlen(full_row_str); + size_t space_left = sizeof(full_row_str) - current_len; + if (space_left == 0) { + break; } + int nwritten = snprintf(full_row_str + current_len, space_left, "%s%s", val, (j < cols - 1) ? "\t" : ""); + if (nwritten < 0 || (size_t)nwritten >= space_left) { + break; + } + } + size_t current_len = strlen(full_row_str); + size_t space_left = sizeof(full_row_str) - current_len; + if (space_left > 1) { + snprintf(full_row_str + current_len, space_left, "\n"); } - strcat(full_row_str, "\n"); // Compare reconstructed row string with target if (strcmp(full_row_str, target_str) == 0) { From 6d623a082a0ad19adbbc957d18663556e9a657f1 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:53:42 +0000 Subject: [PATCH 126/227] src: bound-format TLS path construction Replace sprintf path concatenation in ssl file path setup with bounded snprintf to remove fixed-size overflow-prone writes under S5801/Snprintf hotspot rules. --- src/proxy_tls.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proxy_tls.cpp b/src/proxy_tls.cpp index 38acc553fa..3703cccd83 100644 --- a/src/proxy_tls.cpp +++ b/src/proxy_tls.cpp @@ -234,7 +234,7 @@ int ssl_mkit(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int days, boo // check if files exists if (bootstrap == true) { ssl_key_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_key_rp)+8); - sprintf(ssl_key_fp,"%s/%s",GloVars.datadir,ssl_key_rp); + snprintf(ssl_key_fp, strlen(GloVars.datadir)+strlen(ssl_key_rp)+2, "%s/%s",GloVars.datadir,ssl_key_rp); } if (access(ssl_key_fp, R_OK)) { ssl_key_exists = false; @@ -242,7 +242,7 @@ int ssl_mkit(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int days, boo if (bootstrap == true) { ssl_cert_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_cert_rp)+8); - sprintf(ssl_cert_fp,"%s/%s",GloVars.datadir,ssl_cert_rp); + snprintf(ssl_cert_fp, strlen(GloVars.datadir)+strlen(ssl_cert_rp)+2, "%s/%s",GloVars.datadir,ssl_cert_rp); } if (access(ssl_cert_fp, R_OK)) { ssl_cert_exists = false; @@ -250,7 +250,7 @@ int ssl_mkit(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int days, boo if (bootstrap == true) { ssl_ca_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_ca_rp)+8); - sprintf(ssl_ca_fp,"%s/%s",GloVars.datadir,ssl_ca_rp); + snprintf(ssl_ca_fp, strlen(GloVars.datadir)+strlen(ssl_ca_rp)+2, "%s/%s",GloVars.datadir,ssl_ca_rp); } if (access(ssl_ca_fp, R_OK)) { ssl_ca_exists = false; From e2d4839393334988f62b6df680439cf2eae7333e Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:53:59 +0000 Subject: [PATCH 127/227] src: replace unbounded sprintf in main path formatting Convert remaining sprintf calls in main initialization to bounded snprintf for derived file paths and token formatting to reduce overflow exposure. --- src/main.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 6cc8ed8dc4..88f3916234 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -890,28 +890,28 @@ void ProxySQL_Main_process_global_variables(int argc, const char **argv) { free(t); GloVars.admindb=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql.db")+2); - sprintf(GloVars.admindb,"%s/%s",GloVars.datadir, (char *)"proxysql.db"); + snprintf(GloVars.admindb, strlen(GloVars.datadir)+strlen((char *)"proxysql.db")+2, "%s/%s", GloVars.datadir, (char *)"proxysql.db"); GloVars.sqlite3serverdb=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"sqlite3server.db")+2); - sprintf(GloVars.sqlite3serverdb,"%s/%s",GloVars.datadir, (char *)"sqlite3server.db"); + snprintf(GloVars.sqlite3serverdb, strlen(GloVars.datadir)+strlen((char *)"sqlite3server.db")+2, "%s/%s", GloVars.datadir, (char *)"sqlite3server.db"); GloVars.statsdb_disk=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql_stats.db")+2); - sprintf(GloVars.statsdb_disk,"%s/%s",GloVars.datadir, (char *)"proxysql_stats.db"); + snprintf(GloVars.statsdb_disk, strlen(GloVars.datadir)+strlen((char *)"proxysql_stats.db")+2, "%s/%s", GloVars.datadir, (char *)"proxysql_stats.db"); if (GloVars.errorlog == NULL) { GloVars.errorlog=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql.log")+2); - sprintf(GloVars.errorlog,"%s/%s",GloVars.datadir, (char *)"proxysql.log"); + snprintf(GloVars.errorlog, strlen(GloVars.datadir)+strlen((char *)"proxysql.log")+2, "%s/%s", GloVars.datadir, (char *)"proxysql.log"); } if (GloVars.pid == NULL) { GloVars.pid=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql.pid")+2); - sprintf(GloVars.pid,"%s/%s",GloVars.datadir, (char *)"proxysql.pid"); + snprintf(GloVars.pid, strlen(GloVars.datadir)+strlen((char *)"proxysql.pid")+2, "%s/%s", GloVars.datadir, (char *)"proxysql.pid"); } if (GloVars.__cmd_proxysql_initial==true) { std::cerr << "Renaming database file " << GloVars.admindb << endl; char *newpath=(char *)malloc(strlen(GloVars.admindb)+8); - sprintf(newpath,"%s.bak",GloVars.admindb); + snprintf(newpath, strlen(GloVars.admindb)+5, "%s.bak", GloVars.admindb); rename(GloVars.admindb,newpath); // FIXME: should we check return value, or ignore whatever it successed or not? } @@ -2047,7 +2047,7 @@ bool ProxySQL_daemonize_phase3() { if (GloVars.__cmd_proxysql_initial==true) { std::cerr << "Renaming database file " << GloVars.admindb << endl; char *newpath=(char *)malloc(strlen(GloVars.admindb)+8); - sprintf(newpath,"%s.bak",GloVars.admindb); + snprintf(newpath, strlen(GloVars.admindb)+5, "%s.bak", GloVars.admindb); rename(GloVars.admindb,newpath); // FIXME: should we check return value, or ignore whatever it successed or not? } parent_close_error_log(); @@ -3146,7 +3146,7 @@ int main(int argc, const char * argv[]) { memset(binary_sha1, 0, SHA_DIGEST_LENGTH*2+1); char buf[SHA_DIGEST_LENGTH*2 + 1]; for (int i=0; i < SHA_DIGEST_LENGTH; i++) { - sprintf((char*)&(buf[i*2]), "%02x", temp[i]); + snprintf((char*)&(buf[i*2]), 3, "%02x", temp[i]); } memcpy(binary_sha1, buf, SHA_DIGEST_LENGTH*2); munmap(fb,statbuf.st_size); From 3d3f438e09c78ba82a2a6f8652d1f25b4a9fcf94 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:54:38 +0000 Subject: [PATCH 128/227] genai: replace unbounded MCP variable copy with bounded snprintf Use a fixed output size when writing MCP string variable values into the caller-provided buffer to avoid unbounded sprintf behavior while preserving behavior and allocation assumptions for this handler path. --- plugins/genai/src/MCP_Thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/genai/src/MCP_Thread.cpp b/plugins/genai/src/MCP_Thread.cpp index ceace02026..c516a30347 100644 --- a/plugins/genai/src/MCP_Thread.cpp +++ b/plugins/genai/src/MCP_Thread.cpp @@ -210,7 +210,7 @@ int MCP_Threads_Handler::get_variable(const char* name, char* val) { pthread_rwlock_unlock(&rwlock); if (rc == 0) { - sprintf(val, "%s", out.c_str()); + snprintf(val, 1024, "%s", out.c_str()); } return rc; } From fbf661cce0c85ea01545e7fb019bb940daab8a62 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:54:47 +0000 Subject: [PATCH 129/227] sqlite: replace unsafe sprintf calls with bounded snprintf Switch remaining SQLite test/query string constructors away from sprintf to bounded formatting, including user-derived and numeric values, without changing SQL strings or execution flow. --- src/SQLite3_Server.cpp | 98 +++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 6816328950..3a9ce021db 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -603,17 +603,19 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } if (query_no_space_length==SELECT_DB_USER_LEN) { - if (!strncasecmp(SELECT_DB_USER, query_no_space, query_no_space_length)) { - l_free(query_length,query); - char *query1=(char *)"SELECT \"admin\" AS 'DATABASE()', \"%s\" AS 'USER()'"; - char *query2=(char *)malloc(strlen(query1)+strlen(sess->client_myds->myconn->userinfo->username)+10); - sprintf(query2,query1,sess->client_myds->myconn->userinfo->username); - query=l_strdup(query2); - query_length=strlen(query2)+1; - free(query2); - goto __run_query; + if (!strncasecmp(SELECT_DB_USER, query_no_space, query_no_space_length)) { + l_free(query_length,query); + char *query1=(char *)"SELECT \"admin\" AS 'DATABASE()', \"%s\" AS 'USER()'"; + const char* username = sess->client_myds->myconn->userinfo->username; + size_t query2_length = strlen(query1) + (username ? strlen(username) : 0) + 1; + char *query2=(char *)malloc(query2_length); + snprintf(query2, query2_length, query1, username ? username : ""); + query=l_strdup(query2); + query_length=strlen(query2)+1; + free(query2); + goto __run_query; + } } - } if (query_no_space_length==SELECT_CHARSET_VARIOUS_LEN) { if (!strncasecmp(SELECT_CHARSET_VARIOUS, query_no_space, query_no_space_length)) { @@ -625,23 +627,23 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } } - if (!strncasecmp("SELECT @@version", query_no_space, k_select_version_len)) { - l_free(query_length,query); - char *q=(char *)"SELECT '%s' AS '@@version'"; - query_length=strlen(q)+strlen(PROXYSQL_VERSION)+20; - query=(char *)l_alloc(query_length); - sprintf(query,q,PROXYSQL_VERSION); - goto __run_query; - } + if (!strncasecmp("SELECT @@version", query_no_space, k_select_version_len)) { + l_free(query_length,query); + char *q=(char *)"SELECT '%s' AS '@@version'"; + query_length=strlen(q)+strlen(PROXYSQL_VERSION)+20; + query=(char *)l_alloc(query_length); + snprintf(query, query_length, q, PROXYSQL_VERSION); + goto __run_query; + } - if (!strncasecmp("SELECT version()", query_no_space, k_select_version_fn_len)) { - l_free(query_length,query); - char *q=(char *)"SELECT '%s' AS 'version()'"; - query_length=strlen(q)+strlen(PROXYSQL_VERSION)+20; - query=(char *)l_alloc(query_length); - sprintf(query,q,PROXYSQL_VERSION); - goto __run_query; - } + if (!strncasecmp("SELECT version()", query_no_space, k_select_version_fn_len)) { + l_free(query_length,query); + char *q=(char *)"SELECT '%s' AS 'version()'"; + query_length=strlen(q)+strlen(PROXYSQL_VERSION)+20; + query=(char *)l_alloc(query_length); + snprintf(query, query_length, q, PROXYSQL_VERSION); + goto __run_query; + } // MySQL client check command for dollars quote support, starting at version '8.1.0'. See #4300. if (!strncasecmp("SELECT $$", query_no_space, k_select_dollar_len)) { @@ -982,19 +984,19 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p if (strncasecmp("SELECT @@global.read_only read_only ",query_no_space, k_select_read_only_len)==0) { if (strlen(query_no_space) > k_select_read_only_len+5) { pthread_mutex_lock(&GloSQLite3Server->test_readonly_mutex); - // the current test doesn't try to simulate failures, therefore it will return immediately - if (GloSQLite3Server->readonly_map_size() == 0) { - // probably never initialized - GloSQLite3Server->load_readonly_table(sess); + // the current test doesn't try to simulate failures, therefore it will return immediately + if (GloSQLite3Server->readonly_map_size() == 0) { + // probably never initialized + GloSQLite3Server->load_readonly_table(sess); + } + int rc = GloSQLite3Server->readonly_test_value(query_no_space+k_select_read_only_len); + free(query); + char *a = (char *)"SELECT %d as read_only"; + query = (char *)malloc(strlen(a)+2); + snprintf(query, strlen(a)+2, a, rc); + pthread_mutex_unlock(&GloSQLite3Server->test_readonly_mutex); } - int rc = GloSQLite3Server->readonly_test_value(query_no_space+k_select_read_only_len); - free(query); - char *a = (char *)"SELECT %d as read_only"; - query = (char *)malloc(strlen(a)+2); - sprintf(query,a,rc); - pthread_mutex_unlock(&GloSQLite3Server->test_readonly_mutex); } - } #endif // TEST_READONLY || TEST_RDS_BGD #ifdef TEST_REPLICATIONLAG if ( @@ -1014,23 +1016,23 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p const int* rc = GloSQLite3Server->replicationlag_test_value(query_no_space + addr_offset); free(query); - string SELECT { "SELECT " + (rc ? std::to_string(*rc) : string { "null" }) + " AS " }; - SELECT += strstr(query_no_space, "REPLICA") ? "Seconds_Behind_Source" : "Seconds_Behind_Master"; + string SELECT { "SELECT " + (rc ? std::to_string(*rc) : string { "null" }) + " AS " }; + SELECT += strstr(query_no_space, "REPLICA") ? "Seconds_Behind_Source" : "Seconds_Behind_Master"; - query = static_cast(malloc(SELECT.size() + 1)); - sprintf(query, SELECT.c_str()); + query = static_cast(malloc(SELECT.size() + 1)); + snprintf(query, SELECT.size() + 1, "%s", SELECT.c_str()); pthread_mutex_unlock(&GloSQLite3Server->test_replicationlag_mutex); } } #endif // TEST_REPLICATIONLAG - if (strstr(query_no_space,(char *)"Seconds_Behind_Master")) { - free(query); - char *a = (char *)"SELECT %d as Seconds_Behind_Master"; - query = (char *)malloc(strlen(a)+4); - sprintf(query,a,rand()%30+10); + if (strstr(query_no_space,(char *)"Seconds_Behind_Master")) { + free(query); + char *a = (char *)"SELECT %d as Seconds_Behind_Master"; + query = (char *)malloc(strlen(a)+4); + snprintf(query, strlen(a)+4, a, rand()%30+10); + } } - } #endif // TEST_AURORA || TEST_GALERA || TEST_GROUPREP || TEST_READONLY || TEST_REPLICATIONLAG || TEST_RDS_BGD if (!run_query) { l_free(pkt->size-sizeof(mysql_hdr),query_no_space); @@ -1567,7 +1569,7 @@ void SQLite3_Server::populate_galera_table(MySQL_Session *sess) { cluster_id--; int hg_id = 2270+(cluster_id*10)+1; char buf[1024]; - sprintf(buf, (char *)"SELECT * FROM HOST_STATUS_GALERA WHERE hostgroup_id = %d LIMIT 1", hg_id); + snprintf(buf, sizeof(buf), "SELECT * FROM HOST_STATUS_GALERA WHERE hostgroup_id = %d LIMIT 1", hg_id); sessdb->execute_statement(buf, &error , &cols , &affected_rows , &resultset); if (resultset->rows_count==0) { //sessdb->execute("DELETE FROM HOST_STATUS_GALERA"); From 37e27540502f6ccba8a16f9d10cc04ad2a132396 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:55:26 +0000 Subject: [PATCH 130/227] tools: bound eventslog sample formatting buffers Replace unsafe sprintf usage in eventslog_reader_sample output formatting with bounded snprintf while preserving existing output fields. --- tools/eventslog_reader_sample.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/eventslog_reader_sample.cpp b/tools/eventslog_reader_sample.cpp index 44377c2405..bd1ae1103a 100644 --- a/tools/eventslog_reader_sample.cpp +++ b/tools/eventslog_reader_sample.cpp @@ -170,7 +170,7 @@ class MySQL_Event { read_encoded_length((uint64_t *)&rows_sent,f); read_encoded_length((uint64_t *)&query_digest,f); char digest_hex[20]; - sprintf(digest_hex,"0x%016llX", (long long unsigned int)query_digest); + snprintf(digest_hex, sizeof(digest_hex), "0x%016llX", (long long unsigned int)query_digest); read_encoded_length((uint64_t *)&query_len,f); query_ptr=read_string(f,query_len); char buffer[26]; @@ -180,12 +180,12 @@ class MySQL_Event { timer=start_time/1000/1000; tm_info = localtime(&timer); strftime(buffer, 26, "%Y-%m-%d %H:%M:%S", tm_info); - sprintf(buffer2,"%06u", (unsigned)(start_time%1000000)); + snprintf(buffer2, sizeof(buffer2), "%06u", (unsigned)(start_time%1000000)); cout << " starttime=\"" << buffer << "." << buffer2 << "\""; timer=end_time/1000/1000; tm_info = localtime(&timer); strftime(buffer, 26, "%Y-%m-%d %H:%M:%S", tm_info); - sprintf(buffer2,"%06u", (unsigned)(end_time%1000000)); + snprintf(buffer2, sizeof(buffer2), "%06u", (unsigned)(end_time%1000000)); cout << " endtime=\"" << buffer << "." << buffer2 << "\""; cout << " duration=" << (end_time-start_time) << "us"; if (et == PROXYSQL_COM_STMT_PREPARE || et == PROXYSQL_COM_STMT_EXECUTE) { From a51612a92616c34f6979c34d3e799842ec0ba6bd Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:55:38 +0000 Subject: [PATCH 131/227] core: make itostr integer-to-string conversion bounded Update the itostr macro to use snprintf with an explicit 32-byte destination bound instead of unbounded sprintf while preserving allocation behavior. --- include/proxysql_macros.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/proxysql_macros.h b/include/proxysql_macros.h index 5aa3f55d70..29c8337acc 100644 --- a/include/proxysql_macros.h +++ b/include/proxysql_macros.h @@ -4,7 +4,7 @@ #define char_malloc (char *)malloc #define free_null(__c) { if(__c) { free(__c); (__c)=NULL; } } -#define itostr(__s, __i) { (__s)=char_malloc(32); sprintf(__s, "%lld", (__i)); } +#define itostr(__s, __i) { (__s)=char_malloc(32); snprintf((__s), 32, "%lld", (__i)); } // fast memory copy forward . Use this instead of memcpy for small buffers #define MEM_COPY_FWD(dst_p, src_p, bytes) \ From bd2d7be3e4b74477defad3f532b643ffefd7c899 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:56:24 +0000 Subject: [PATCH 132/227] tests: bound-format aurora sqlite3 query builders Replace remaining sprintf calls in test/tap/tests/aurora.cpp with bounded snprintf while keeping SQL formatting behavior for version, user, and replication helper queries. --- test/tap/tests/aurora.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/test/tap/tests/aurora.cpp b/test/tap/tests/aurora.cpp index 6e5c77deb7..f5c5a81e2a 100644 --- a/test/tap/tests/aurora.cpp +++ b/test/tap/tests/aurora.cpp @@ -257,8 +257,10 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p if (!strncasecmp(SELECT_VERSION_COMMENT, query_no_space, query_no_space_length)) { l_free(query_length,query); char *a = (char *)"SELECT '(ProxySQL Automated Test Server) - %s'"; - query = (char *)malloc(strlen(a)+strlen(sess->client_myds->proxy_addr.addr)); - sprintf(query,a,sess->client_myds->proxy_addr.addr); + const char* proxy_addr = sess->client_myds->proxy_addr.addr; + size_t query_len = strlen(a) + (proxy_addr ? strlen(proxy_addr) : 0); + query = (char *)malloc(query_len); + snprintf(query, query_len, a, proxy_addr ? proxy_addr : ""); query_length=strlen(query)+1; goto __run_query; } @@ -268,8 +270,10 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p if (!strncasecmp(SELECT_DB_USER, query_no_space, query_no_space_length)) { l_free(query_length,query); char *query1=(char *)"SELECT \"admin\" AS 'DATABASE()', \"%s\" AS 'USER()'"; - char *query2=(char *)malloc(strlen(query1)+strlen(sess->client_myds->myconn->userinfo->username)+10); - sprintf(query2,query1,sess->client_myds->myconn->userinfo->username); + const char* username = sess->client_myds->myconn->userinfo->username; + size_t query2_len = strlen(query1) + (username ? strlen(username) : 0) + 1; + char *query2=(char *)malloc(query2_len); + snprintf(query2, query2_len, query1, username ? username : ""); query=l_strdup(query2); query_length=strlen(query2)+1; free(query2); @@ -292,7 +296,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p char *q=(char *)"SELECT '%s' AS '@@version'"; query_length=strlen(q)+20; query=(char *)l_alloc(query_length); - sprintf(query,q,PROXYSQL_VERSION); + snprintf(query, query_length, q, PROXYSQL_VERSION); goto __run_query; } @@ -301,7 +305,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p char *q=(char *)"SELECT '%s' AS 'version()'"; query_length=strlen(q)+20; query=(char *)l_alloc(query_length); - sprintf(query,q,PROXYSQL_VERSION); + snprintf(query, query_length, q, PROXYSQL_VERSION); goto __run_query; } @@ -444,7 +448,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p free(query); char *a = (char *)"SELECT %d as Seconds_Behind_Master"; query = (char *)malloc(strlen(a)+4); - sprintf(query,a,rand()%30+10); + snprintf(query, strlen(a)+4, a, rand()%30+10); } } SQLite3_Session *sqlite_sess = (SQLite3_Session *)sess->thread->gen_args; From 90f9519a1e2313f0da209ecffc6f08e0307f13c9 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:56:31 +0000 Subject: [PATCH 133/227] tests: bound tap time formatter outputs Rewrite nice_time() string assembly to use bounded snprintf with tracked remaining capacity to avoid unbounded sprintf writes while preserving the same format output. --- test/tap/tap/tap.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/test/tap/tap/tap.cpp b/test/tap/tap/tap.cpp index c5df26bf22..b466cd0fd8 100644 --- a/test/tap/tap/tap.cpp +++ b/test/tap/tap/tap.cpp @@ -435,28 +435,40 @@ static ulong start_timer(void) static void nice_time(double sec,char *buff, my_bool part_second) { ulong tmp; + size_t remaining = 53; // per comment above: 52 chars + '\0' + char *p = buff; + if (sec >= 3600.0*24) { tmp=(ulong) (sec/(3600.0*24)); sec-=3600.0*24*tmp; - buff+= sprintf(buff, "%ld %s", tmp, tmp > 1 ? " days " : " day "); + int n = snprintf(p, remaining, "%ld %s", tmp, tmp > 1 ? " days " : " day "); + if (n < 0 || (size_t)n >= remaining) return; + p += n; + remaining -= n; } if (sec >= 3600.0) { tmp=(ulong) (sec/3600.0); sec-=3600.0*tmp; - buff+= sprintf(buff, "%ld %s", tmp, tmp > 1 ? " hours " : " hour "); + int n = snprintf(p, remaining, "%ld %s", tmp, tmp > 1 ? " hours " : " hour "); + if (n < 0 || (size_t)n >= remaining) return; + p += n; + remaining -= n; } if (sec >= 60.0) { tmp=(ulong) (sec/60.0); sec-=60.0*tmp; - buff+= sprintf(buff, "%ld min ", tmp); + int n = snprintf(p, remaining, "%ld min ", tmp); + if (n < 0 || (size_t)n >= remaining) return; + p += n; + remaining -= n; } if (part_second) - sprintf(buff,"%.2f sec",sec); + snprintf(p, remaining, "%.2f sec",sec); else - sprintf(buff,"%d sec",(int) sec); + snprintf(p, remaining,"%d sec",(int) sec); } From 2defe3ab0b63fb5d3b0aba5854b2d7cea4ddcebc Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:56:52 +0000 Subject: [PATCH 134/227] tests: bound admin SHOW FIELDS query formatting Use a fixed per-row buffer size with snprintf in admin_show_fields_from-t.cpp to replace unbounded sprintf while preserving behavior. --- test/tap/tests/admin_show_fields_from-t.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/admin_show_fields_from-t.cpp b/test/tap/tests/admin_show_fields_from-t.cpp index 5e7b5847ae..8c654d8589 100644 --- a/test/tap/tests/admin_show_fields_from-t.cpp +++ b/test/tap/tests/admin_show_fields_from-t.cpp @@ -74,9 +74,10 @@ int main() { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); return -1; } - char *query = (char *) malloc(strlen(queries[0]) + it->length() + 8); + const size_t query_len = strlen(queries[0]) + it->size() + 1; + char *query = (char *) malloc(query_len); for (std::vector::iterator it2 = queries.begin(); it2 != queries.end(); it2++) { - sprintf(query,*it2, it->c_str()); + snprintf(query, query_len, *it2, it->c_str()); diag("Running query: %s", query); MYSQL_QUERY(proxysql_admin, query); MYSQL_RES* proxy_res = mysql_store_result(proxysql_admin); From f5c18e023e0bd2ed49e1efeb57046ea7b671cd34 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:56:53 +0000 Subject: [PATCH 135/227] tests: bound admin SHOW TABLE STATUS query formatting Replace sprintf with bounded snprintf in admin_show_table_status-t.cpp using precomputed output length to avoid unbounded writes. --- test/tap/tests/admin_show_table_status-t.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/admin_show_table_status-t.cpp b/test/tap/tests/admin_show_table_status-t.cpp index ace170af3d..efbd68d984 100644 --- a/test/tap/tests/admin_show_table_status-t.cpp +++ b/test/tap/tests/admin_show_table_status-t.cpp @@ -74,9 +74,10 @@ int main() { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); return -1; } - char *query = (char *) malloc(strlen(queries[0]) + it->length() + 8); + const size_t query_len = strlen(queries[0]) + it->size() + 1; + char *query = (char *) malloc(query_len); for (std::vector::iterator it2 = queries.begin(); it2 != queries.end(); it2++) { - sprintf(query,*it2, it->c_str()); + snprintf(query, query_len, *it2, it->c_str()); diag("Running query: %s", query); MYSQL_QUERY(proxysql_admin, query); MYSQL_RES* proxy_res = mysql_store_result(proxysql_admin); From 50a54d5f455ecb1e36ca734db691be0e7d783a46 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:57:04 +0000 Subject: [PATCH 136/227] tests: bound create-table statement formatting Replace unsafe sprintf in test_ps_hg_routing-t.cpp with bounded snprintf into the fixed 1024-byte local buffer. --- test/tap/tests/test_ps_hg_routing-t.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/tap/tests/test_ps_hg_routing-t.cpp b/test/tap/tests/test_ps_hg_routing-t.cpp index 02a514f111..2e5fb85fa8 100644 --- a/test/tap/tests/test_ps_hg_routing-t.cpp +++ b/test/tap/tests/test_ps_hg_routing-t.cpp @@ -47,7 +47,7 @@ int main(int argc, char** argv) { MYSQL_QUERY(mysql, "create database if not exists test"); MYSQL_QUERY(mysql, "drop table if exists test.ps_hg_routing"); - sprintf(buf, "create table if not exists test.ps_hg_routing (c1 varchar(%d) primary key, c2 varchar(%d))", STRING_SIZE, STRING_SIZE); + snprintf(buf, sizeof(buf), "create table if not exists test.ps_hg_routing (c1 varchar(%d) primary key, c2 varchar(%d))", STRING_SIZE, STRING_SIZE); MYSQL_QUERY(mysql, buf); MYSQL_QUERY(mysql, "insert into test.ps_hg_routing (c1,c2) values ('abcdef', 'abcdef')"); @@ -158,4 +158,3 @@ int main(int argc, char** argv) { mysql_close(mysql); mysql_close(mysqladmin); } - From 11aa3bb015f66c25ab762cca3547863412aa8361 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:57:13 +0000 Subject: [PATCH 137/227] tests: bound read-only session SQL formatting Replace sprintf with bounded snprintf for SET @@global.read_only query string assembly. --- .../tap/tests/test_read_only_actions_offline_hard_servers-t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/test_read_only_actions_offline_hard_servers-t.cpp b/test/tap/tests/test_read_only_actions_offline_hard_servers-t.cpp index 8e8fc5c0fa..ba71f150af 100644 --- a/test/tap/tests/test_read_only_actions_offline_hard_servers-t.cpp +++ b/test/tap/tests/test_read_only_actions_offline_hard_servers-t.cpp @@ -185,7 +185,7 @@ int set_read_only_value(const std::string& host, uint16_t port, const std::strin } char query[256]; - sprintf(query, "SET @@global.read_only=%d", read_only_val); + snprintf(query, sizeof(query), "SET @@global.read_only=%d", read_only_val); rc_query = mysql_query(mysqldb,query); From c8226a9137ccf6afa42bae580410dfdb9baf6794 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:57:34 +0000 Subject: [PATCH 138/227] tests: bound-select query in set_testing thread worker Replace sprintf with bounded snprintf for per-iteration diagnostic SQL query in set_testing-t.cpp. --- test/tap/tests/set_testing-t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/set_testing-t.cpp b/test/tap/tests/set_testing-t.cpp index e65c88500e..f4fe8b89d8 100644 --- a/test/tap/tests/set_testing-t.cpp +++ b/test/tap/tests/set_testing-t.cpp @@ -207,7 +207,7 @@ void * my_conn_thread(void *arg) { usleep(sleepDelay * 1000); char query[128]; - sprintf(query, "SELECT /* %p %s */ %d;", mysql, paddress.c_str(), sleepDelay); + snprintf(query, sizeof(query), "SELECT /* %p %s */ %d;", mysql, paddress.c_str(), sleepDelay); if (mysql_query(mysql,query)) { select_ERR++; __sync_fetch_and_add(&g_select_ERR,1); From 4b551a0a674c787bc331631584c6c6d5daafdc62 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:57:37 +0000 Subject: [PATCH 139/227] tests: bound-select query in set_testing-multi worker Replace sprintf with bounded snprintf for multi-threaded select query formatting in set_testing-multi-t.cpp. --- test/tap/tests/set_testing-multi-t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/set_testing-multi-t.cpp b/test/tap/tests/set_testing-multi-t.cpp index 4b03581bdc..12676d112c 100644 --- a/test/tap/tests/set_testing-multi-t.cpp +++ b/test/tap/tests/set_testing-multi-t.cpp @@ -197,7 +197,7 @@ void * my_conn_thread(void *arg) { usleep(sleepDelay * 1000); char query[128]; - sprintf(query, "SELECT /* %p */ %d;", mysql, sleepDelay); + snprintf(query, sizeof(query), "SELECT /* %p */ %d;", mysql, sleepDelay); if (mysql_query(mysql,query)) { select_ERR++; __sync_fetch_and_add(&g_select_ERR,1); From 45a727f15093957e47f6228beb28c92f55faf7f8 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:57:39 +0000 Subject: [PATCH 140/227] tests: bound-select query in set_testing-240 worker Replace sprintf with bounded snprintf for per-iteration debug SQL construction in set_testing-240-t.cpp. --- test/tap/tests/set_testing-240-t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/set_testing-240-t.cpp b/test/tap/tests/set_testing-240-t.cpp index 467cd2c740..3e2c15eb7f 100644 --- a/test/tap/tests/set_testing-240-t.cpp +++ b/test/tap/tests/set_testing-240-t.cpp @@ -281,7 +281,7 @@ void * my_conn_thread(void *arg) { usleep(sleepDelay * 1000); char query[128]; - sprintf(query, "SELECT /* %p %s */ %d;", mysql, paddress.c_str(), sleepDelay); + snprintf(query, sizeof(query), "SELECT /* %p %s */ %d;", mysql, paddress.c_str(), sleepDelay); if (mysql_query(mysql,query)) { select_ERR++; __sync_fetch_and_add(&g_select_ERR,1); From c598092a913687e47448e42d0a982958ba96f59b Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:57:59 +0000 Subject: [PATCH 141/227] tests: bound galera 1 timeout query formatting Use snprintf with explicit buffer sizes in galera_1_timeout_count.cpp for hostgroup status and seconds_behind query formatting. --- test/tap/tests/galera_1_timeout_count.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/galera_1_timeout_count.cpp b/test/tap/tests/galera_1_timeout_count.cpp index 58ab87c15a..968cdb12fb 100644 --- a/test/tap/tests/galera_1_timeout_count.cpp +++ b/test/tap/tests/galera_1_timeout_count.cpp @@ -84,7 +84,7 @@ void SQLite3_Server::populate_galera_table(MySQL_Session *sess) { cluster_id--; int hg_id = 2270+(cluster_id*10)+1; char buf[1024]; - sprintf(buf, (char *)"SELECT * FROM HOST_STATUS_GALERA WHERE hostgroup_id = %d LIMIT 1", hg_id); + snprintf(buf, sizeof(buf), "SELECT * FROM HOST_STATUS_GALERA WHERE hostgroup_id = %d LIMIT 1", hg_id); sessdb->execute_statement(buf, &error , &cols , &affected_rows , &resultset); if (resultset->rows_count==0) { //sessdb->execute("DELETE FROM HOST_STATUS_GALERA"); @@ -197,7 +197,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p free(query); char *a = (char *)"SELECT %d as Seconds_Behind_Master"; query = (char *)malloc(strlen(a)+4); - sprintf(query,a,rand()%30+10); + snprintf(query, strlen(a)+4, a, rand()%30+10); } } SQLite3_Session *sqlite_sess = (SQLite3_Session *)sess->thread->gen_args; From f098e0749b3b1b5412feacf5f084d0e23c22500e Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:58:01 +0000 Subject: [PATCH 142/227] tests: bound galera 2 timeout query formatting Use snprintf with explicit buffer sizes in galera_2_timeout_no_count.cpp for hostgroup status and seconds_behind query formatting. --- test/tap/tests/galera_2_timeout_no_count.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/galera_2_timeout_no_count.cpp b/test/tap/tests/galera_2_timeout_no_count.cpp index d558ec292f..e55052c726 100644 --- a/test/tap/tests/galera_2_timeout_no_count.cpp +++ b/test/tap/tests/galera_2_timeout_no_count.cpp @@ -85,7 +85,7 @@ void SQLite3_Server::populate_galera_table(MySQL_Session *sess) { cluster_id--; int hg_id = 2270+(cluster_id*10)+1; char buf[1024]; - sprintf(buf, (char *)"SELECT * FROM HOST_STATUS_GALERA WHERE hostgroup_id = %d LIMIT 1", hg_id); + snprintf(buf, sizeof(buf), "SELECT * FROM HOST_STATUS_GALERA WHERE hostgroup_id = %d LIMIT 1", hg_id); sessdb->execute_statement(buf, &error , &cols , &affected_rows , &resultset); if (resultset->rows_count==0) { //sessdb->execute("DELETE FROM HOST_STATUS_GALERA"); @@ -207,7 +207,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p free(query); char *a = (char *)"SELECT %d as Seconds_Behind_Master"; query = (char *)malloc(strlen(a)+4); - sprintf(query,a,rand()%30+10); + snprintf(query, strlen(a)+4, a, rand()%30+10); } } SQLite3_Session *sqlite_sess = (SQLite3_Session *)sess->thread->gen_args; From a4f1b3aba81d31c8b3d3b9c7ea2bb3e891597e4d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:58:29 +0000 Subject: [PATCH 143/227] PrepStmt: replace sprintf with snprintf in client2 query formatting Use bounded snprintf for numeric SQL text generation to eliminate S5801 unsafe copy hotspot in client2 benchmark. --- test/PrepStmt/client2.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/PrepStmt/client2.cpp b/test/PrepStmt/client2.cpp index ad9f744d9d..1e33cfa228 100644 --- a/test/PrepStmt/client2.cpp +++ b/test/PrepStmt/client2.cpp @@ -83,7 +83,7 @@ int main() { fprintf(stderr, " mysql_stmt_init(), out of memory\n"); exit(EXIT_FAILURE); } - sprintf(buff,"SELECT %u + ?",(uint32_t)mt_rand()%NUMPRO); + snprintf(buff,sizeof(buff),"SELECT %u + ?",(uint32_t)mt_rand()%NUMPRO); bl=strlen(buff); uint64_t hash=local_stmts->compute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -105,7 +105,7 @@ int main() { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -117,7 +117,7 @@ int main() { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -129,7 +129,7 @@ int main() { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -159,7 +159,7 @@ int main() { // for comparison, we run also queries in TEXT protocol cpu_timer t; for (i=0; i Date: Mon, 10 Aug 2026 15:58:35 +0000 Subject: [PATCH 144/227] PrepStmt: use snprintf for client3 query strings Replace unsafe sprintf calls in client3 benchmark with bounded snprintf for deterministic SQL string construction. --- test/PrepStmt/client3.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/PrepStmt/client3.cpp b/test/PrepStmt/client3.cpp index dc49b35435..05e52dcfc9 100644 --- a/test/PrepStmt/client3.cpp +++ b/test/PrepStmt/client3.cpp @@ -98,7 +98,7 @@ void * mysql_thread(int tid) { fprintf(stderr, " mysql_stmt_init(), out of memory\n"); exit(EXIT_FAILURE); } - sprintf(buff,"SELECT %u + ?",(uint32_t)mt_rand()%NUMPRO); + snprintf(buff,sizeof(buff),"SELECT %u + ?",(uint32_t)mt_rand()%NUMPRO); bl=strlen(buff); uint64_t hash=local_stmts->compute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -120,7 +120,7 @@ void * mysql_thread(int tid) { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -132,7 +132,7 @@ void * mysql_thread(int tid) { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -144,7 +144,7 @@ void * mysql_thread(int tid) { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -156,7 +156,7 @@ void * mysql_thread(int tid) { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -176,7 +176,7 @@ void * mysql_thread(int tid) { // for comparison, we run also queries in TEXT protocol cpu_timer t; for (i=0; i Date: Mon, 10 Aug 2026 15:58:42 +0000 Subject: [PATCH 145/227] PrepStmt: replace sprintf with bounded snprintf in client4 Convert fixed-size SQL text construction from sprintf to snprintf in client4 benchmark harness. --- test/PrepStmt/client4.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/PrepStmt/client4.cpp b/test/PrepStmt/client4.cpp index a272656b5f..cb3d2387df 100644 --- a/test/PrepStmt/client4.cpp +++ b/test/PrepStmt/client4.cpp @@ -90,7 +90,7 @@ void * mysql_thread() { fprintf(stderr, " mysql_stmt_init(), out of memory\n"); exit(EXIT_FAILURE); } - sprintf(buff,"SELECT %u + ?",(uint32_t)mt_rand()%NUMPRO); + snprintf(buff,sizeof(buff),"SELECT %u + ?",(uint32_t)mt_rand()%NUMPRO); bl=strlen(buff); uint64_t hash=local_stmts->compute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -112,7 +112,7 @@ void * mysql_thread() { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -124,7 +124,7 @@ void * mysql_thread() { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -136,7 +136,7 @@ void * mysql_thread() { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -166,7 +166,7 @@ void * mysql_thread() { // for comparison, we run also queries in TEXT protocol cpu_timer t; for (i=0; i Date: Mon, 10 Aug 2026 15:58:48 +0000 Subject: [PATCH 146/227] PrepStmt: switch client5 random-query builders to snprintf Replace unsafe sprintf usage in client5 benchmark with bounded snprintf for safer fixed-buffer SQL text formatting. --- test/PrepStmt/client5.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/PrepStmt/client5.cpp b/test/PrepStmt/client5.cpp index ae7932893a..33de9924e8 100644 --- a/test/PrepStmt/client5.cpp +++ b/test/PrepStmt/client5.cpp @@ -104,7 +104,7 @@ void * mysql_thread(int tid) { cpu_timer t; // in this loop we create only some the prepared statements for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -133,7 +133,7 @@ void * mysql_thread(int tid) { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -145,7 +145,7 @@ void * mysql_thread(int tid) { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -157,7 +157,7 @@ void * mysql_thread(int tid) { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -172,7 +172,7 @@ void * mysql_thread(int tid) { unsigned int executed=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); From 50fa138dc3faba0c7d2f88cb0bc387d5bd6f609a Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:58:54 +0000 Subject: [PATCH 147/227] PrepStmt: guard active client6 SQL formatting with snprintf Replace the active buff+5 sprintf in client6 with bounded snprintf using the known 128-byte packet buffer capacity. --- test/PrepStmt/client6.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/PrepStmt/client6.cpp b/test/PrepStmt/client6.cpp index f2e2b12bce..0a6d760c3d 100644 --- a/test/PrepStmt/client6.cpp +++ b/test/PrepStmt/client6.cpp @@ -130,7 +130,7 @@ void * mysql_thread(int tid) { } for (i=0; i<16; i++) { MySQL_Session *sess=SESS[i]; - sprintf(buff+5,"SELECT %u + ?",(uint32_t)mt_rand()%NUMPRO); + snprintf(buff+5,sizeof(buff)-5,"SELECT %u + ?",(uint32_t)mt_rand()%NUMPRO); bl=strlen(buff+5); mysql_hdr hdr; hdr.pkt_id=0; From df2e21f1d9f2ca72d27b44b92fc58dfd16fa21b2 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:59:02 +0000 Subject: [PATCH 148/227] PrepStmt: replace active sprintf formatting in client7 Use bounded snprintf for client7 hot paths generating benchmark SQL strings to eliminate unsafe copy hazards. --- test/PrepStmt/client7.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/PrepStmt/client7.cpp b/test/PrepStmt/client7.cpp index 470669a73b..667b82ae10 100644 --- a/test/PrepStmt/client7.cpp +++ b/test/PrepStmt/client7.cpp @@ -168,7 +168,7 @@ void * mysql_thread(int tid) { cpu_timer t; // in this loop we create only some the prepared statements for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -209,7 +209,7 @@ void * mysql_thread(int tid) { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -221,7 +221,7 @@ void * mysql_thread(int tid) { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -233,7 +233,7 @@ void * mysql_thread(int tid) { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); @@ -248,7 +248,7 @@ void * mysql_thread(int tid) { unsigned int executed=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); From 12569f9a7c0f1118673af12e3490d79a5343d9c7 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:59:07 +0000 Subject: [PATCH 149/227] PrepStmt: use snprintf for query formatting in client9 Replace malloc+sprintf usage in client9 query construction with bounded snprintf to avoid fixed-format overflow risk. --- test/PrepStmt/client9.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/PrepStmt/client9.cpp b/test/PrepStmt/client9.cpp index e2b2a67e77..ac21dbe234 100644 --- a/test/PrepStmt/client9.cpp +++ b/test/PrepStmt/client9.cpp @@ -64,7 +64,7 @@ void run(MYSQL *mysql) { uint32_t r=(uint32_t)mt_rand(); r=r%3000; char *query=(char *)malloc(strlen(QUERY1)+16); - sprintf(query,QUERY1,r); + snprintf(query,strlen(QUERY1)+16,QUERY1,r); if (DBG) { fprintf(stdout,"%s\n",query); } From 107a6b37be716576b81a399d15ae02877a627a23 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:59:13 +0000 Subject: [PATCH 150/227] PrepStmt: make client10 SQL format bounded and length-safe Replace sprintf in client10 with bounded snprintf and use returned length directly to avoid unsafe copy and stale-length use. --- test/PrepStmt/client10.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/PrepStmt/client10.cpp b/test/PrepStmt/client10.cpp index b0a147c4d0..85cfdf1efb 100644 --- a/test/PrepStmt/client10.cpp +++ b/test/PrepStmt/client10.cpp @@ -96,7 +96,7 @@ void * mysql_thread(const std::string& username, int tid) { fprintf(stderr, " mysql_stmt_init(), out of memory\n"); return NULL; } - sprintf(buff,"SELECT %u + ?",(uint32_t)mt_rand()%NUMPRO); + bl = (unsigned int)snprintf(buff,sizeof(buff),"SELECT %u + ?",(uint32_t)mt_rand()%NUMPRO); if (mysql_stmt_prepare(stmt, buff, bl)) { // the prepared statement is created fprintf(stderr, " mysql_stmt_prepare(), failed: %s\n" , mysql_stmt_error(stmt)); From a29b2b4827275e5d7068ed07b103edb077bb2040 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:59:33 +0000 Subject: [PATCH 151/227] PrepStmt: finish remaining sprintf replacement in client2 Patch final active sprintf instance in client2 benchmark query generation path. --- test/PrepStmt/client2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/PrepStmt/client2.cpp b/test/PrepStmt/client2.cpp index 1e33cfa228..ae979999ec 100644 --- a/test/PrepStmt/client2.cpp +++ b/test/PrepStmt/client2.cpp @@ -141,7 +141,7 @@ int main() { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); From 8f9382fecaa4dd8d2720e0ba18d250014af0276f Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:59:36 +0000 Subject: [PATCH 152/227] PrepStmt: finish remaining sprintf replacement in client4 Replace final active sprintf use in client4 prepared-statement loop with bounded snprintf. --- test/PrepStmt/client4.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/PrepStmt/client4.cpp b/test/PrepStmt/client4.cpp index cb3d2387df..e7babe6d71 100644 --- a/test/PrepStmt/client4.cpp +++ b/test/PrepStmt/client4.cpp @@ -148,7 +148,7 @@ void * mysql_thread() { unsigned int founds=0; cpu_timer t; for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); From b8ca2163f0591cd3562035124a35e22e4ac1d92c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:59:51 +0000 Subject: [PATCH 153/227] Tap test: replace sprintf in mysql-reg_test_4867 query rules Convert mysql-reg_test_4867_query_rules string formatting to bounded snprintf/format-size-safe calls for S5801 cleanup. --- .../tests/mysql-reg_test_4867_query_rules-t.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp b/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp index c1eab3d333..d837fc4846 100644 --- a/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp +++ b/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp @@ -75,21 +75,21 @@ int next_val(ValueGenerator* vg) { char* unique_str(ValueGenerator* vg, const char* field) { char* str = (char*)malloc(32); - sprintf(str, "%s_%d", field, next_val(vg)); + snprintf(str, 32, "%s_%d", field, next_val(vg)); return str; } char* unique_ip(ValueGenerator* vg) { char* ip = (char*)malloc(24); unsigned int octet = vg->base + vg->offset++; - sprintf(ip, "%u.%u.%u.%u", + snprintf(ip, 24, "%u.%u.%u.%u", octet % 256, (octet + 1) % 256, (octet + 2) % 256, (octet + 3) % 256); return ip; } char* unique_json(ValueGenerator* vg) { char* json = (char*)malloc(50); - sprintf(json, "{\"%s\":%d}", "unique_key", next_val(vg)); + snprintf(json, 50, "{\"%s\":%d}", "unique_key", next_val(vg)); return json; } @@ -139,8 +139,9 @@ char* escape_str(MYSQL* mysql, const char* str) { if (!str) return strdup("NULL"); char* escaped = (char*)malloc(2 * strlen(str) + 1); mysql_real_escape_string(mysql, escaped, str, strlen(str)); - char* result = (char*)malloc(strlen(escaped) + 3); - sprintf(result, "'%s'", escaped); + size_t len = strlen(escaped); + char* result = (char*)malloc(len + 3); + snprintf(result, len + 3, "'%s'", escaped); free(escaped); return result; } @@ -297,7 +298,7 @@ bool check_result(MYSQL_RES* res, RuleData* expected, bool runtime_table) { // converting digest to hex string char hex_string[20]; - sprintf(hex_string, "0x%016X", expected->digest); + snprintf(hex_string, sizeof(hex_string), "0x%016X", expected->digest); if (strcmp(row[field_idx], hex_string ? hex_string : "") != 0) { diag("Expected digest to be '%s', got '%s'", hex_string, row[field_idx]); @@ -408,7 +409,7 @@ int main() { // Check rules in runtime table for (int i = 0; i < num_tests; i++) { char query[256]; - sprintf(query, "SELECT * FROM runtime_mysql_query_rules WHERE rule_id = %d", rule_ids[i]); + snprintf(query, sizeof(query), "SELECT * FROM runtime_mysql_query_rules WHERE rule_id = %d", rule_ids[i]); MYSQL_QUERY_ON_ERR_CLEANUP(proxysql_admin, query); MYSQL_RES* res = mysql_store_result(proxysql_admin); if (!res || mysql_num_rows(res) == 0) { @@ -429,7 +430,7 @@ int main() { // Check rules in runtime table for (int i = 0; i < num_tests; i++) { char query[256]; - sprintf(query, "SELECT * FROM disk.mysql_query_rules WHERE rule_id = %d", rule_ids[i]); + snprintf(query, sizeof(query), "SELECT * FROM disk.mysql_query_rules WHERE rule_id = %d", rule_ids[i]); if (mysql_query(proxysql_admin, query)) { fprintf(stderr, "File %s, line %d, Error: %s (%s)\n", __FILE__, __LINE__, mysql_error(proxysql_admin), query); From bd39144ef87630e6049dd1f2fc933bea07da3b2a Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 15:59:58 +0000 Subject: [PATCH 154/227] Tap test: replace sprintf in pgsql-reg_test_4867 query rules Convert pgsql-reg_test_4867 query rules string constructions to bounded snprintf in IDs and digest/ID query paths. --- test/tap/tests/pgsql-reg_test_4867_query_rules-t.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/tap/tests/pgsql-reg_test_4867_query_rules-t.cpp b/test/tap/tests/pgsql-reg_test_4867_query_rules-t.cpp index 4bc34f28b5..a233dfb0ba 100644 --- a/test/tap/tests/pgsql-reg_test_4867_query_rules-t.cpp +++ b/test/tap/tests/pgsql-reg_test_4867_query_rules-t.cpp @@ -122,21 +122,21 @@ int next_val(ValueGenerator* vg) { char* unique_str(ValueGenerator* vg, const char* field) { char* str = (char*)malloc(32); - sprintf(str, "%s_%d", field, next_val(vg)); + snprintf(str, 32, "%s_%d", field, next_val(vg)); return str; } char* unique_ip(ValueGenerator* vg) { char* ip = (char*)malloc(24); unsigned int octet = vg->base + vg->offset++; - sprintf(ip, "%u.%u.%u.%u", + snprintf(ip, 24, "%u.%u.%u.%u", octet % 256, (octet + 1) % 256, (octet + 2) % 256, (octet + 3) % 256); return ip; } char* unique_json(ValueGenerator* vg) { char* json = (char*)malloc(50); - sprintf(json, "{\"%s\":%d}", "unique_key", next_val(vg)); + snprintf(json, 50, "{\"%s\":%d}", "unique_key", next_val(vg)); return json; } @@ -343,7 +343,7 @@ bool check_result(PGresult* res, RuleData* expected, bool runtime_table) { // converting digest to hex string char hex_string[20]; - sprintf(hex_string, "0x%016X", expected->digest); + snprintf(hex_string, sizeof(hex_string), "0x%016X", expected->digest); f = PQfnumber(res, "digest"); if (!compare_str(PQgetvalue(res, 0, f), hex_string)) { match = false; @@ -515,7 +515,7 @@ int main() { // Check rules in runtime table for (int i = 0; i < num_tests; i++) { char query[256]; - sprintf(query, "SELECT * FROM runtime_pgsql_query_rules WHERE rule_id = %d", rule_ids[i]); + snprintf(query, sizeof(query), "SELECT * FROM runtime_pgsql_query_rules WHERE rule_id = %d", rule_ids[i]); PGresult* res = PQexec(conn.get(), query); if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) == 0) { fprintf(stderr, "Rule %d not found\n", rule_ids[i]); @@ -536,7 +536,7 @@ int main() { // Check rules in runtime table for (int i = 0; i < num_tests; i++) { char query[256]; - sprintf(query, "SELECT * FROM disk.pgsql_query_rules WHERE rule_id = %d", rule_ids[i]); + snprintf(query, sizeof(query), "SELECT * FROM disk.pgsql_query_rules WHERE rule_id = %d", rule_ids[i]); PGresult* res = PQexec(conn.get(), query); if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) == 0) { fprintf(stderr, "Rule %d not found\n", rule_ids[i]); From e9f96522876e04756b7aef29d11155bc4ddf22dc Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 16:00:05 +0000 Subject: [PATCH 155/227] Tap test: bound snprintf in reg_test_3585 metadata checks Replace sprintf in reg_test_3585 expected/actual row comparisons with snprintf to remove S5801 risk. --- .../tests/reg_test_3585-stmt_metadata-t.cpp | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp b/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp index f40e959a39..b7a753a405 100644 --- a/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp +++ b/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp @@ -248,24 +248,24 @@ int insert_and_check(MYSQL_STMT *stmti, MYSQL_STMT *stmts, int id, char *name1, } char buf1[256], buf2[256]; if (ts1) { - sprintf(buf1,"%d:%d:%d", ts1->hour, ts1->minute, ts1->second); + snprintf(buf1,sizeof(buf1),"%d:%d:%d", ts1->hour, ts1->minute, ts1->second); } else { - sprintf(buf1,"NULL"); + snprintf(buf1,sizeof(buf1),"NULL"); } if (is_null[2]) { - sprintf(buf2,"NULL"); + snprintf(buf2,sizeof(buf2),"NULL"); } else { - sprintf(buf2,"%d:%d:%d", ts_res1.hour, ts_res1.minute, ts_res1.second); + snprintf(buf2,sizeof(buf2),"%d:%d:%d", ts_res1.hour, ts_res1.minute, ts_res1.second); } if (strcmp(buf1,buf2)==0) matches++; diag("time1 expected/retrieved: %s , %s", buf1, buf2); - sprintf(buf1,"NULL"); - sprintf(buf2,"NULL"); + snprintf(buf1,sizeof(buf1),"NULL"); + snprintf(buf2,sizeof(buf2),"NULL"); if (i1) - sprintf(buf1, "%d", *i1); + snprintf(buf1,sizeof(buf1), "%d", *i1); if (!is_null[3]) - sprintf(buf2, "%d", i1_res); + snprintf(buf2,sizeof(buf2), "%d", i1_res); diag("i1 expected/retrieved: %s , %s", buf1, buf2); if (strcmp(buf1,buf2)==0) matches++; @@ -277,24 +277,24 @@ int insert_and_check(MYSQL_STMT *stmti, MYSQL_STMT *stmts, int id, char *name1, matches++; } if (ts2) { - sprintf(buf1,"%d:%d:%d", ts2->hour, ts2->minute, ts2->second); + snprintf(buf1,sizeof(buf1),"%d:%d:%d", ts2->hour, ts2->minute, ts2->second); } else { - sprintf(buf1,"NULL"); + snprintf(buf1,sizeof(buf1),"NULL"); } if (is_null[5]) { - sprintf(buf2,"NULL"); + snprintf(buf2,sizeof(buf2),"NULL"); } else { - sprintf(buf2,"%d:%d:%d", ts_res2.hour, ts_res2.minute, ts_res2.second); + snprintf(buf2,sizeof(buf2),"%d:%d:%d", ts_res2.hour, ts_res2.minute, ts_res2.second); } if (strcmp(buf1,buf2)==0) matches++; diag("time2 expected/retrieved: %s , %s", buf1, buf2); - sprintf(buf1,"NULL"); - sprintf(buf2,"NULL"); + snprintf(buf1,sizeof(buf1),"NULL"); + snprintf(buf2,sizeof(buf2),"NULL"); if (i2) - sprintf(buf1, "%d", *i2); + snprintf(buf1,sizeof(buf1), "%d", *i2); if (!is_null[6]) - sprintf(buf2, "%d", i2_res); + snprintf(buf2,sizeof(buf2), "%d", i2_res); diag("i2 expected/retrieved: %s , %s", buf1, buf2); if (strcmp(buf1,buf2)==0) matches++; From 00a4dc38ab59992e2c581d32a37d43e464376d0c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 16:00:14 +0000 Subject: [PATCH 156/227] Tap test: replace sprintf in reg_test_3603 metadata checks Convert metadata comparison value formatting in reg_test_3603 to bounded snprintf for S5801 cleanup. --- .../tests/reg_test_3603-stmt_metadata-t.cpp | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp b/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp index 8afc9a8614..7fe30d22cb 100644 --- a/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp +++ b/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp @@ -383,13 +383,13 @@ int update_and_check( } { - sprintf(buf1,"NULL"); - sprintf(buf2,"NULL"); + snprintf(buf1,sizeof(buf1),"NULL"); + snprintf(buf2,sizeof(buf2),"NULL"); if (dur) - sprintf(buf1, "%ld", *dur); + snprintf(buf1,sizeof(buf1), "%ld", *dur); if (!is_null[3]) - sprintf(buf2, "%d", i_duration); + snprintf(buf2,sizeof(buf2), "%d", i_duration); diag("'duration' expected/retrieved: %s , %s", buf1, buf2); if (strcmp(buf1,buf2)==0) matches++; @@ -397,20 +397,20 @@ int update_and_check( { if (end_time) { - sprintf(buf1,"%d:%d:%d", end_time->hour, end_time->minute, end_time->second); + snprintf(buf1,sizeof(buf1),"%d:%d:%d", end_time->hour, end_time->minute, end_time->second); } else { - sprintf(buf1,"NULL"); + snprintf(buf1,sizeof(buf1),"NULL"); } if (is_null[4]) { - sprintf(buf2,"NULL"); + snprintf(buf2,sizeof(buf2),"NULL"); } else { - sprintf(buf2,"%d:%d:%d", ts_end_time.hour, ts_end_time.minute, ts_end_time.second); + snprintf(buf2,sizeof(buf2),"%d:%d:%d", ts_end_time.hour, ts_end_time.minute, ts_end_time.second); } if (strcmp(buf1,buf2)==0) matches++; diag("'end_time' expected/retrieved: %s , %s", buf1, buf2); - sprintf(buf1,"NULL"); - sprintf(buf2,"NULL"); + snprintf(buf1,sizeof(buf1),"NULL"); + snprintf(buf2,sizeof(buf2),"NULL"); } { @@ -434,13 +434,13 @@ int update_and_check( } { - sprintf(buf1,"NULL"); - sprintf(buf2,"NULL"); + snprintf(buf1,sizeof(buf1),"NULL"); + snprintf(buf2,sizeof(buf2),"NULL"); if (mapping_id) - sprintf(buf1, "%d", *mapping_id); + snprintf(buf1,sizeof(buf1), "%d", *mapping_id); if (!is_null[7]) - sprintf(buf2, "%d", i_mapping_id); + snprintf(buf2,sizeof(buf2), "%d", i_mapping_id); diag("'mapping_id' expected/retrieved: %s , %s", buf1, buf2); if (strcmp(buf1,buf2)==0) matches++; @@ -468,20 +468,20 @@ int update_and_check( { if (st_time) { - sprintf(buf1,"%d:%d:%d", st_time->hour, st_time->minute, st_time->second); + snprintf(buf1,sizeof(buf1),"%d:%d:%d", st_time->hour, st_time->minute, st_time->second); } else { - sprintf(buf1,"NULL"); + snprintf(buf1,sizeof(buf1),"NULL"); } if (is_null[10]) { - sprintf(buf2,"NULL"); + snprintf(buf2,sizeof(buf2),"NULL"); } else { - sprintf(buf2,"%d:%d:%d", ts_st_time.hour, ts_st_time.minute, ts_st_time.second); + snprintf(buf2,sizeof(buf2),"%d:%d:%d", ts_st_time.hour, ts_st_time.minute, ts_st_time.second); } if (strcmp(buf1,buf2)==0) matches++; diag("'st_time' expected/retrieved: %s , %s", buf1, buf2); - sprintf(buf1,"NULL"); - sprintf(buf2,"NULL"); + snprintf(buf1,sizeof(buf1),"NULL"); + snprintf(buf2,sizeof(buf2),"NULL"); } { From 426e4948d0616eea5c405b2c5dbe36716a1b4a1e Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 16:00:24 +0000 Subject: [PATCH 157/227] Tap test: bound dummy-query formatting in reg_test_4399 Replace one remaining sprintf in stats_mysql_query_digest traffic generator with bounded snprintf. --- test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp b/test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp index 4dbba2cb85..b86731175f 100644 --- a/test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp +++ b/test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp @@ -64,7 +64,7 @@ int main(int argc, char** argv) { char query[128]{}; diag("Generating simulated traffic..."); for (unsigned int i=0; i < QUERY_COUNT; i++) { - sprintf(query, "DO /*#%d#*/ %d", i, i); + snprintf(query, sizeof(query), "DO /*#%d#*/ %d", i, i); MYSQL_QUERY(proxysql, query); std::this_thread::sleep_for(std::chrono::milliseconds(1)); } From 4e4c7b35ae64b8150aa0b45132f097e9aae97ddb Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 16:00:40 +0000 Subject: [PATCH 158/227] Tap test: bound snprintf in pgsql copy-to insert generator Replace sprintf with snprintf when building INSERT statements in pgsql copy-to large-volume loop. --- test/tap/tests/pgsql-copy_to_test-t.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/tap/tests/pgsql-copy_to_test-t.cpp b/test/tap/tests/pgsql-copy_to_test-t.cpp index 5286256ec9..28a082f478 100644 --- a/test/tap/tests/pgsql-copy_to_test-t.cpp +++ b/test/tap/tests/pgsql-copy_to_test-t.cpp @@ -285,12 +285,12 @@ void testLargeDataVolume(PGconn* admin_conn, PGconn* conn) { return; // Insert a large number of rows - for (int i = 0; i < 1000; i++) { - char query[256]; - sprintf(query, "INSERT INTO copy_test (name, value, active, created_at) VALUES ('User%d', %d, %s, NOW())", - i, i * 10, (i % 2 == 0) ? "TRUE" : "FALSE"); - if (!executeQueries(conn, { - query + for (int i = 0; i < 1000; i++) { + char query[256]; + snprintf(query, sizeof(query), "INSERT INTO copy_test (name, value, active, created_at) VALUES ('User%d', %d, %s, NOW())", + i, i * 10, (i % 2 == 0) ? "TRUE" : "FALSE"); + if (!executeQueries(conn, { + query })) return; } From f61fa5aea1b94629b54016b26a9abaa3215d49bf Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:13:05 +0000 Subject: [PATCH 159/227] test: cover short checksum source handling Add a regression case that places guard bytes immediately after a short checksum string. The test verifies that set_checksum preserves zero padding instead of copying bytes past the source terminator. --- test/tap/tests/unit/glovars_unit-t.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/test/tap/tests/unit/glovars_unit-t.cpp b/test/tap/tests/unit/glovars_unit-t.cpp index 61da3289b1..c39ca2cdae 100644 --- a/test/tap/tests/unit/glovars_unit-t.cpp +++ b/test/tap/tests/unit/glovars_unit-t.cpp @@ -162,6 +162,28 @@ static void test_checksum_value_set() { ok(spaces_replaced, "set_checksum(spaces): positions 2-17 all replaced with '0'"); } +static void test_checksum_value_short_input() { + ProxySQL_Checksum_Value cv; + struct { + char input[5]; + char guard[15]; + } source; + memcpy(source.input, "0x12", sizeof(source.input)); + memset(source.guard, 'X', sizeof(source.guard)); + + cv.set_checksum(source.input); + + bool zero_padded = true; + for (int i = 4; i < 18; i++) { + if (cv.checksum[i] != '0') { + zero_padded = false; + break; + } + } + ok(zero_padded, + "set_checksum(short input): zero-pads without reading beyond the source string"); +} + static void test_checksum_value_shutdown_flag() { // Normal destruction (in_shutdown == false): no crash { @@ -415,7 +437,7 @@ static void test_replace_checksum_zeros() { } int main() { - plan(78); + plan(79); test_init_minimal(); @@ -425,6 +447,7 @@ int main() { // Checksum value (16 tests) test_checksum_value_defaults(); // 5 test_checksum_value_set(); // 9 + test_checksum_value_short_input(); // 1 test_checksum_value_shutdown_flag(); // 2 // Checksum value version/epoch (6 tests) From dd16de0fdfa29dfc50975464422c66ba1e539ca0 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:13:06 +0000 Subject: [PATCH 160/227] fix: bound checksum copies by source length Limit set_checksum to the available source string length after clearing the destination buffer. This preserves the existing zero-padding behavior without reading past short checksum strings, and safely handles a null source. --- include/proxysql_glovars.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/proxysql_glovars.hpp b/include/proxysql_glovars.hpp index e3ba08981f..6879227ac7 100644 --- a/include/proxysql_glovars.hpp +++ b/include/proxysql_glovars.hpp @@ -48,9 +48,12 @@ class ProxySQL_Checksum_Value { epoch = 0; in_shutdown = false; } - void set_checksum(char *c) { + void set_checksum(const char *c) { memset(checksum,0,ProxySQL_Checksum_Value_LENGTH); - memcpy(checksum,c,ProxySQL_Checksum_Value_LENGTH); + if (c) { + const size_t length = strnlen(c, ProxySQL_Checksum_Value_LENGTH); + memcpy(checksum, c, length); + } replace_checksum_zeros(checksum); } ~ProxySQL_Checksum_Value() { From 1c07f935ddeb475ad8bcd29acae1f21665514b69 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:13:06 +0000 Subject: [PATCH 161/227] fix: preserve MySQL digest length limits Restore the configured query_digests_max_digest_length bound when hashing MySQL digest text. This keeps digest bucketing consistent with the truncated text retained by query digest statistics. --- lib/MySQLFFTO.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/MySQLFFTO.cpp b/lib/MySQLFFTO.cpp index 3c530dd621..923881ab92 100644 --- a/lib/MySQLFFTO.cpp +++ b/lib/MySQLFFTO.cpp @@ -272,7 +272,7 @@ void MySQLFFTO::report_query_stats(const std::string& query, unsigned long long ((query.length() < QUERY_DIGEST_BUF) ? qp.buf : NULL), &opts); if (digest_text) { qp.digest_text = digest_text; - const int digest_len = static_cast(std::string_view(digest_text).size()); + const int digest_len = static_cast(strnlen(digest_text, mysql_thread___query_digests_max_digest_length)); qp.digest = SpookyHash::Hash64(digest_text, digest_len, 0); char* ca = (char*)""; if (mysql_thread___query_digests_track_hostname && m_session->client_myds->addr.addr) ca = m_session->client_myds->addr.addr; From 08da7fb390ae56fd7fa96142982512c8f05bfcef Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:13:06 +0000 Subject: [PATCH 162/227] fix: preserve PostgreSQL digest length limits Restore the configured query_digests_max_digest_length bound when hashing PostgreSQL digest text. This keeps digest bucketing consistent with the truncated text retained by query digest statistics. --- lib/PgSQLFFTO.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/PgSQLFFTO.cpp b/lib/PgSQLFFTO.cpp index 88ecaddc88..e6d3238b06 100644 --- a/lib/PgSQLFFTO.cpp +++ b/lib/PgSQLFFTO.cpp @@ -355,7 +355,7 @@ void PgSQLFFTO::report_query_stats(const std::string& query, unsigned long long ((query.length() < QUERY_DIGEST_BUF) ? qp.buf : NULL), &opts); if (digest_text) { qp.digest_text = digest_text; - const int digest_len = static_cast(std::string_view(digest_text).size()); + const int digest_len = static_cast(strnlen(digest_text, pgsql_thread___query_digests_max_digest_length)); qp.digest = SpookyHash::Hash64(digest_text, digest_len, 0); char* ca = (char*)""; if (pgsql_thread___query_digests_track_hostname && m_session->client_myds->addr.addr) ca = m_session->client_myds->addr.addr; From f1eb14928d5671ef8cefcb826b8bf1b18379e126 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:13:06 +0000 Subject: [PATCH 163/227] fix: use checksum buffer capacity for cluster updates Pass ProxySQL_Checksum_Value_LENGTH to snprintf when copying a peer checksum. sizeof on the inherited char pointer only reported pointer width and truncated normal checksums on 64-bit systems. --- lib/ProxySQL_Cluster.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index c7a6b23b23..707b3ba67d 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -591,10 +591,10 @@ static void process_component_checksum( checksum.epoch = atoll(row[2]); checksum.last_updated = now; - if (strcmp(checksum.checksum, row[3])) { - const char *checksum_source = row[3] ? row[3] : ""; - snprintf(checksum.checksum, sizeof(checksum.checksum), "%s", checksum_source); - checksum.last_changed = now; + if (strcmp(checksum.checksum, row[3])) { + const char *checksum_source = row[3] ? row[3] : ""; + snprintf(checksum.checksum, ProxySQL_Checksum_Value_LENGTH, "%s", checksum_source); + checksum.last_changed = now; checksum.diff_check = 1; const char* no_sync_message = NULL; From da855f63b7ffde8d66f9b7b4d1017ddd5fd0355e Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:24:45 +0000 Subject: [PATCH 164/227] fix: bound PostgreSQL command tag formatting Handle empty and whitespace-delimited command tags without unsigned length wraparound, and build completion tags with dynamic strings instead of a fixed stack buffer. Validate binary DataRow lengths and allocations before encoding so oversized or unavailable buffers become NULL fields instead of causing invalid memory access. --- lib/PgSQL_Protocol.cpp | 82 +++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/lib/PgSQL_Protocol.cpp b/lib/PgSQL_Protocol.cpp index f7ecae213b..0e5d04614a 100644 --- a/lib/PgSQL_Protocol.cpp +++ b/lib/PgSQL_Protocol.cpp @@ -1,6 +1,7 @@ #include #include +#include #include "proxysql.h" #include "cpp.h" #include "PgSQL_Authentication.h" @@ -200,18 +201,11 @@ void PG_pkt::write_RowDescription(const char *tupdesc, ...) { void SQLite3_to_Postgres(PtrSizeArray *psa, SQLite3_result *result, char *error, int affected_rows, const char *query_type, bool send_ready_for_query, char txn_state) { assert(psa != NULL); - const char *fs = strchr(query_type, ' '); - int qtlen = strlen(query_type); - if (fs != NULL) { - qtlen = (fs - query_type) + 1; - } - std::string buf(query_type, qtlen - 1); - { - char *s = &buf[0]; - while (*s) { - *s = toupper((unsigned char) *s); - s++; - } + const char *query = query_type ? query_type : ""; + const size_t command_len = strcspn(query, " \t\r\n"); + std::string buf(query, command_len); + for (char& c : buf) { + c = static_cast(toupper((unsigned char)c)); } if (result) { int ncol = result->columns; @@ -249,9 +243,8 @@ void SQLite3_to_Postgres(PtrSizeArray *psa, SQLite3_result *result, char *error, } if (buf == "SELECT") { - char tmpbuf[128]; - sprintf(tmpbuf,"%s %d", buf.c_str(), result->rows_count); - pkt.write_generic('C', "s", tmpbuf); + const std::string completion_tag = buf + " " + std::to_string(result->rows_count); + pkt.write_generic('C', "s", completion_tag.c_str()); } else { pkt.write_CommandComplete(buf.c_str()); } @@ -276,13 +269,12 @@ void SQLite3_to_Postgres(PtrSizeArray *psa, SQLite3_result *result, char *error, */ // see https://www.postgresql.org/docs/current/protocol-message-formats.html } else { - char tmpbuf[128]; if (buf == "INSERT") { - sprintf(tmpbuf,"%s 0 %d", buf.c_str(), affected_rows); - pkt.write_generic('C', "s", tmpbuf); + const std::string completion_tag = buf + " 0 " + std::to_string(affected_rows); + pkt.write_generic('C', "s", completion_tag.c_str()); } else if (buf == "UPDATE" || buf == "DELETE") { - sprintf(tmpbuf,"%s %d", buf.c_str(), affected_rows); - pkt.write_generic('C', "s", tmpbuf); + const std::string completion_tag = buf + " " + std::to_string(affected_rows); + pkt.write_generic('C', "s", completion_tag.c_str()); } else { pkt.write_CommandComplete(buf.c_str()); } @@ -315,20 +307,23 @@ void PG_pkt::write_DataRow(const char *tupdesc, ...) { val = va_arg(ap, char *); } else if (tupdesc[i] == 'b') { int blen = va_arg(ap, int); - if (blen >= 0) { - uint8_t *bval = va_arg(ap, uint8_t *); - size_t required = 2 + blen * 2 + 1; - tmp2 = (char *)malloc(required); - tmp2[0] = '\\'; - tmp2[1] = 'x'; - tmp2[2] = '\0'; - for (int j = 0; j < blen; j++) - snprintf(tmp2 + (2 + j * 2), 3, "%02x", bval[j]); - val = tmp2; - } else { - (void) va_arg(ap, uint8_t *); - val = NULL; - } + uint8_t *bval = va_arg(ap, uint8_t *); + if (blen >= 0 && (bval != nullptr || blen == 0)) { + const size_t byte_len = static_cast(blen); + const size_t max_byte_len = (std::numeric_limits::max() - 3) / 2; + if (byte_len <= max_byte_len) { + const size_t required = 2 + byte_len * 2 + 1; + tmp2 = (char *)malloc(required); + if (tmp2 != nullptr) { + tmp2[0] = '\\'; + tmp2[1] = 'x'; + tmp2[2] = '\0'; + for (size_t j = 0; j < byte_len; j++) + snprintf(tmp2 + (2 + j * 2), 3, "%02x", bval[j]); + val = tmp2; + } + } + } } else if (tupdesc[i] == 'T') { usec_t time = va_arg(ap, usec_t); val = format_time_s(time, tmp, sizeof(tmp)); @@ -1610,6 +1605,9 @@ char* extract_tag_from_query(const char* query) { constexpr size_t deallocate_prepare_all_len = sizeof("DEALLOCATE PREPARE ALL") - 1; constexpr size_t discard_all_len = sizeof("DISCARD ALL") - 1; + if (query == nullptr) { + return strdup(""); + } size_t qtlen = strlen(query); if ((qtlen > create_table_len) && strncasecmp(query, "CREATE TABLE AS", create_table_len) == 0) { return strdup("SELECT"); @@ -1620,18 +1618,10 @@ char* extract_tag_from_query(const char* query) { } else if ((qtlen >= discard_all_len) && (strncasecmp(query, "DISCARD ALL", discard_all_len) == 0)) { return strdup("DISCARD ALL"); } else { - const char* fs = strchr(query, ' '); - - if (fs != NULL) { - qtlen = (fs - query) + 1; - } - std::string buf(query, qtlen - 1); - { - char* s = &buf[0]; - while (*s) { - *s = toupper((unsigned char)*s); - s++; - } + qtlen = strcspn(query, " \t\r\n"); + std::string buf(query, qtlen); + for (char& c : buf) { + c = static_cast(toupper((unsigned char)c)); } return strdup(buf.c_str()); From 0b16151830a1551cd94a685264fa6589d36b9c2d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:25:28 +0000 Subject: [PATCH 165/227] fix: size configuration queries for all values Include default_schema and digest contents in the calculated SQL capacities before formatting MySQL user and query-rule records. Use a bounded formatter for the user query so long configuration values cannot overrun the allocated buffer. --- lib/ProxySQL_Config.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index 8eac46c21a..126e35bb4e 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -260,9 +260,9 @@ int ProxySQL_Config::Read_MySQL_Users_from_configfile(std::string& error) { const size_t password_len = password.size(); const size_t safe_comment_len = safe_strlen(safe_comment); const size_t attributes_len = attributes.size(); - const size_t query_len = query_base_len + username_len + password_len + safe_comment_len + attributes_len + 128; + const size_t query_len = query_base_len + username_len + password_len + default_schema.size() + safe_comment_len + attributes_len + 128; char *query=(char *)malloc(query_len); - sprintf(query,q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, default_schema.c_str(), schema_locked, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); + snprintf(query, query_len, q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, default_schema.c_str(), schema_locked, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); admindb->execute(query); if (o!=o1) free(o); free(o1); @@ -961,6 +961,7 @@ int ProxySQL_Config::Read_MySQL_Query_Rules_from_configfile() { ( client_addr_exists ? client_addr.size() : 0 ) + 4 + ( proxy_addr_exists ? proxy_addr.size() : 0 ) + 4 + proxy_port_str.size() + 4 + + ( digest_exists ? digest.size() : 0 ) + 4 + ( match_digest_exists ? match_digest.size() : 0 ) + 4 + ( match_pattern_exists ? match_pattern.size() : 0 ) + 4 + negate_match_pattern_str.size() + 4 + @@ -2780,6 +2781,7 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_from_configfile() { (client_addr_exists ? client_addr.size() : 0) + 4 + (proxy_addr_exists ? proxy_addr.size() : 0) + 4 + proxy_port_str.size() + 4 + + (digest_exists ? digest.size() : 0) + 4 + (match_digest_exists ? match_digest.size() : 0) + 4 + (match_pattern_exists ? match_pattern.size() : 0) + 4 + negate_match_pattern_str.size() + 4 + From 818ca49336bf69903a7372ed98864e190c6722e3 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:26:39 +0000 Subject: [PATCH 166/227] fix: handle SQLite test server allocation failures Build interface lists before replacing the active set so strdup or array allocation failures release the write lock without losing the previous configuration. Normalize nullable proxy addresses, reject failed temporary query allocations, and reuse the case-insensitive replica status match when selecting the response column. --- src/SQLite3_Server.cpp | 58 +++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 3a9ce021db..71cf437314 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -224,22 +224,38 @@ class sqlite3server_main_loop_listeners { bool update_ifaces(char *list, char ***_ifaces) { wrlock(); - int i; - char **ifaces=*_ifaces; + int i = 0; + char **old_ifaces = *_ifaces; + char **new_ifaces = (char **)calloc(MAX_IFACES, sizeof(char *)); tokenizer_t tok; tokenizer( &tok, list, ";", TOKENIZER_NO_EMPTIES ); const char* token; - ifaces=reset_ifaces(ifaces); - i=0; - for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { + if (new_ifaces == NULL) { + free_tokenizer( &tok ); + wrunlock(); + return false; + } + for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { char *token_copy = strdup(token); if (token_copy == NULL) { + for (int j = 0; j < i; ++j) { + free(new_ifaces[j]); + } + free(new_ifaces); free_tokenizer( &tok ); + wrunlock(); return false; } - ifaces[i]=token_copy; + new_ifaces[i]=token_copy; i++; } + if (old_ifaces != NULL) { + for (int j = 0; j < MAX_IFACES; ++j) { + free(old_ifaces[j]); + } + free(old_ifaces); + } + *_ifaces = new_ifaces; free_tokenizer( &tok ); version++; wrunlock(); @@ -593,7 +609,12 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p const size_t proxy_addr_len = proxy_addr ? strlen(proxy_addr) : 0; const size_t query_len = a_len + proxy_addr_len + 1; query = (char *)malloc(query_len); - snprintf(query, query_len, a, proxy_addr); + if (query == NULL) { + GloSQLite3Server->send_MySQL_ERR(&sess->client_myds->myprot, 1105, "Out of memory"); + run_query = false; + goto __run_query; + } + snprintf(query, query_len, a, proxy_addr ? proxy_addr : ""); #else query=l_strdup("SELECT '(ProxySQL SQLite3 Server)'"); #endif // TEST_AURORA || TEST_GALERA || TEST_GROUPREP || TEST_READONLY || TEST_REPLICATIONLAG || TEST_RDS_BGD @@ -603,15 +624,27 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } if (query_no_space_length==SELECT_DB_USER_LEN) { - if (!strncasecmp(SELECT_DB_USER, query_no_space, query_no_space_length)) { + if (!strncasecmp(SELECT_DB_USER, query_no_space, query_no_space_length)) { l_free(query_length,query); + query = NULL; char *query1=(char *)"SELECT \"admin\" AS 'DATABASE()', \"%s\" AS 'USER()'"; const char* username = sess->client_myds->myconn->userinfo->username; size_t query2_length = strlen(query1) + (username ? strlen(username) : 0) + 1; char *query2=(char *)malloc(query2_length); + if (query2 == NULL) { + GloSQLite3Server->send_MySQL_ERR(&sess->client_myds->myprot, 1105, "Out of memory"); + run_query = false; + goto __run_query; + } snprintf(query2, query2_length, query1, username ? username : ""); query=l_strdup(query2); - query_length=strlen(query2)+1; + if (query == NULL) { + free(query2); + GloSQLite3Server->send_MySQL_ERR(&sess->client_myds->myprot, 1105, "Out of memory"); + run_query = false; + goto __run_query; + } + query_length=strlen(query)+1; free(query2); goto __run_query; } @@ -999,12 +1032,13 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } #endif // TEST_READONLY || TEST_RDS_BGD #ifdef TEST_REPLICATIONLAG + const bool replica_status = strncasecmp("SELECT REPLICA STATUS ", query_no_space, k_select_replica_status_len) == 0; if ( strncasecmp("SELECT SLAVE STATUS ", query_no_space, k_select_slave_status_len) == 0 - || strncasecmp("SELECT REPLICA STATUS ", query_no_space, k_select_replica_status_len) == 0 + || replica_status ) { uint64_t addr_offset { - strstr(query_no_space, "REPLICA") ? k_select_replica_status_len : k_select_slave_status_len + replica_status ? k_select_replica_status_len : k_select_slave_status_len }; if (strlen(query_no_space) > k_select_slave_status_len + 5) { pthread_mutex_lock(&GloSQLite3Server->test_replicationlag_mutex); @@ -1017,7 +1051,7 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p free(query); string SELECT { "SELECT " + (rc ? std::to_string(*rc) : string { "null" }) + " AS " }; - SELECT += strstr(query_no_space, "REPLICA") ? "Seconds_Behind_Source" : "Seconds_Behind_Master"; + SELECT += replica_status ? "Seconds_Behind_Source" : "Seconds_Behind_Master"; query = static_cast(malloc(SELECT.size() + 1)); snprintf(query, SELECT.size() + 1, "%s", SELECT.c_str()); From 0173c41d19f3d6131447f089792772ee64f4a59c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:27:25 +0000 Subject: [PATCH 167/227] fix: preserve admin interfaces on copy failure Construct the replacement interface array before discarding the active list. Clean up partial copies and release the write lock when allocation fails, so failed interface updates cannot leak memory, deadlock future writers, or erase a working configuration. --- include/Admin_ifaces.h | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/include/Admin_ifaces.h b/include/Admin_ifaces.h index 37b87f5f63..bfec89d329 100644 --- a/include/Admin_ifaces.h +++ b/include/Admin_ifaces.h @@ -129,21 +129,37 @@ class admin_main_loop_listeners { bool update_ifaces(char *list, char ***_ifaces) { wrlock(); - int i; - char **ifaces=*_ifaces; + int i = 0; + char **old_ifaces = *_ifaces; + char **new_ifaces = (char **)calloc(MAX_IFACES, sizeof(char *)); tokenizer_t tok; tokenizer( &tok, list, ";", TOKENIZER_NO_EMPTIES ); const char* token; - ifaces=reset_ifaces(ifaces); - i=0; - for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - ifaces[i] = strdup(token); - if (ifaces[i] == NULL) { + if (new_ifaces == NULL) { free_tokenizer( &tok ); + wrunlock(); return false; } - i++; - } + for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { + new_ifaces[i] = strdup(token); + if (new_ifaces[i] == NULL) { + for (int j = 0; j < i; ++j) { + free(new_ifaces[j]); + } + free(new_ifaces); + free_tokenizer( &tok ); + wrunlock(); + return false; + } + i++; + } + if (old_ifaces != NULL) { + for (int j = 0; j < MAX_IFACES; ++j) { + free(old_ifaces[j]); + } + free(old_ifaces); + } + *_ifaces = new_ifaces; free_tokenizer( &tok ); version++; wrunlock(); From f23a625aed0afc5c872e56689c8b3b76879daa01 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:28:17 +0000 Subject: [PATCH 168/227] fix: release test server interface locks on failure Build replacement interface arrays before publishing them, clean up partial strdup results, and release the write lock on every allocation-failure path. This prevents failed test-server interface updates from leaking memory or blocking later writers. --- test/tap/tap/SQLite3_Server.cpp | 35 +++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/test/tap/tap/SQLite3_Server.cpp b/test/tap/tap/SQLite3_Server.cpp index 97939a87d2..aa4717b3e7 100644 --- a/test/tap/tap/SQLite3_Server.cpp +++ b/test/tap/tap/SQLite3_Server.cpp @@ -194,22 +194,37 @@ class sqlite3server_main_loop_listeners { bool update_ifaces(char *list, char ***_ifaces) { wrlock(); - int i; - char **ifaces=*_ifaces; + int i = 0; + char **old_ifaces = *_ifaces; + char **new_ifaces = (char **)calloc(MAX_IFACES, sizeof(char *)); tokenizer_t tok; tokenizer( &tok, list, ";", TOKENIZER_NO_EMPTIES ); const char* token; - ifaces=reset_ifaces(ifaces); - i=0; - for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - char *token_copy = strdup(token); - if (token_copy == NULL) { + if (new_ifaces == NULL) { free_tokenizer( &tok ); + wrunlock(); return false; } - ifaces[i]=token_copy; - i++; - } + for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { + new_ifaces[i] = strdup(token); + if (new_ifaces[i] == NULL) { + for (int j = 0; j < i; ++j) { + free(new_ifaces[j]); + } + free(new_ifaces); + free_tokenizer( &tok ); + wrunlock(); + return false; + } + i++; + } + if (old_ifaces != NULL) { + for (int j = 0; j < MAX_IFACES; ++j) { + free(old_ifaces[j]); + } + free(old_ifaces); + } + *_ifaces = new_ifaces; free_tokenizer( &tok ); version++; wrunlock(); From 19f92920fdca86c2f53041d29bd45036c5922d99 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:28:45 +0000 Subject: [PATCH 169/227] fix: preserve ClickHouse interfaces on allocation failure Build a replacement interface array before publishing it and retain the existing array until every strdup succeeds. Clean up partial allocations and release the write lock on failure to avoid deadlocks and loss of the active interface set. --- lib/ClickHouse_Server.cpp | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/ClickHouse_Server.cpp b/lib/ClickHouse_Server.cpp index d777b2a68e..226be1c682 100644 --- a/lib/ClickHouse_Server.cpp +++ b/lib/ClickHouse_Server.cpp @@ -566,22 +566,37 @@ class sqlite3server_main_loop_listeners { bool update_ifaces(char *list, char ***_ifaces) { wrlock(); - int i; - char **ifaces=*_ifaces; + int i = 0; + char **old_ifaces = *_ifaces; + char **new_ifaces = (char **)calloc(MAX_IFACES, sizeof(char *)); tokenizer_t tok; tokenizer( &tok, list, ";", TOKENIZER_NO_EMPTIES ); const char* token; - ifaces=reset_ifaces(ifaces); - i=0; - for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - char *token_copy = strdup(token); - if (token_copy == NULL) { + if (new_ifaces == NULL) { free_tokenizer( &tok ); + wrunlock(); return false; } - ifaces[i]=token_copy; - i++; - } + for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { + new_ifaces[i] = strdup(token); + if (new_ifaces[i] == NULL) { + for (int j = 0; j < i; ++j) { + free(new_ifaces[j]); + } + free(new_ifaces); + free_tokenizer( &tok ); + wrunlock(); + return false; + } + i++; + } + if (old_ifaces != NULL) { + for (int j = 0; j < MAX_IFACES; ++j) { + free(old_ifaces[j]); + } + free(old_ifaces); + } + *_ifaces = new_ifaces; free_tokenizer( &tok ); version++; wrunlock(); From feac1d5848fe762d5f72d4c08067fd6a574ce57b Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:29:03 +0000 Subject: [PATCH 170/227] fix: handle min GTID allocation failure Check the buffer allocation before copying a validated min_gtid annotation. Leave the existing query-processor value unchanged and report the allocation failure instead of dereferencing a null pointer. --- include/MySQL_Query_Processor.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/MySQL_Query_Processor.h b/include/MySQL_Query_Processor.h index b14a3400fa..bd655a0d3a 100644 --- a/include/MySQL_Query_Processor.h +++ b/include/MySQL_Query_Processor.h @@ -81,6 +81,10 @@ class MySQL_Query_Processor : public Query_Processor { size_t l = strlen(value); if (_is_valid_gtid((char*)value, l)) { char* buf = (char*)malloc(l + 1); + if (buf == nullptr) { + proxy_warning("Unable to allocate memory for min_gtid=%s\n", value); + return; + } memcpy(buf, value, l); buf[l] = '\0'; From 71fc810a1a021c15f750de055b267b8574564b57 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:29:23 +0000 Subject: [PATCH 171/227] fix: reject oversized Unix socket paths Check the formatted sun_path length before binding the socket. Return a failure with ENAMETOOLONG when the requested path cannot fit, preventing bind from using a truncated path while unlink and chmod still reference the original. --- lib/network.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/network.cpp b/lib/network.cpp index fe1529cea9..3111983ff9 100644 --- a/lib/network.cpp +++ b/lib/network.cpp @@ -104,7 +104,13 @@ int listen_on_unix(char *path, int backlog) { memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sun_family = AF_UNIX; - snprintf(serveraddr.sun_path, sizeof(serveraddr.sun_path), "%s", path); + const int path_len = snprintf(serveraddr.sun_path, sizeof(serveraddr.sun_path), "%s", path); + if (path_len < 0 || (size_t)path_len >= sizeof(serveraddr.sun_path)) { + close(sd); + errno = (path_len < 0) ? EINVAL : ENAMETOOLONG; + proxy_error("Unix Socket path is too long: %s\n", path); + return -1; + } // call bind() to bind the socket on the specified file if ( bind(sd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_un)) != 0 ) { From 8ffd93c2c21f8791f91c4f4976fe38a5d5f4c461 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:29:38 +0000 Subject: [PATCH 172/227] fix: free demangled debug symbols Release the buffer returned by abi::__cxa_demangle after each backtrace frame is appended to the verbose debug message. This prevents a leak for every demangled symbol while preserving the existing logging output. --- lib/debug.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/debug.cpp b/lib/debug.cpp index bee184c323..6aa1d128dc 100644 --- a/lib/debug.cpp +++ b/lib/debug.cpp @@ -255,6 +255,7 @@ extern "C" void proxy_debug_func( ); } } + free(realname); } free(strings); } From 783006c53502e6b65f19f0e4b6faad315c7c0f2a Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:30:11 +0000 Subject: [PATCH 173/227] fix: reject truncated GTID records and UUIDs Disconnect when a binlog-reader record exceeds the fixed parsing buffer instead of applying a prefix. Also reject I1 and I3 UUID fields that cannot fit in uuid_server, preventing truncated identities from corrupting GTID state. --- lib/GTID_Server_Data.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/GTID_Server_Data.cpp b/lib/GTID_Server_Data.cpp index 52b35ba9ac..56d1da757b 100644 --- a/lib/GTID_Server_Data.cpp +++ b/lib/GTID_Server_Data.cpp @@ -391,7 +391,14 @@ bool GTID_Server_Data::read_next_gtid() { events_read++; } } else { - size_t rec_msg_len = (l >= (int)sizeof(rec_msg)) ? (sizeof(rec_msg)-1) : (size_t)l; + if (l >= (int)sizeof(rec_msg)) { + pos += l + 1; + proxy_warning("GTID: oversized message from binlog reader on port %d for server %s:%d, disconnecting\n", + port, address, mysql_port); + active = false; + return false; + } + size_t rec_msg_len = (size_t)l; memcpy(rec_msg, data + pos, rec_msg_len); pos += l+1; rec_msg[rec_msg_len] = 0; @@ -408,7 +415,11 @@ bool GTID_Server_Data::read_next_gtid() { } ul = a-rec_msg-3; { - size_t uuid_len = (ul >= 0 && (size_t)ul < sizeof(uuid_server)) ? (size_t)ul : (sizeof(uuid_server)-1); + if (ul < 0 || (size_t)ul >= sizeof(uuid_server)) { + invalid_msg = true; + break; + } + size_t uuid_len = (size_t)ul; memcpy(uuid_server, rec_msg+3, uuid_len); uuid_server[uuid_len] = 0; } @@ -427,7 +438,11 @@ bool GTID_Server_Data::read_next_gtid() { } ul = a-rec_msg-3; { - size_t uuid_len = (ul >= 0 && (size_t)ul < sizeof(uuid_server)) ? (size_t)ul : (sizeof(uuid_server)-1); + if (ul < 0 || (size_t)ul >= sizeof(uuid_server)) { + invalid_msg = true; + break; + } + size_t uuid_len = (size_t)ul; memcpy(uuid_server, rec_msg+3, uuid_len); uuid_server[uuid_len] = 0; } From c56b1891bf7403b0e045edf2ee6c2af66a719028 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:30:30 +0000 Subject: [PATCH 174/227] fix: normalize peer checksum fields before comparison Treat a NULL checksum column as an empty string before calling strcmp. This keeps cluster checksum processing null-safe while preserving the bounded copy and synchronization decisions for non-null values. --- lib/ProxySQL_Cluster.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index 707b3ba67d..aba46e7140 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -591,8 +591,8 @@ static void process_component_checksum( checksum.epoch = atoll(row[2]); checksum.last_updated = now; - if (strcmp(checksum.checksum, row[3])) { - const char *checksum_source = row[3] ? row[3] : ""; + const char *checksum_source = row[3] ? row[3] : ""; + if (strcmp(checksum.checksum, checksum_source)) { snprintf(checksum.checksum, ProxySQL_Checksum_Value_LENGTH, "%s", checksum_source); checksum.last_changed = now; checksum.diff_check = 1; From 377edfaca50f60017941cd95fcd509505abdc040 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:30:44 +0000 Subject: [PATCH 175/227] api: add capacity to MCP variable reads Extend MCP_Threads_Handler::get_variable with the caller-provided output-buffer size. The implementation can now bound formatting according to the actual destination instead of relying on its historical 1024-byte assumption. --- plugins/genai/include/MCP_Thread.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/genai/include/MCP_Thread.h b/plugins/genai/include/MCP_Thread.h index b91168b375..e47ecb89ab 100644 --- a/plugins/genai/include/MCP_Thread.h +++ b/plugins/genai/include/MCP_Thread.h @@ -270,6 +270,7 @@ class MCP_Threads_Handler * * @param name The name of the variable (without 'mcp-' prefix) * @param val Output buffer to store the value + * @param val_size Size of the output buffer in bytes * @return 0 on success, -1 if variable not found * * @deprecated The unbounded sprintf into `val` is a stack-buffer @@ -278,7 +279,7 @@ class MCP_Threads_Handler * operators). Prefer `get_variable_string()` below. Retained * for callers that haven't been updated yet. */ - int get_variable(const char* name, char* val); + int get_variable(const char* name, char* val, size_t val_size); /** * @brief Get the value of a variable as a std::string. From 5528ead7b7096b2e7564c1a480f7e2d2261c7513 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:31:34 +0000 Subject: [PATCH 176/227] fix: bound MCP variable output formatting Require a valid destination buffer and use its supplied capacity when copying MCP variable values. Long endpoint credentials are now truncated only within the caller-owned buffer instead of relying on a fixed 1024-byte limit. --- plugins/genai/src/MCP_Thread.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/genai/src/MCP_Thread.cpp b/plugins/genai/src/MCP_Thread.cpp index c516a30347..e3d927c133 100644 --- a/plugins/genai/src/MCP_Thread.cpp +++ b/plugins/genai/src/MCP_Thread.cpp @@ -168,8 +168,8 @@ void MCP_Threads_Handler::wrunlock() { pthread_rwlock_unlock(&rwlock); } -int MCP_Threads_Handler::get_variable(const char* name, char* val) { - if (!name || !val) +int MCP_Threads_Handler::get_variable(const char* name, char* val, size_t val_size) { + if (!name || !val || val_size == 0) return -1; pthread_rwlock_rdlock(&rwlock); @@ -210,7 +210,7 @@ int MCP_Threads_Handler::get_variable(const char* name, char* val) { pthread_rwlock_unlock(&rwlock); if (rc == 0) { - snprintf(val, 1024, "%s", out.c_str()); + snprintf(val, val_size, "%s", out.c_str()); } return rc; } From 1a8b67b268b7db8f2ea40756a92a864816aa745c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:31:54 +0000 Subject: [PATCH 177/227] fix: pass MCP output buffer capacities Update both configuration-tool callers to pass the actual size of their stack value buffers to get_variable. This preserves their current output format while enforcing the new bounded API contract. --- plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp b/plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp index c330a6038e..706e818c6f 100644 --- a/plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp +++ b/plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp @@ -421,7 +421,7 @@ json Config_Tool_Handler::handle_get_config(const std::string& var_name) { } char val[1024]; - if (mcp_handler->get_variable(var_name.c_str(), val) == 0) { + if (mcp_handler->get_variable(var_name.c_str(), val, sizeof(val)) == 0) { json result; result["variable_name"] = var_name; result["value"] = val; @@ -548,7 +548,7 @@ json Config_Tool_Handler::handle_list_variables(const std::string& filter) { } char val[1024]; - if (mcp_handler->get_variable(var_name.c_str(), val) == 0) { + if (mcp_handler->get_variable(var_name.c_str(), val, sizeof(val)) == 0) { json var; var["name"] = var_name; var["value"] = val; From 09d7a5484c00336576369e700d8c31a3f2939d1d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:32:34 +0000 Subject: [PATCH 178/227] fix: handle TLS path allocation failures Check each bootstrap path allocation before formatting and accessing it, and release previously allocated paths when a later allocation fails. Return a TLS initialization error instead of continuing with null path buffers. --- src/proxy_tls.cpp | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/proxy_tls.cpp b/src/proxy_tls.cpp index 3703cccd83..45f77d4d38 100644 --- a/src/proxy_tls.cpp +++ b/src/proxy_tls.cpp @@ -233,24 +233,45 @@ int ssl_mkit(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int days, boo // check if files exists if (bootstrap == true) { - ssl_key_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_key_rp)+8); - snprintf(ssl_key_fp, strlen(GloVars.datadir)+strlen(ssl_key_rp)+2, "%s/%s",GloVars.datadir,ssl_key_rp); + const size_t key_path_len = strlen(GloVars.datadir) + strlen(ssl_key_rp) + 2; + ssl_key_fp = (char *)malloc(key_path_len); + if (ssl_key_fp == NULL) { + msg = "Unable to allocate memory for the TLS key path"; + return 1; + } + snprintf(ssl_key_fp, key_path_len, "%s/%s",GloVars.datadir,ssl_key_rp); } if (access(ssl_key_fp, R_OK)) { ssl_key_exists = false; } if (bootstrap == true) { - ssl_cert_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_cert_rp)+8); - snprintf(ssl_cert_fp, strlen(GloVars.datadir)+strlen(ssl_cert_rp)+2, "%s/%s",GloVars.datadir,ssl_cert_rp); + const size_t cert_path_len = strlen(GloVars.datadir) + strlen(ssl_cert_rp) + 2; + ssl_cert_fp = (char *)malloc(cert_path_len); + if (ssl_cert_fp == NULL) { + free(ssl_key_fp); + ssl_key_fp = NULL; + msg = "Unable to allocate memory for the TLS certificate path"; + return 1; + } + snprintf(ssl_cert_fp, cert_path_len, "%s/%s",GloVars.datadir,ssl_cert_rp); } if (access(ssl_cert_fp, R_OK)) { ssl_cert_exists = false; } if (bootstrap == true) { - ssl_ca_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_ca_rp)+8); - snprintf(ssl_ca_fp, strlen(GloVars.datadir)+strlen(ssl_ca_rp)+2, "%s/%s",GloVars.datadir,ssl_ca_rp); + const size_t ca_path_len = strlen(GloVars.datadir) + strlen(ssl_ca_rp) + 2; + ssl_ca_fp = (char *)malloc(ca_path_len); + if (ssl_ca_fp == NULL) { + free(ssl_key_fp); + free(ssl_cert_fp); + ssl_key_fp = NULL; + ssl_cert_fp = NULL; + msg = "Unable to allocate memory for the TLS CA path"; + return 1; + } + snprintf(ssl_ca_fp, ca_path_len, "%s/%s",GloVars.datadir,ssl_ca_rp); } if (access(ssl_ca_fp, R_OK)) { ssl_ca_exists = false; From 5b80cd218c4c9646b95fe162fe9cb349c9f255b1 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:32:55 +0000 Subject: [PATCH 179/227] fix: validate client2 statement allocation Fail the benchmark immediately when the statement-handle array cannot be allocated, before the preparation loop dereferences it. Also align the text-protocol format specifier with the signed loop counter. --- test/PrepStmt/client2.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/PrepStmt/client2.cpp b/test/PrepStmt/client2.cpp index ae979999ec..8a2eb18f06 100644 --- a/test/PrepStmt/client2.cpp +++ b/test/PrepStmt/client2.cpp @@ -75,6 +75,10 @@ int main() { } int i; stmt=(MYSQL_STMT **)malloc(sizeof(MYSQL_STMT*)*NUMPREP); + if (stmt == NULL) { + fprintf(stderr, "Unable to allocate statement handles\n"); + exit(EXIT_FAILURE); + } { cpu_timer t; for (i=0; i Date: Mon, 10 Aug 2026 17:33:08 +0000 Subject: [PATCH 180/227] fix: validate client9 query allocation Check the dynamically allocated prepared-statement query before formatting it. The test now exits cleanly on allocation failure instead of passing a null destination to snprintf. --- test/PrepStmt/client9.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/PrepStmt/client9.cpp b/test/PrepStmt/client9.cpp index ac21dbe234..bc3f78d9c9 100644 --- a/test/PrepStmt/client9.cpp +++ b/test/PrepStmt/client9.cpp @@ -64,6 +64,10 @@ void run(MYSQL *mysql) { uint32_t r=(uint32_t)mt_rand(); r=r%3000; char *query=(char *)malloc(strlen(QUERY1)+16); + if (query == NULL) { + fprintf(stderr, "Unable to allocate statement query\n"); + exit(EXIT_FAILURE); + } snprintf(query,strlen(QUERY1)+16,QUERY1,r); if (DBG) { fprintf(stdout,"%s\n",query); From 9c98b1fe3bfbc7019b1cbfc1090b9d4fa40c29a1 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:33:30 +0000 Subject: [PATCH 181/227] fix: validate benchmark thread allocations Check both thread-state arrays before initializing worker arguments or pthread handles. Free whichever allocation succeeded and return an error when either calloc fails. --- tools/bench_connect.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/bench_connect.c b/tools/bench_connect.c index 709a6b5332..c65bc8df5f 100644 --- a/tools/bench_connect.c +++ b/tools/bench_connect.c @@ -124,6 +124,12 @@ int main(int argc, char **argv) { // NOSONAR: benchmark tool, cognitive complexi /* Launch threads */ pthread_t *tids = calloc(threads, sizeof(pthread_t)); struct thread_arg *args = calloc(threads, sizeof(struct thread_arg)); + if (tids == NULL || args == NULL) { + fprintf(stderr, "Unable to allocate benchmark thread state\n"); + free(tids); + free(args); + return 1; + } struct timespec wall0, wall1; clock_gettime(CLOCK_MONOTONIC, &wall0); From 7ea1331a64e4c74033e16dc79a9eb1b013910352 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:33:44 +0000 Subject: [PATCH 182/227] fix: match client3 query format types Use the signed conversion for the int loop counter and retain the unsigned conversion for the explicitly cast random operand. This keeps the prepared-statement benchmark formatting type-correct. --- test/PrepStmt/client3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/PrepStmt/client3.cpp b/test/PrepStmt/client3.cpp index 05e52dcfc9..93a0084575 100644 --- a/test/PrepStmt/client3.cpp +++ b/test/PrepStmt/client3.cpp @@ -176,7 +176,7 @@ void * mysql_thread(int tid) { // for comparison, we run also queries in TEXT protocol cpu_timer t; for (i=0; i Date: Mon, 10 Aug 2026 17:33:52 +0000 Subject: [PATCH 183/227] fix: match client4 query format types Use the signed conversion for the int loop counter while keeping the random unsigned operand on its matching conversion. This removes undefined variadic formatting behavior in the benchmark. --- test/PrepStmt/client4.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/PrepStmt/client4.cpp b/test/PrepStmt/client4.cpp index e7babe6d71..40506aa2ac 100644 --- a/test/PrepStmt/client4.cpp +++ b/test/PrepStmt/client4.cpp @@ -166,7 +166,7 @@ void * mysql_thread() { // for comparison, we run also queries in TEXT protocol cpu_timer t; for (i=0; i Date: Mon, 10 Aug 2026 17:34:05 +0000 Subject: [PATCH 184/227] fix: match digest test format types Use unsigned conversions for both loop-counter arguments passed to the generated DO statement. This matches the declared unsigned int type and avoids variadic format mismatches. --- test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp b/test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp index b86731175f..7ce4cbb5d1 100644 --- a/test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp +++ b/test/tap/tests/reg_test_4399-stats_mysql_query_digest-t.cpp @@ -64,7 +64,7 @@ int main(int argc, char** argv) { char query[128]{}; diag("Generating simulated traffic..."); for (unsigned int i=0; i < QUERY_COUNT; i++) { - snprintf(query, sizeof(query), "DO /*#%d#*/ %d", i, i); + snprintf(query, sizeof(query), "DO /*#%u#*/ %u", i, i); MYSQL_QUERY(proxysql, query); std::this_thread::sleep_for(std::chrono::milliseconds(1)); } From e596031410d502e35ae1e910765273202be43ce4 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:34:28 +0000 Subject: [PATCH 185/227] fix: match MYSQL_TIME format specifiers Use unsigned conversions for MYSQL_TIME hour, minute, and second fields in the statement-metadata checks. The generated diagnostics now match the field types without changing NULL handling or comparison behavior. --- test/tap/tests/reg_test_3585-stmt_metadata-t.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp b/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp index b7a753a405..9c947074a6 100644 --- a/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp +++ b/test/tap/tests/reg_test_3585-stmt_metadata-t.cpp @@ -248,14 +248,14 @@ int insert_and_check(MYSQL_STMT *stmti, MYSQL_STMT *stmts, int id, char *name1, } char buf1[256], buf2[256]; if (ts1) { - snprintf(buf1,sizeof(buf1),"%d:%d:%d", ts1->hour, ts1->minute, ts1->second); + snprintf(buf1,sizeof(buf1),"%u:%u:%u", ts1->hour, ts1->minute, ts1->second); } else { snprintf(buf1,sizeof(buf1),"NULL"); } if (is_null[2]) { snprintf(buf2,sizeof(buf2),"NULL"); } else { - snprintf(buf2,sizeof(buf2),"%d:%d:%d", ts_res1.hour, ts_res1.minute, ts_res1.second); + snprintf(buf2,sizeof(buf2),"%u:%u:%u", ts_res1.hour, ts_res1.minute, ts_res1.second); } if (strcmp(buf1,buf2)==0) matches++; @@ -277,14 +277,14 @@ int insert_and_check(MYSQL_STMT *stmti, MYSQL_STMT *stmts, int id, char *name1, matches++; } if (ts2) { - snprintf(buf1,sizeof(buf1),"%d:%d:%d", ts2->hour, ts2->minute, ts2->second); + snprintf(buf1,sizeof(buf1),"%u:%u:%u", ts2->hour, ts2->minute, ts2->second); } else { snprintf(buf1,sizeof(buf1),"NULL"); } if (is_null[5]) { snprintf(buf2,sizeof(buf2),"NULL"); } else { - snprintf(buf2,sizeof(buf2),"%d:%d:%d", ts_res2.hour, ts_res2.minute, ts_res2.second); + snprintf(buf2,sizeof(buf2),"%u:%u:%u", ts_res2.hour, ts_res2.minute, ts_res2.second); } if (strcmp(buf1,buf2)==0) matches++; From 2682a953476d8a35a0bb5a7509c87da4eca11d4a Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:34:40 +0000 Subject: [PATCH 186/227] fix: match second metadata time types Use unsigned conversions for MYSQL_TIME hour, minute, and second fields in the second statement-metadata regression test. This removes variadic format mismatches while preserving the existing assertions. --- test/tap/tests/reg_test_3603-stmt_metadata-t.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp b/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp index 7fe30d22cb..6c8231cc48 100644 --- a/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp +++ b/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp @@ -397,14 +397,14 @@ int update_and_check( { if (end_time) { - snprintf(buf1,sizeof(buf1),"%d:%d:%d", end_time->hour, end_time->minute, end_time->second); + snprintf(buf1,sizeof(buf1),"%u:%u:%u", end_time->hour, end_time->minute, end_time->second); } else { snprintf(buf1,sizeof(buf1),"NULL"); } if (is_null[4]) { snprintf(buf2,sizeof(buf2),"NULL"); } else { - snprintf(buf2,sizeof(buf2),"%d:%d:%d", ts_end_time.hour, ts_end_time.minute, ts_end_time.second); + snprintf(buf2,sizeof(buf2),"%u:%u:%u", ts_end_time.hour, ts_end_time.minute, ts_end_time.second); } if (strcmp(buf1,buf2)==0) matches++; @@ -468,14 +468,14 @@ int update_and_check( { if (st_time) { - snprintf(buf1,sizeof(buf1),"%d:%d:%d", st_time->hour, st_time->minute, st_time->second); + snprintf(buf1,sizeof(buf1),"%u:%u:%u", st_time->hour, st_time->minute, st_time->second); } else { snprintf(buf1,sizeof(buf1),"NULL"); } if (is_null[10]) { snprintf(buf2,sizeof(buf2),"NULL"); } else { - snprintf(buf2,sizeof(buf2),"%d:%d:%d", ts_st_time.hour, ts_st_time.minute, ts_st_time.second); + snprintf(buf2,sizeof(buf2),"%u:%u:%u", ts_st_time.hour, ts_st_time.minute, ts_st_time.second); } if (strcmp(buf1,buf2)==0) matches++; From 1ab158e5555fc50313a01bfe458cd5c17f0be77b Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:36:18 +0000 Subject: [PATCH 187/227] fix: use unsigned duration formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Match nice_time’s ulong duration values with the unsigned-long conversion specifier for days, hours, and minutes. Preserve the existing output wording and truncation checks while removing variadic type mismatches. --- test/tap/tap/tap.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/tap/tap/tap.cpp b/test/tap/tap/tap.cpp index b466cd0fd8..74bb4d02cb 100644 --- a/test/tap/tap/tap.cpp +++ b/test/tap/tap/tap.cpp @@ -442,7 +442,7 @@ static void nice_time(double sec,char *buff, my_bool part_second) { tmp=(ulong) (sec/(3600.0*24)); sec-=3600.0*24*tmp; - int n = snprintf(p, remaining, "%ld %s", tmp, tmp > 1 ? " days " : " day "); + int n = snprintf(p, remaining, "%lu %s", tmp, tmp > 1 ? " days " : " day "); if (n < 0 || (size_t)n >= remaining) return; p += n; remaining -= n; @@ -451,7 +451,7 @@ static void nice_time(double sec,char *buff, my_bool part_second) { tmp=(ulong) (sec/3600.0); sec-=3600.0*tmp; - int n = snprintf(p, remaining, "%ld %s", tmp, tmp > 1 ? " hours " : " hour "); + int n = snprintf(p, remaining, "%lu %s", tmp, tmp > 1 ? " hours " : " hour "); if (n < 0 || (size_t)n >= remaining) return; p += n; remaining -= n; @@ -460,7 +460,7 @@ static void nice_time(double sec,char *buff, my_bool part_second) { tmp=(ulong) (sec/60.0); sec-=60.0*tmp; - int n = snprintf(p, remaining, "%ld min ", tmp); + int n = snprintf(p, remaining, "%lu min ", tmp); if (n < 0 || (size_t)n >= remaining) return; p += n; remaining -= n; From 3715865b80554c996be85099e33662112be9ca1e Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:36:36 +0000 Subject: [PATCH 188/227] fix: validate PostgreSQL startup message boundaries Extract query keywords up to any whitespace without dropping the final character, so keyword-only SELECT and COPY commands are classified correctly. Check capacity before writing the startup-message terminator to prevent a one-byte overflow. --- .../tests/pgsql-connection_parameters_test-t.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/tap/tests/pgsql-connection_parameters_test-t.cpp b/test/tap/tests/pgsql-connection_parameters_test-t.cpp index c8e2367c7f..d372de70a9 100644 --- a/test/tap/tests/pgsql-connection_parameters_test-t.cpp +++ b/test/tap/tests/pgsql-connection_parameters_test-t.cpp @@ -64,14 +64,10 @@ PGConnPtr createNewConnection(ConnType conn_type, const std::string& parameters return PGConnPtr(conn, &PQfinish); } -bool executeQueries(PGconn* conn, const std::vector& queries) { - auto fnResultType = [](const char* query) -> int { - const char* fs = strchr(query, ' '); - size_t qtlen = strlen(query); - if (fs != NULL) { - qtlen = (fs - query) + 1; - } - std::string query_type(query, qtlen - 1); + bool executeQueries(PGconn* conn, const std::vector& queries) { + auto fnResultType = [](const char* query) -> int { + const size_t qtlen = strcspn(query, " \t\r\n"); + std::string query_type(query, qtlen); for (char& c : query_type) { c = static_cast(toupper((unsigned char)c)); } @@ -311,6 +307,9 @@ void send_startup_message(int sock, const std::vector= sizeof(msg)) { + return; + } msg[offset++] = '\0'; send(sock, msg, offset, 0); From 07b7a6627d9d4d7ce7a9415c275ece469505c137 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:38:53 +0000 Subject: [PATCH 189/227] fix: reject oversized numeric COPY values Stop truncating numeric input into the fixed 128-byte assembly buffer. Return an explicit encoding error for values that cannot fit, abort the affected test transmission, and keep PostgreSQL numeric metadata aligned with the bytes actually encoded. --- test/tap/tests/pgsql-copy_from_test-t.cpp | 24 ++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/test/tap/tests/pgsql-copy_from_test-t.cpp b/test/tap/tests/pgsql-copy_from_test-t.cpp index 6da122a177..3cc7691f10 100644 --- a/test/tap/tests/pgsql-copy_from_test-t.cpp +++ b/test/tap/tests/pgsql-copy_from_test-t.cpp @@ -135,7 +135,7 @@ void write_int16(uint8_t* dest, int16_t value) { dest[1] = value & 0xFF; } -bool encodeNumericBinary(uint8_t* out, const char* numStr) { +int encodeNumericBinary(uint8_t* out, const char* numStr) { int16_t numDigits = 0, weight = 0, sign = 0x0000, scale = 0; int16_t digits[64] = { 0 }; // Temporary storage for up to 64 4-digit groups size_t digitCount = 0; @@ -154,12 +154,15 @@ bool encodeNumericBinary(uint8_t* out, const char* numStr) { // Combine integer and fractional parts into a single string of digits char combined[128] = { 0 }; - size_t copy_len = std::min(intPartLen, sizeof(combined)-1); + if (intPartLen >= sizeof(combined) || fracPartLen > sizeof(combined) - 1 - intPartLen) { + return -1; + } + size_t copy_len = intPartLen; memcpy(combined, numericPart, copy_len); combined[copy_len] = 0; if (fracPartLen > 0) { size_t combined_len = strlen(combined); - size_t copy_len_frac = std::min(fracPartLen, sizeof(combined) - combined_len - 1); + size_t copy_len_frac = fracPartLen; memcpy(combined + combined_len, dotPos + 1, copy_len_frac); combined[combined_len + copy_len_frac] = 0; } @@ -470,8 +473,13 @@ void testSTDIN_TEXT_BINARY(PGconn* admin_conn, PGconn* conn, std::fstream& f_pro } else if (columns_type[j] == NUMERIC) { uint8_t* prev_pos = (row + offset); offset += sizeof(int32_t); - bool has_digits = encodeNumericBinary(row + offset, data.c_str()); - if (has_digits) { + int digit_count = encodeNumericBinary(row + offset, data.c_str()); + if (digit_count < 0) { + fprintf(stderr, "Numeric value is too long for the binary COPY test buffer: %s\n", data.c_str()); + success = false; + break; + } + if (digit_count > 0) { write_int32(prev_pos, 12); offset += 12; } else { @@ -480,6 +488,9 @@ void testSTDIN_TEXT_BINARY(PGconn* admin_conn, PGconn* conn, std::fstream& f_pro } } } + if (!success) { + break; + } bool last = (i == (test_data.size() - 1)); @@ -494,6 +505,9 @@ void testSTDIN_TEXT_BINARY(PGconn* admin_conn, PGconn* conn, std::fstream& f_pro } ok(success, "Copy data transmission should be successful"); + if (!success) { + return; + } PGresult* res = PQgetResult(conn); From a5522b452272c25fd9f346e09ff2f1c0def4cd2f Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:39:09 +0000 Subject: [PATCH 190/227] fix: reject truncated set-testing queries Check the snprintf result before sending generated SQL to MySQL. Oversized addresses now count as a failed iteration and are skipped instead of executing a silently truncated query. --- test/tap/tests/set_testing-240-t.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/tap/tests/set_testing-240-t.cpp b/test/tap/tests/set_testing-240-t.cpp index 3e2c15eb7f..99d983cbfb 100644 --- a/test/tap/tests/set_testing-240-t.cpp +++ b/test/tap/tests/set_testing-240-t.cpp @@ -281,7 +281,13 @@ void * my_conn_thread(void *arg) { usleep(sleepDelay * 1000); char query[128]; - snprintf(query, sizeof(query), "SELECT /* %p %s */ %d;", mysql, paddress.c_str(), sleepDelay); + const int query_len = snprintf(query, sizeof(query), "SELECT /* %p %s */ %d;", mysql, paddress.c_str(), sleepDelay); + if (query_len < 0 || (size_t)query_len >= sizeof(query)) { + diag("Skipping truncated query for address of length %zu", paddress.size()); + select_ERR++; + __sync_fetch_and_add(&g_select_ERR,1); + continue; + } if (mysql_query(mysql,query)) { select_ERR++; __sync_fetch_and_add(&g_select_ERR,1); From c087731da392ff695dc435fa50f23a8e10fae466 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:39:20 +0000 Subject: [PATCH 191/227] fix: detect truncated queries in set testing Validate the formatted query length before calling mysql_query. The stress test now records and skips an oversized address rather than sending incomplete SQL to the server. --- test/tap/tests/set_testing-t.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/tap/tests/set_testing-t.cpp b/test/tap/tests/set_testing-t.cpp index f4fe8b89d8..e1b9adb29d 100644 --- a/test/tap/tests/set_testing-t.cpp +++ b/test/tap/tests/set_testing-t.cpp @@ -207,7 +207,13 @@ void * my_conn_thread(void *arg) { usleep(sleepDelay * 1000); char query[128]; - snprintf(query, sizeof(query), "SELECT /* %p %s */ %d;", mysql, paddress.c_str(), sleepDelay); + const int query_len = snprintf(query, sizeof(query), "SELECT /* %p %s */ %d;", mysql, paddress.c_str(), sleepDelay); + if (query_len < 0 || (size_t)query_len >= sizeof(query)) { + diag("Skipping truncated query for address of length %zu", paddress.size()); + select_ERR++; + __sync_fetch_and_add(&g_select_ERR,1); + continue; + } if (mysql_query(mysql,query)) { select_ERR++; __sync_fetch_and_add(&g_select_ERR,1); From fb0efebb9529f792dc8befb3f9baff5ca8c27a6b Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:39:45 +0000 Subject: [PATCH 192/227] style: rename PostgreSQL hash delimiter macros Use conventional uppercase macro names without leading or trailing underscores for the PostgreSQL connection hash delimiters. Update every reference while preserving the hash input bytes and resulting values. --- lib/PgSQL_Connection.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/PgSQL_Connection.cpp b/lib/PgSQL_Connection.cpp index 4be26c144b..c00eda3923 100644 --- a/lib/PgSQL_Connection.cpp +++ b/lib/PgSQL_Connection.cpp @@ -52,10 +52,10 @@ uint64_t PgSQL_Connection_userinfo::compute_hash() { size_t dbname_len = dbname ? strlen(dbname) : 0; size_t l = username_len + password_len + dbname_len; // two random seperator -#define _COMPUTE_HASH_DEL1_ "-ujhtgf76y576574fhYTRDF345wdt-" -#define _COMPUTE_HASH_DEL2_ "-8k7jrhtrgJHRgrefgreyhtRFewg6-" - size_t delimiter1_len = strlen(_COMPUTE_HASH_DEL1_); - size_t delimiter2_len = strlen(_COMPUTE_HASH_DEL2_); +#define COMPUTE_HASH_DELIMITER_1 "-ujhtgf76y576574fhYTRDF345wdt-" +#define COMPUTE_HASH_DELIMITER_2 "-8k7jrhtrgJHRgrefgreyhtRFewg6-" + size_t delimiter1_len = strlen(COMPUTE_HASH_DELIMITER_1); + size_t delimiter2_len = strlen(COMPUTE_HASH_DELIMITER_2); l += delimiter1_len + delimiter2_len; std::string hash_input; @@ -63,14 +63,14 @@ uint64_t PgSQL_Connection_userinfo::compute_hash() { if (username) { hash_input.append(username, username_len); } - hash_input.append(_COMPUTE_HASH_DEL1_); + hash_input.append(COMPUTE_HASH_DELIMITER_1); if (password) { hash_input.append(password, password_len); } if (dbname) { hash_input.append(dbname, dbname_len); } - hash_input.append(_COMPUTE_HASH_DEL2_); + hash_input.append(COMPUTE_HASH_DELIMITER_2); return SpookyHash::Hash64(hash_input.data(), hash_input.size(), 0); } From eb8cb90f6f2e8dcf644430e0aef5ebc2ca0d6122 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:40:38 +0000 Subject: [PATCH 193/227] refactor: manage admin field query buffers Use the project malloc-compatible RAII pointer for the temporary SHOW FIELDS query and release it automatically after each table. Handle allocation failure before formatting or submitting the query. --- test/tap/tests/admin_show_fields_from-t.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/tap/tests/admin_show_fields_from-t.cpp b/test/tap/tests/admin_show_fields_from-t.cpp index 8c654d8589..cd3725d0a3 100644 --- a/test/tap/tests/admin_show_fields_from-t.cpp +++ b/test/tap/tests/admin_show_fields_from-t.cpp @@ -12,6 +12,7 @@ #include "tap.h" #include "command_line.h" #include "utils.h" +#include "proxysql_utils.h" using std::string; @@ -75,17 +76,21 @@ int main() { return -1; } const size_t query_len = strlen(queries[0]) + it->size() + 1; - char *query = (char *) malloc(query_len); + mf_unique_ptr query { (char *)malloc(query_len) }; + if (!query) { + fprintf(stderr, "Unable to allocate query buffer\n"); + mysql_close(proxysql_admin); + return -1; + } for (std::vector::iterator it2 = queries.begin(); it2 != queries.end(); it2++) { - snprintf(query, query_len, *it2, it->c_str()); - diag("Running query: %s", query); - MYSQL_QUERY(proxysql_admin, query); + snprintf(query.get(), query_len, *it2, it->c_str()); + diag("Running query: %s", query.get()); + MYSQL_QUERY(proxysql_admin, query.get()); MYSQL_RES* proxy_res = mysql_store_result(proxysql_admin); unsigned long rows = proxy_res->row_count; ok(rows > 0 , "Number of rows in %s = %lu", it->c_str(), rows); mysql_free_result(proxy_res); } - free(query); mysql_close(proxysql_admin); } From 271ea23ea3b129fe7f1eb937ddaddd842c2263df Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:40:56 +0000 Subject: [PATCH 194/227] refactor: manage table status query buffers Replace the manual malloc/free lifetime for SHOW TABLE STATUS queries with the project malloc-compatible RAII pointer. Report allocation failure before formatting and executing the query. --- test/tap/tests/admin_show_table_status-t.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/tap/tests/admin_show_table_status-t.cpp b/test/tap/tests/admin_show_table_status-t.cpp index efbd68d984..f9c017f6ff 100644 --- a/test/tap/tests/admin_show_table_status-t.cpp +++ b/test/tap/tests/admin_show_table_status-t.cpp @@ -12,6 +12,7 @@ #include "tap.h" #include "command_line.h" #include "utils.h" +#include "proxysql_utils.h" using std::string; @@ -75,17 +76,21 @@ int main() { return -1; } const size_t query_len = strlen(queries[0]) + it->size() + 1; - char *query = (char *) malloc(query_len); + mf_unique_ptr query { (char *)malloc(query_len) }; + if (!query) { + fprintf(stderr, "Unable to allocate query buffer\n"); + mysql_close(proxysql_admin); + return -1; + } for (std::vector::iterator it2 = queries.begin(); it2 != queries.end(); it2++) { - snprintf(query, query_len, *it2, it->c_str()); - diag("Running query: %s", query); - MYSQL_QUERY(proxysql_admin, query); + snprintf(query.get(), query_len, *it2, it->c_str()); + diag("Running query: %s", query.get()); + MYSQL_QUERY(proxysql_admin, query.get()); MYSQL_RES* proxy_res = mysql_store_result(proxysql_admin); unsigned long rows = proxy_res->row_count; ok(rows == 1 , "SHOW TABLE STATUS %s generated %lu row(s)", it->c_str(), rows); mysql_free_result(proxy_res); } - free(query); mysql_close(proxysql_admin); } From 43735c918ff4236d8d0e39cee3b175e545b8d6af Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:44:41 +0000 Subject: [PATCH 195/227] refactor: manage Aurora temporary query buffers Use the project's malloc-compatible RAII pointer for temporary database-user and quoted-table buffers. Check both temporary and duplicated query allocations before continuing, and release temporary storage automatically on success. --- test/tap/tests/aurora.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/test/tap/tests/aurora.cpp b/test/tap/tests/aurora.cpp index f5c5a81e2a..9427db7e01 100644 --- a/test/tap/tests/aurora.cpp +++ b/test/tap/tests/aurora.cpp @@ -10,6 +10,7 @@ #include "MySQL_Data_Stream.h" #include "query_processor.h" #include "SQLite3_Server.h" +#include "proxysql_utils.h" #include #include @@ -272,11 +273,18 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p char *query1=(char *)"SELECT \"admin\" AS 'DATABASE()', \"%s\" AS 'USER()'"; const char* username = sess->client_myds->myconn->userinfo->username; size_t query2_len = strlen(query1) + (username ? strlen(username) : 0) + 1; - char *query2=(char *)malloc(query2_len); - snprintf(query2, query2_len, query1, username ? username : ""); - query=l_strdup(query2); - query_length=strlen(query2)+1; - free(query2); + mf_unique_ptr query2 { (char *)malloc(query2_len) }; + if (!query2) { + l_free(pkt->size-sizeof(mysql_hdr), query_no_space); + return; + } + snprintf(query2.get(), query2_len, query1, username ? username : ""); + query=l_strdup(query2.get()); + if (!query) { + l_free(pkt->size-sizeof(mysql_hdr), query_no_space); + return; + } + query_length=strlen(query)+1; goto __run_query; } } @@ -379,11 +387,13 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p size_t tbh_len = strlen(tbh); if (tbh_len>=3 && tbh[0]=='`' && tbh[tbh_len-1]=='`') { // tablename is quoted size_t db_len = tbh_len - 2; - char *tbh_tmp=(char *)malloc(db_len+1); - memcpy(tbh_tmp,tbh+1,db_len); - tbh_tmp[db_len]=0; - free(tbh); - tbh=tbh_tmp; + mf_unique_ptr tbh_tmp { (char *)malloc(db_len + 1) }; + if (tbh_tmp) { + memcpy(tbh_tmp.get(), tbh + 1, db_len); + tbh_tmp.get()[db_len] = 0; + free(tbh); + tbh = tbh_tmp.release(); + } } int l=strBl+strlen(tbh)*3+strlen(dbh)-8; char *buff=(char *)l_alloc(l+1); From ceb6589c26b93b08800fa70c9c2c6f52f92c8886 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 17:44:45 +0000 Subject: [PATCH 196/227] test: pass capacities to MCP variable reads Update the PROXYSQL40 MCP unit tests for the bounded get_variable API. Every test buffer now supplies its capacity, while null-buffer cases continue to verify rejection without relying on an unsafe legacy call. --- test/tap/tests/unit/genai_mcp_thread_unit-t.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/tap/tests/unit/genai_mcp_thread_unit-t.cpp b/test/tap/tests/unit/genai_mcp_thread_unit-t.cpp index 22d62b82c6..703abaf863 100644 --- a/test/tap/tests/unit/genai_mcp_thread_unit-t.cpp +++ b/test/tap/tests/unit/genai_mcp_thread_unit-t.cpp @@ -36,7 +36,7 @@ /* ------------------------------------------------------------------ */ static std::string get_var(MCP_Threads_Handler& h, const char* name) { char buf[4096] = {0}; - int rc = h.get_variable(name, buf); + int rc = h.get_variable(name, buf, sizeof(buf)); if (rc != 0) return "__ERROR__"; return std::string(buf); } @@ -361,11 +361,11 @@ static void test_get_variables_list(MCP_Threads_Handler& h) { static void test_null_safety(MCP_Threads_Handler& h) { char buf[256]; - ok(h.get_variable(nullptr, buf) == -1, + ok(h.get_variable(nullptr, buf, sizeof(buf)) == -1, "get_variable(nullptr, buf) = -1"); - ok(h.get_variable("enabled", nullptr) == -1, + ok(h.get_variable("enabled", nullptr, 0) == -1, "get_variable(enabled, nullptr) = -1"); - ok(h.get_variable(nullptr, nullptr) == -1, + ok(h.get_variable(nullptr, nullptr, 0) == -1, "get_variable(nullptr, nullptr) = -1"); ok(h.set_variable(nullptr, "true") == -1, @@ -392,9 +392,9 @@ static void test_get_variable_string_contract(MCP_Threads_Handler& h) { */ static void test_get_unknown_variable(MCP_Threads_Handler& h) { char buf[256]; - ok(h.get_variable("nonexistent", buf) == -1, + ok(h.get_variable("nonexistent", buf, sizeof(buf)) == -1, "get_variable(nonexistent) = -1"); - ok(h.get_variable("", buf) == -1, + ok(h.get_variable("", buf, sizeof(buf)) == -1, "get_variable(empty) = -1"); } From b5d246beb3767b9a9360c6450882b76350d58aad Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:22:45 +0000 Subject: [PATCH 197/227] fix: validate Unix socket paths before unlink Reject null and overlong Unix socket paths before touching the filesystem. This prevents an existing socket at an overlong requested path from being unlinked before the path is rejected, and copies only the already-validated path into sockaddr_un. --- lib/network.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/network.cpp b/lib/network.cpp index 3111983ff9..f1c9928381 100644 --- a/lib/network.cpp +++ b/lib/network.cpp @@ -88,6 +88,19 @@ int listen_on_unix(char *path, int backlog) { int sd; int r; + if (path == nullptr) { + errno = EINVAL; + proxy_error("Cannot listen on a null Unix Socket path\n"); + return -1; + } + + const size_t path_len = strnlen(path, sizeof(serveraddr.sun_path)); + if (path_len >= sizeof(serveraddr.sun_path)) { + errno = ENAMETOOLONG; + proxy_error("Unix Socket path is too long: %s\n", path); + return -1; + } + // remove the socket r=unlink(path); if ( (r==-1) && (errno!=ENOENT) ) { @@ -104,13 +117,7 @@ int listen_on_unix(char *path, int backlog) { memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sun_family = AF_UNIX; - const int path_len = snprintf(serveraddr.sun_path, sizeof(serveraddr.sun_path), "%s", path); - if (path_len < 0 || (size_t)path_len >= sizeof(serveraddr.sun_path)) { - close(sd); - errno = (path_len < 0) ? EINVAL : ENAMETOOLONG; - proxy_error("Unix Socket path is too long: %s\n", path); - return -1; - } + memcpy(serveraddr.sun_path, path, path_len + 1); // call bind() to bind the socket on the specified file if ( bind(sd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_un)) != 0 ) { From fc6b6a3d592a27680e8d73c3ad2d5657bb35301d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:23:06 +0000 Subject: [PATCH 198/227] fix: report truncated MCP variable reads Route bounded variable reads through the existing string-based lookup and reject values that do not fit in the caller buffer. Clear the output on truncation so callers cannot mistake a partial bearer token or configuration value for a successful read. --- plugins/genai/src/MCP_Thread.cpp | 46 ++++++-------------------------- 1 file changed, 8 insertions(+), 38 deletions(-) diff --git a/plugins/genai/src/MCP_Thread.cpp b/plugins/genai/src/MCP_Thread.cpp index e3d927c133..881b4b896d 100644 --- a/plugins/genai/src/MCP_Thread.cpp +++ b/plugins/genai/src/MCP_Thread.cpp @@ -172,47 +172,17 @@ int MCP_Threads_Handler::get_variable(const char* name, char* val, size_t val_si if (!name || !val || val_size == 0) return -1; - pthread_rwlock_rdlock(&rwlock); - std::string out; - int rc = 0; - if (!strcmp(name, "enabled")) { - out = variables.mcp_enabled ? "true" : "false"; - } else if (!strcmp(name, "port")) { - out = std::to_string(variables.mcp_port); - } else if (!strcmp(name, "use_ssl")) { - out = variables.mcp_use_ssl ? "true" : "false"; - } else if (!strcmp(name, "config_endpoint_auth")) { - out = variables.mcp_config_endpoint_auth ? variables.mcp_config_endpoint_auth : ""; - } else if (!strcmp(name, "stats_endpoint_auth")) { - out = variables.mcp_stats_endpoint_auth ? variables.mcp_stats_endpoint_auth : ""; - } else if (!strcmp(name, "query_endpoint_auth")) { - out = variables.mcp_query_endpoint_auth ? variables.mcp_query_endpoint_auth : ""; - } else if (!strcmp(name, "admin_endpoint_auth")) { - out = variables.mcp_admin_endpoint_auth ? variables.mcp_admin_endpoint_auth : ""; - } else if (!strcmp(name, "cache_endpoint_auth")) { - out = variables.mcp_cache_endpoint_auth ? variables.mcp_cache_endpoint_auth : ""; - } else if (!strcmp(name, "ai_endpoint_auth")) { - out = variables.mcp_ai_endpoint_auth ? variables.mcp_ai_endpoint_auth : ""; - } else if (!strcmp(name, "rag_endpoint_auth")) { - out = variables.mcp_rag_endpoint_auth ? variables.mcp_rag_endpoint_auth : ""; - } else if (!strcmp(name, "timeout_ms")) { - out = std::to_string(variables.mcp_timeout_ms); - } else if (!strcmp(name, "stats_show_queries_max_rows")) { - out = std::to_string(variables.mcp_stats_show_queries_max_rows); - } else if (!strcmp(name, "stats_show_processlist_max_rows")) { - out = std::to_string(variables.mcp_stats_show_processlist_max_rows); - } else if (!strcmp(name, "stats_enable_debug_tools")) { - out = variables.mcp_stats_enable_debug_tools ? "true" : "false"; - } else { - rc = -1; - } - pthread_rwlock_unlock(&rwlock); + if (!get_variable_string(name, out)) + return -1; - if (rc == 0) { - snprintf(val, val_size, "%s", out.c_str()); + if (out.size() >= val_size) { + val[0] = '\0'; + return -1; } - return rc; + + memcpy(val, out.c_str(), out.size() + 1); + return 0; } int MCP_Threads_Handler::set_variable(const char* name, const char* value) { From c42edf1c1bc4847d90e9eaa984e7000572e28a0f Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:23:15 +0000 Subject: [PATCH 199/227] docs: describe bounded MCP variable read failures Document that get_variable rejects null or zero-sized buffers and reports values that would be truncated. Keep the legacy API contract aligned with its new bounded-copy behavior and direct callers toward get_variable_string(). --- plugins/genai/include/MCP_Thread.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/genai/include/MCP_Thread.h b/plugins/genai/include/MCP_Thread.h index e47ecb89ab..a8d443dfa9 100644 --- a/plugins/genai/include/MCP_Thread.h +++ b/plugins/genai/include/MCP_Thread.h @@ -271,7 +271,8 @@ class MCP_Threads_Handler * @param name The name of the variable (without 'mcp-' prefix) * @param val Output buffer to store the value * @param val_size Size of the output buffer in bytes - * @return 0 on success, -1 if variable not found + * @return 0 on success, -1 if the variable is unknown, the arguments are + * null/invalid, or the value does not fit in `val_size` bytes * * @deprecated The unbounded sprintf into `val` is a stack-buffer * overflow risk for variables that hold arbitrary-length values From a05d757840c9377ebca655306c7c694c36cf58e6 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:23:28 +0000 Subject: [PATCH 200/227] fix: avoid truncating MCP config values Use get_variable_string() for config reads and variable listings so bearer tokens and other arbitrary-length values are returned intact. Unknown variables still produce the existing error response, while known empty values remain valid results. --- plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp b/plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp index 706e818c6f..f8e91c8f16 100644 --- a/plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp +++ b/plugins/genai/src/tool_handlers/Config_Tool_Handler.cpp @@ -420,8 +420,8 @@ json Config_Tool_Handler::handle_get_config(const std::string& var_name) { return create_error_response("MCP handler not initialized"); } - char val[1024]; - if (mcp_handler->get_variable(var_name.c_str(), val, sizeof(val)) == 0) { + std::string val; + if (mcp_handler->get_variable_string(var_name.c_str(), val)) { json result; result["variable_name"] = var_name; result["value"] = val; @@ -547,8 +547,8 @@ json Config_Tool_Handler::handle_list_variables(const std::string& filter) { } } - char val[1024]; - if (mcp_handler->get_variable(var_name.c_str(), val, sizeof(val)) == 0) { + std::string val; + if (mcp_handler->get_variable_string(var_name.c_str(), val)) { json var; var["name"] = var_name; var["value"] = val; From f8e8e69438b111e095914728b520c8546dd575bf Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:23:51 +0000 Subject: [PATCH 201/227] test: cover bounded MCP variable reads Use get_variable_string() for exact-value assertions and keep explicit null-buffer and zero-capacity checks for the legacy API. Add coverage proving oversized endpoint credentials are rejected, the destination is cleared, and the test restores the handler state. --- .../tests/unit/genai_mcp_thread_unit-t.cpp | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/test/tap/tests/unit/genai_mcp_thread_unit-t.cpp b/test/tap/tests/unit/genai_mcp_thread_unit-t.cpp index 703abaf863..286101c0f5 100644 --- a/test/tap/tests/unit/genai_mcp_thread_unit-t.cpp +++ b/test/tap/tests/unit/genai_mcp_thread_unit-t.cpp @@ -35,10 +35,9 @@ /* Helper: get a variable value as std::string */ /* ------------------------------------------------------------------ */ static std::string get_var(MCP_Threads_Handler& h, const char* name) { - char buf[4096] = {0}; - int rc = h.get_variable(name, buf, sizeof(buf)); - if (rc != 0) return "__ERROR__"; - return std::string(buf); + std::string value; + if (!h.get_variable_string(name, value)) return "__ERROR__"; + return value; } /* ================================================================== */ @@ -363,8 +362,10 @@ static void test_null_safety(MCP_Threads_Handler& h) { ok(h.get_variable(nullptr, buf, sizeof(buf)) == -1, "get_variable(nullptr, buf) = -1"); - ok(h.get_variable("enabled", nullptr, 0) == -1, + ok(h.get_variable("enabled", nullptr, sizeof(buf)) == -1, "get_variable(enabled, nullptr) = -1"); + ok(h.get_variable("enabled", buf, 0) == -1, + "get_variable(enabled, buf, 0) = -1"); ok(h.get_variable(nullptr, nullptr, 0) == -1, "get_variable(nullptr, nullptr) = -1"); @@ -376,6 +377,23 @@ static void test_null_safety(MCP_Threads_Handler& h) { "set_variable(nullptr, nullptr) = -1"); } +/** + * @brief Bounded get_variable() rejects values that would be truncated. + */ +static void test_bounded_get_variable(MCP_Threads_Handler& h) { + const std::string long_value(4096, 'x'); + ok(h.set_variable("config_endpoint_auth", long_value.c_str()) == 0, + "set long config_endpoint_auth value"); + + char buf[16]; + memset(buf, 'x', sizeof(buf)); + ok(h.get_variable("config_endpoint_auth", buf, sizeof(buf)) == -1 && buf[0] == '\0', + "get_variable rejects and clears a truncated value"); + + ok(h.set_variable("config_endpoint_auth", "") == 0, + "reset config_endpoint_auth after bounded read test"); +} + /** * @brief get_variable_string() should overwrite stale output. */ @@ -450,7 +468,8 @@ static void test_wrlock_wrunlock(MCP_Threads_Handler& h) { * String variables: 7 vars * 6 tests each = 42 * has_variable: 13 * get_variables_list: 2 + 14 = 16 - * Null safety: 6 + * Null safety: 7 + * Bounded get_variable: 3 * get_variable_string: 2 * Get unknown: 2 * Set unknown: 2 @@ -458,9 +477,9 @@ static void test_wrlock_wrunlock(MCP_Threads_Handler& h) { * Load target auth null: 1 * wrlock/wrunlock: 1 * ------------------------------------------------- - * Total: 199 + * Total: 203 */ -static const int TOTAL_TESTS = 199; +static const int TOTAL_TESTS = 203; int main() { plan(TOTAL_TESTS); @@ -477,6 +496,7 @@ int main() { test_has_variable(handler); test_get_variables_list(handler); test_null_safety(handler); + test_bounded_get_variable(handler); test_get_variable_string_contract(handler); test_get_unknown_variable(handler); test_set_unknown_variable(handler); From b5648b6d69508d54ff7991b426a2016e7191cb39 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:24:05 +0000 Subject: [PATCH 202/227] fix: reject oversized bootstrap UUIDs Validate the UUID length after removing separator dashes before writing the normalized value into uuid_server. Mark oversized ST= bootstrap records invalid and disconnect instead of allowing the fixed buffer to overflow or silently truncate the GTID identity. --- lib/GTID_Server_Data.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/GTID_Server_Data.cpp b/lib/GTID_Server_Data.cpp index 56d1da757b..95b3138b1f 100644 --- a/lib/GTID_Server_Data.cpp +++ b/lib/GTID_Server_Data.cpp @@ -355,14 +355,21 @@ bool GTID_Server_Data::read_next_gtid() { } j++; if (j%2 == 1) { // we are reading the uuid - char *p = uuid_server; - for (unsigned int k=0; k= sizeof(uuid_server)) { + invalid_msg = true; + break; + } + uuid_server[uuid_len++] = *uuid_char; + } + if (invalid_msg) { + break; } - *p = '\0'; + uuid_server[uuid_len] = '\0'; } else { // we are reading the trxid or trxid range TrxId_Interval iv(trxid_t(0)); if (!TrxId_Interval::parse(subtoken, &iv)) { From 6218c0530a415e4d76370cec0fe3287fba618023 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:25:04 +0000 Subject: [PATCH 203/227] refactor: make admin interface replacement exception-safe Introduce scoped owners for tokenizer state and the replacement interface array. Partial token duplication is now released automatically on allocation failure, and the old interface array is freed only after the new list is complete and installed. --- include/Admin_ifaces.h | 69 ++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/include/Admin_ifaces.h b/include/Admin_ifaces.h index bfec89d329..09eb8c366b 100644 --- a/include/Admin_ifaces.h +++ b/include/Admin_ifaces.h @@ -44,6 +44,39 @@ class ifaces_desc { class admin_main_loop_listeners { private: + struct tokenizer_owner { + tokenizer_t value; + + explicit tokenizer_owner(char *list) { + tokenizer(&value, list, ";", TOKENIZER_NO_EMPTIES); + } + + ~tokenizer_owner() { + free_tokenizer(&value); + } + }; + + struct interface_array_owner { + char **value; + + explicit interface_array_owner(char **value_) : value(value_) {} + + ~interface_array_owner() { + if (value) { + for (int i = 0; i < MAX_IFACES; ++i) { + free(value[i]); + } + free(value); + } + } + + char **release() { + char **released = value; + value = nullptr; + return released; + } + }; + int version; #ifdef PA_PTHREAD_MUTEX pthread_rwlock_t rwlock; @@ -129,38 +162,28 @@ class admin_main_loop_listeners { bool update_ifaces(char *list, char ***_ifaces) { wrlock(); - int i = 0; char **old_ifaces = *_ifaces; - char **new_ifaces = (char **)calloc(MAX_IFACES, sizeof(char *)); - tokenizer_t tok; - tokenizer( &tok, list, ";", TOKENIZER_NO_EMPTIES ); - const char* token; - if (new_ifaces == NULL) { - free_tokenizer( &tok ); + interface_array_owner replacement((char **)calloc(MAX_IFACES, sizeof(char *))); + if (replacement.value == nullptr) { wrunlock(); return false; } - for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - new_ifaces[i] = strdup(token); - if (new_ifaces[i] == NULL) { - for (int j = 0; j < i; ++j) { - free(new_ifaces[j]); - } - free(new_ifaces); - free_tokenizer( &tok ); + + tokenizer_owner tokens(list); + int i = 0; + for (const char *token = tokenize(&tokens.value); + token && i < MAX_IFACES; + token = tokenize(&tokens.value)) { + replacement.value[i] = strdup(token); + if (replacement.value[i] == nullptr) { wrunlock(); return false; } i++; } - if (old_ifaces != NULL) { - for (int j = 0; j < MAX_IFACES; ++j) { - free(old_ifaces[j]); - } - free(old_ifaces); - } - *_ifaces = new_ifaces; - free_tokenizer( &tok ); + + interface_array_owner previous(old_ifaces); + *_ifaces = replacement.release(); version++; wrunlock(); return true; From 43318e393cd21d5e7ffde37a69dce472f32bef88 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:25:04 +0000 Subject: [PATCH 204/227] refactor: make ClickHouse interface replacement exception-safe Use scoped owners for tokenizer state and replacement listener arrays so partial duplication cannot leak or leave mixed state behind. Install the fully built list before releasing the previous interface array. --- lib/ClickHouse_Server.cpp | 69 ++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/lib/ClickHouse_Server.cpp b/lib/ClickHouse_Server.cpp index 226be1c682..c402b35c18 100644 --- a/lib/ClickHouse_Server.cpp +++ b/lib/ClickHouse_Server.cpp @@ -506,6 +506,39 @@ class ifaces_desc { class sqlite3server_main_loop_listeners { private: + struct tokenizer_owner { + tokenizer_t value; + + explicit tokenizer_owner(char *list) { + tokenizer(&value, list, ";", TOKENIZER_NO_EMPTIES); + } + + ~tokenizer_owner() { + free_tokenizer(&value); + } + }; + + struct interface_array_owner { + char **value; + + explicit interface_array_owner(char **value_) : value(value_) {} + + ~interface_array_owner() { + if (value) { + for (int i = 0; i < MAX_IFACES; ++i) { + free(value[i]); + } + free(value); + } + } + + char **release() { + char **released = value; + value = nullptr; + return released; + } + }; + int version; pthread_rwlock_t rwlock; @@ -566,38 +599,28 @@ class sqlite3server_main_loop_listeners { bool update_ifaces(char *list, char ***_ifaces) { wrlock(); - int i = 0; char **old_ifaces = *_ifaces; - char **new_ifaces = (char **)calloc(MAX_IFACES, sizeof(char *)); - tokenizer_t tok; - tokenizer( &tok, list, ";", TOKENIZER_NO_EMPTIES ); - const char* token; - if (new_ifaces == NULL) { - free_tokenizer( &tok ); + interface_array_owner replacement((char **)calloc(MAX_IFACES, sizeof(char *))); + if (replacement.value == nullptr) { wrunlock(); return false; } - for ( token = tokenize( &tok ) ; token && i < MAX_IFACES ; token = tokenize( &tok ) ) { - new_ifaces[i] = strdup(token); - if (new_ifaces[i] == NULL) { - for (int j = 0; j < i; ++j) { - free(new_ifaces[j]); - } - free(new_ifaces); - free_tokenizer( &tok ); + + tokenizer_owner tokens(list); + int i = 0; + for (const char *token = tokenize(&tokens.value); + token && i < MAX_IFACES; + token = tokenize(&tokens.value)) { + replacement.value[i] = strdup(token); + if (replacement.value[i] == nullptr) { wrunlock(); return false; } i++; } - if (old_ifaces != NULL) { - for (int j = 0; j < MAX_IFACES; ++j) { - free(old_ifaces[j]); - } - free(old_ifaces); - } - *_ifaces = new_ifaces; - free_tokenizer( &tok ); + + interface_array_owner previous(old_ifaces); + *_ifaces = replacement.release(); version++; wrunlock(); return true; From 53f3474bf16facd075655a05a27b0a1307584320 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:25:47 +0000 Subject: [PATCH 205/227] test: build table status queries without C formatting Replace the temporary malloc buffer and nonliteral snprintf call with std::string placeholder substitution. The test keeps all case variants intact while avoiding format-string handling and manual buffer sizing. --- test/tap/tests/admin_show_table_status-t.cpp | 21 ++++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/test/tap/tests/admin_show_table_status-t.cpp b/test/tap/tests/admin_show_table_status-t.cpp index f9c017f6ff..dde72e3014 100644 --- a/test/tap/tests/admin_show_table_status-t.cpp +++ b/test/tap/tests/admin_show_table_status-t.cpp @@ -12,7 +12,6 @@ #include "tap.h" #include "command_line.h" #include "utils.h" -#include "proxysql_utils.h" using std::string; @@ -75,17 +74,17 @@ int main() { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); return -1; } - const size_t query_len = strlen(queries[0]) + it->size() + 1; - mf_unique_ptr query { (char *)malloc(query_len) }; - if (!query) { - fprintf(stderr, "Unable to allocate query buffer\n"); - mysql_close(proxysql_admin); - return -1; - } for (std::vector::iterator it2 = queries.begin(); it2 != queries.end(); it2++) { - snprintf(query.get(), query_len, *it2, it->c_str()); - diag("Running query: %s", query.get()); - MYSQL_QUERY(proxysql_admin, query.get()); + std::string query(*it2); + const size_t placeholder = query.find("%s"); + if (placeholder == std::string::npos) { + fprintf(stderr, "Query template is missing its table placeholder\n"); + mysql_close(proxysql_admin); + return -1; + } + query.replace(placeholder, 2, *it); + diag("Running query: %s", query.c_str()); + MYSQL_QUERY(proxysql_admin, query.c_str()); MYSQL_RES* proxy_res = mysql_store_result(proxysql_admin); unsigned long rows = proxy_res->row_count; ok(rows == 1 , "SHOW TABLE STATUS %s generated %lu row(s)", it->c_str(), rows); From 631a9c6acb8142d79c0d1675198656e3625458df Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:25:47 +0000 Subject: [PATCH 206/227] test: build field-list queries without C formatting Construct each SHOW FIELDS query by replacing its fixed %s placeholder in a std::string. This removes the manual allocation and nonliteral snprintf path while preserving the existing table-name variants. --- test/tap/tests/admin_show_fields_from-t.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/test/tap/tests/admin_show_fields_from-t.cpp b/test/tap/tests/admin_show_fields_from-t.cpp index cd3725d0a3..00eb8d5133 100644 --- a/test/tap/tests/admin_show_fields_from-t.cpp +++ b/test/tap/tests/admin_show_fields_from-t.cpp @@ -12,7 +12,6 @@ #include "tap.h" #include "command_line.h" #include "utils.h" -#include "proxysql_utils.h" using std::string; @@ -75,17 +74,17 @@ int main() { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); return -1; } - const size_t query_len = strlen(queries[0]) + it->size() + 1; - mf_unique_ptr query { (char *)malloc(query_len) }; - if (!query) { - fprintf(stderr, "Unable to allocate query buffer\n"); - mysql_close(proxysql_admin); - return -1; - } for (std::vector::iterator it2 = queries.begin(); it2 != queries.end(); it2++) { - snprintf(query.get(), query_len, *it2, it->c_str()); - diag("Running query: %s", query.get()); - MYSQL_QUERY(proxysql_admin, query.get()); + std::string query(*it2); + const size_t placeholder = query.find("%s"); + if (placeholder == std::string::npos) { + fprintf(stderr, "Query template is missing its table placeholder\n"); + mysql_close(proxysql_admin); + return -1; + } + query.replace(placeholder, 2, *it); + diag("Running query: %s", query.c_str()); + MYSQL_QUERY(proxysql_admin, query.c_str()); MYSQL_RES* proxy_res = mysql_store_result(proxysql_admin); unsigned long rows = proxy_res->row_count; ok(rows > 0 , "Number of rows in %s = %lu", it->c_str(), rows); From 12f5e89c405fe3d7caa4b8008637955b435d8786 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:27:32 +0000 Subject: [PATCH 207/227] test: construct Aurora SQL with std::string Replace temporary malloc buffers and nonliteral snprintf calls with explicit string construction for version, user, and SHOW CREATE TABLE queries. Preserve allocation-failure handling while removing the new Sonar buffer and format-string findings. --- test/tap/tests/aurora.cpp | 60 ++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/test/tap/tests/aurora.cpp b/test/tap/tests/aurora.cpp index 9427db7e01..a4ccd49dc8 100644 --- a/test/tap/tests/aurora.cpp +++ b/test/tap/tests/aurora.cpp @@ -257,12 +257,16 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p if (query_no_space_length==SELECT_VERSION_COMMENT_LEN) { if (!strncasecmp(SELECT_VERSION_COMMENT, query_no_space, query_no_space_length)) { l_free(query_length,query); - char *a = (char *)"SELECT '(ProxySQL Automated Test Server) - %s'"; const char* proxy_addr = sess->client_myds->proxy_addr.addr; - size_t query_len = strlen(a) + (proxy_addr ? strlen(proxy_addr) : 0); - query = (char *)malloc(query_len); - snprintf(query, query_len, a, proxy_addr ? proxy_addr : ""); - query_length=strlen(query)+1; + const std::string query_text = + std::string("SELECT '(ProxySQL Automated Test Server) - ") + + (proxy_addr ? proxy_addr : "") + "'"; + query = l_strdup(query_text.c_str()); + if (!query) { + l_free(pkt->size-sizeof(mysql_hdr), query_no_space); + return; + } + query_length = query_text.size() + 1; goto __run_query; } } @@ -270,21 +274,16 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p if (query_no_space_length==SELECT_DB_USER_LEN) { if (!strncasecmp(SELECT_DB_USER, query_no_space, query_no_space_length)) { l_free(query_length,query); - char *query1=(char *)"SELECT \"admin\" AS 'DATABASE()', \"%s\" AS 'USER()'"; const char* username = sess->client_myds->myconn->userinfo->username; - size_t query2_len = strlen(query1) + (username ? strlen(username) : 0) + 1; - mf_unique_ptr query2 { (char *)malloc(query2_len) }; - if (!query2) { - l_free(pkt->size-sizeof(mysql_hdr), query_no_space); - return; - } - snprintf(query2.get(), query2_len, query1, username ? username : ""); - query=l_strdup(query2.get()); + const std::string query_text = + std::string("SELECT \"admin\" AS 'DATABASE()', \"") + + (username ? username : "") + "\" AS 'USER()'"; + query = l_strdup(query_text.c_str()); if (!query) { l_free(pkt->size-sizeof(mysql_hdr), query_no_space); return; } - query_length=strlen(query)+1; + query_length = query_text.size() + 1; goto __run_query; } } @@ -387,23 +386,32 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p size_t tbh_len = strlen(tbh); if (tbh_len>=3 && tbh[0]=='`' && tbh[tbh_len-1]=='`') { // tablename is quoted size_t db_len = tbh_len - 2; - mf_unique_ptr tbh_tmp { (char *)malloc(db_len + 1) }; - if (tbh_tmp) { - memcpy(tbh_tmp.get(), tbh + 1, db_len); - tbh_tmp.get()[db_len] = 0; - free(tbh); - tbh = tbh_tmp.release(); + const std::string unquoted_table(tbh + 1, db_len); + free(tbh); + tbh = l_strdup(unquoted_table.c_str()); + if (!tbh) { + free(dbh); + l_free(query_length, query); + return; } } - int l=strBl+strlen(tbh)*3+strlen(dbh)-8; - char *buff=(char *)l_alloc(l+1); - snprintf(buff,l+1,strB,tbh,tbh,dbh,tbh); - buff[l]=0; + const std::string table_query = + std::string("SELECT name AS 'table' , REPLACE(REPLACE(sql,' , ', X'2C0A20202020'),") + + "'CREATE TABLE " + tbh + " (','CREATE TABLE " + tbh + + " ('||X'0A20202020') AS 'Create Table' FROM " + dbh + + ".sqlite_master WHERE type='table' AND name='" + tbh + "'"; + char *buff = l_strdup(table_query.c_str()); + if (!buff) { + free(tbh); + free(dbh); + l_free(query_length, query); + return; + } free(tbh); free(dbh); l_free(query_length,query); query=buff; - query_length=l+1; + query_length=table_query.size()+1; goto __run_query; } From 4a6744d72585a270fce83bd6294198bf049f9b10 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:27:51 +0000 Subject: [PATCH 208/227] refactor: use constexpr PostgreSQL hash delimiters Replace function-local delimiter macros with constexpr character arrays and derive their lengths from sizeof. Keep the hash input bytes unchanged while removing macro pollution and the associated Sonar maintainability findings. --- lib/PgSQL_Connection.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/PgSQL_Connection.cpp b/lib/PgSQL_Connection.cpp index c00eda3923..a220207a5a 100644 --- a/lib/PgSQL_Connection.cpp +++ b/lib/PgSQL_Connection.cpp @@ -52,10 +52,10 @@ uint64_t PgSQL_Connection_userinfo::compute_hash() { size_t dbname_len = dbname ? strlen(dbname) : 0; size_t l = username_len + password_len + dbname_len; // two random seperator -#define COMPUTE_HASH_DELIMITER_1 "-ujhtgf76y576574fhYTRDF345wdt-" -#define COMPUTE_HASH_DELIMITER_2 "-8k7jrhtrgJHRgrefgreyhtRFewg6-" - size_t delimiter1_len = strlen(COMPUTE_HASH_DELIMITER_1); - size_t delimiter2_len = strlen(COMPUTE_HASH_DELIMITER_2); + constexpr char delimiter1[] = "-ujhtgf76y576574fhYTRDF345wdt-"; + constexpr char delimiter2[] = "-8k7jrhtrgJHRgrefgreyhtRFewg6-"; + size_t delimiter1_len = sizeof(delimiter1) - 1; + size_t delimiter2_len = sizeof(delimiter2) - 1; l += delimiter1_len + delimiter2_len; std::string hash_input; @@ -63,14 +63,14 @@ uint64_t PgSQL_Connection_userinfo::compute_hash() { if (username) { hash_input.append(username, username_len); } - hash_input.append(COMPUTE_HASH_DELIMITER_1); + hash_input.append(delimiter1); if (password) { hash_input.append(password, password_len); } if (dbname) { hash_input.append(dbname, dbname_len); } - hash_input.append(COMPUTE_HASH_DELIMITER_2); + hash_input.append(delimiter2); return SpookyHash::Hash64(hash_input.data(), hash_input.size(), 0); } From 9f6e6376ca61da420f9471a0a49d729af67d70c4 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:28:41 +0000 Subject: [PATCH 209/227] refactor: isolate binary COPY field encoding Move per-column binary encoding into a dedicated helper so the COPY test no longer nests type handling inside the row and field loops. Preserve the explicit oversized-numeric failure while reducing the Sonar cognitive-complexity and nesting findings. --- test/tap/tests/pgsql-copy_from_test-t.cpp | 88 ++++++++++++----------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/test/tap/tests/pgsql-copy_from_test-t.cpp b/test/tap/tests/pgsql-copy_from_test-t.cpp index 3cc7691f10..b469fdb4d2 100644 --- a/test/tap/tests/pgsql-copy_from_test-t.cpp +++ b/test/tap/tests/pgsql-copy_from_test-t.cpp @@ -346,6 +346,48 @@ const column_type_t columns_type[] = { DATE }; +bool encodeBinaryField(uint8_t* row, int& offset, column_type_t type, const std::string& data) { + switch (type) { + case INT: { + write_int32(row + offset, sizeof(int32_t)); + offset += sizeof(int32_t); + const int32_t value = atoi(data.c_str()); + memcpy(row + offset, &value, sizeof(value)); + offset += sizeof(value); + return true; + } + case DATE: { + write_int32(row + offset, sizeof(int32_t)); + offset += sizeof(int32_t); + const uint32_t date = encodeDateBinary(data.c_str()); + memcpy(row + offset, &date, sizeof(date)); + offset += sizeof(date); + return true; + } + case TEXT: + case BOOLEAN: + write_int32(row + offset, data.size()); + offset += sizeof(int32_t); + memcpy(row + offset, data.c_str(), data.size()); + offset += data.size(); + return true; + case NUMERIC: { + uint8_t* length_pos = row + offset; + offset += sizeof(int32_t); + const int digit_count = encodeNumericBinary(row + offset, data.c_str()); + if (digit_count < 0) { + fprintf(stderr, "Numeric value is too long for the binary COPY test buffer: %s\n", data.c_str()); + return false; + } + const int32_t payload_length = digit_count > 0 ? 12 : 8; + write_int32(length_pos, payload_length); + offset += payload_length; + return true; + } + } + return false; +} + /** * @brief Tests the COPY IN functionality using STDIN in TEXT format. * @@ -445,48 +487,10 @@ void testSTDIN_TEXT_BINARY(PGconn* admin_conn, PGconn* conn, std::fstream& f_pro offset += sizeof(num_fields); for (unsigned int j = 0; j < row_data.size(); j++) { - const std::string& data = row_data[j]; - if (columns_type[j] == INT) { - write_int32(row + offset, sizeof(int32_t)); - offset += sizeof(int32_t); - - int32_t value = atoi(data.c_str()); - // write actual data - memcpy(row + offset, &value, sizeof(value)); - offset += sizeof(value); - } else if (columns_type[j] == DATE) { - write_int32(row + offset, sizeof(int32_t)); - offset += sizeof(int32_t); - - uint32_t date = encodeDateBinary(data.c_str()); - // write actual data - memcpy(row + offset, &date, sizeof(date)); - offset += sizeof(date); - } else if (columns_type[j] == TEXT || columns_type[j] == BOOLEAN) { - // write field length - write_int32(row + offset, data.size()); - offset += sizeof(int32_t); - - // write actual data - memcpy(row + offset, data.c_str(), data.size()); - offset += data.size(); - } else if (columns_type[j] == NUMERIC) { - uint8_t* prev_pos = (row + offset); - offset += sizeof(int32_t); - int digit_count = encodeNumericBinary(row + offset, data.c_str()); - if (digit_count < 0) { - fprintf(stderr, "Numeric value is too long for the binary COPY test buffer: %s\n", data.c_str()); - success = false; - break; - } - if (digit_count > 0) { - write_int32(prev_pos, 12); - offset += 12; - } else { - write_int32(prev_pos, 8); - offset += 8; - } - } + if (!encodeBinaryField(row, offset, columns_type[j], row_data[j])) { + success = false; + break; + } } if (!success) { break; From 005781a799d7b416a3548830708d763537481db5 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:45:06 +0000 Subject: [PATCH 210/227] fix: avoid freeing tokenizer stack storage Release tokenizer memory only when the input was duplicated on the heap. Short inputs point into tokenizer_t::buffer, so checking the pointer identity prevents free() from being called on that stack-backed storage while preserving cleanup for long inputs. --- lib/c_tokenizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/c_tokenizer.cpp b/lib/c_tokenizer.cpp index c85dca6c25..fe53de7861 100644 --- a/lib/c_tokenizer.cpp +++ b/lib/c_tokenizer.cpp @@ -46,7 +46,7 @@ void tokenizer(tokenizer_t *result, const char* s, const char* delimiters, int e const char* free_tokenizer( tokenizer_t* tokenizer ) { - if (tokenizer->s_length > (PROXYSQL_TOKENIZER_BUFFSIZE-1)) { + if (tokenizer->s && tokenizer->s != tokenizer->buffer) { free(tokenizer->s); } tokenizer->s = NULL; From b2ae7ec223d7a3e303cc6efe33730de557b0eff1 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:45:49 +0000 Subject: [PATCH 211/227] fix: bound config query formatting Use the existing computed query capacities for the config loader's dynamic SQL formatting paths. Bounded snprintf calls prevent oversized configuration values from overrunning query buffers while preserving the generated statements and their existing cleanup flow. --- lib/ProxySQL_Config.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index 126e35bb4e..fb01603040 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -1448,7 +1448,7 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + status_len + address_len + safe_comment_len + 128; char *query=(char *)malloc(query_len); - sprintf(query,q, address.c_str(), port, gtid_port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); + snprintf(query, query_len, q, address.c_str(), port, gtid_port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); @@ -1497,7 +1497,7 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t safe_check_type_len = safe_strlen(safe_check_type); const size_t query_len = query_base_len + safe_comment_len + safe_check_type_len + 32; char *query=(char *)malloc(query_len); - sprintf(query,q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); + snprintf(query, query_len, q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); @@ -1616,7 +1616,7 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + safe_comment_len + 128; // 128 vs sizeof(int)*8 char *query=(char *)malloc(query_len); - sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); + snprintf(query, query_len, q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); @@ -1667,7 +1667,7 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + safe_comment_len + 128; // 128 vs sizeof(int)*8 char *query=(char *)malloc(query_len); - sprintf(query,q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); + snprintf(query, query_len, q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); @@ -1728,7 +1728,7 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t safe_domain_len = safe_strlen(safe_domain); const size_t query_len = query_base_len + safe_comment_len + safe_domain_len + 256; // 128 vs sizeof(int)*8 char *query=(char *)malloc(query_len); - sprintf(query,q, writer_hostgroup, reader_hostgroup, active, aurora_port, safe_domain, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, autopurge_missing_checks, safe_comment); + snprintf(query, query_len, q, writer_hostgroup, reader_hostgroup, active, aurora_port, safe_domain, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, autopurge_missing_checks, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); @@ -1788,7 +1788,7 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + safe_comment_len + 256; // 128 vs sizeof(int)*8 char *query=(char *)malloc(query_len); - sprintf(query,q, writer_hostgroup, reader_hostgroup, green_writer_str.c_str(), green_reader_str.c_str(), active, writer_is_also_reader, check_interval_ms, check_timeout_ms, safe_comment); + snprintf(query, query_len, q, writer_hostgroup, reader_hostgroup, green_writer_str.c_str(), green_reader_str.c_str(), active, writer_is_also_reader, check_interval_ms, check_timeout_ms, safe_comment); admindb->execute(query); if (o!=o1) free(o); free(o1); @@ -1966,7 +1966,7 @@ int ProxySQL_Config::Read_ProxySQL_Servers_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + address_len + safe_comment_len + 128; char *query=(char *)malloc(query_len); - sprintf(query, q, address.c_str(), port, weight, safe_comment); + snprintf(query, query_len, q, address.c_str(), port, weight, safe_comment); proxy_info("Cluster: Adding ProxySQL Servers %s:%d from config file\n", address.c_str(), port); //fprintf(stderr, "%s\n", query); admindb->execute(query); @@ -2238,7 +2238,7 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + status_len + address_len + safe_comment_len + 128; char* query = (char*)malloc(query_len); - sprintf(query, q, address.c_str(), port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); + snprintf(query, query_len, q, address.c_str(), port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o != o1) free(o); @@ -2287,7 +2287,7 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { const size_t safe_check_type_len = safe_strlen(safe_check_type); const size_t query_len = query_base_len + safe_comment_len + safe_check_type_len + 32; char* query = (char*)malloc(query_len); - sprintf(query, q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); + snprintf(query, query_len, q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o != o1) free(o); From 31f3dc8e26600afd968f4a8a5ed3d4bb5017a0c2 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:47:16 +0000 Subject: [PATCH 212/227] fix: use bounded randomness in Aurora test output Replace the rand-based replication-lag value used in the Aurora test response with a uniform distribution backed by std::random_device. The generated delay remains in the existing 10-to-39 second range while removing the insecure legacy PRNG flagged by SonarCloud. --- test/tap/tests/aurora.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/tap/tests/aurora.cpp b/test/tap/tests/aurora.cpp index a4ccd49dc8..778530ac20 100644 --- a/test/tap/tests/aurora.cpp +++ b/test/tap/tests/aurora.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,12 @@ extern SQLite3_Server *GloSQLite3Server; +static int random_replication_lag_seconds() { + static thread_local std::random_device random_source; + static thread_local std::uniform_int_distribution distribution(10, 39); + return distribution(random_source); +} + void SQLite3_Server::init_aurora_ifaces_string(std::string& s) { if(!s.empty()) s += ";"; @@ -466,7 +473,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p free(query); char *a = (char *)"SELECT %d as Seconds_Behind_Master"; query = (char *)malloc(strlen(a)+4); - snprintf(query, strlen(a)+4, a, rand()%30+10); + snprintf(query, strlen(a)+4, a, random_replication_lag_seconds()); } } SQLite3_Session *sqlite_sess = (SQLite3_Session *)sess->thread->gen_args; From cf551a2ffff9cc88a0e3a4d9b26959285c5029fb Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:47:33 +0000 Subject: [PATCH 213/227] fix: replace test replication rand calls Generate the synthetic replication lag with the C++ random facilities instead of rand(). Keep the existing 10-to-39 second range and test-only behavior while removing the SonarCloud vulnerability on the runtime test path. --- src/SQLite3_Server.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 71cf437314..dad9093f05 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #ifndef SPOOKYV2 #include "SpookyV2.h" @@ -38,6 +39,14 @@ using std::string; +#if defined(TEST_AURORA) || defined(TEST_GALERA) || defined(TEST_GROUPREP) || defined(TEST_READONLY) || defined(TEST_REPLICATIONLAG) || defined(TEST_RDS_BGD) +static int random_replication_lag_seconds() { + static thread_local std::random_device random_source; + static thread_local std::uniform_int_distribution distribution(10, 39); + return distribution(random_source); +} +#endif + #define SELECT_VERSION_COMMENT "select @@version_comment limit 1" #define SELECT_VERSION_COMMENT_LEN 32 #define SELECT_DB_USER "select DATABASE(), USER() limit 1" @@ -1064,7 +1073,7 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p free(query); char *a = (char *)"SELECT %d as Seconds_Behind_Master"; query = (char *)malloc(strlen(a)+4); - snprintf(query, strlen(a)+4, a, rand()%30+10); + snprintf(query, strlen(a)+4, a, random_replication_lag_seconds()); } } #endif // TEST_AURORA || TEST_GALERA || TEST_GROUPREP || TEST_READONLY || TEST_REPLICATIONLAG || TEST_RDS_BGD From d6794acc0ebde46e03942ab9e71e84474c8b6294 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:48:51 +0000 Subject: [PATCH 214/227] fix: bound overlong socket path logs Limit the diagnostic for rejected Unix socket paths to the bytes already validated by strnlen. This prevents the error path from reading past an unterminated caller buffer while retaining the ENAMETOOLONG rejection. --- lib/network.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/network.cpp b/lib/network.cpp index f1c9928381..d74d3c36ab 100644 --- a/lib/network.cpp +++ b/lib/network.cpp @@ -97,7 +97,8 @@ int listen_on_unix(char *path, int backlog) { const size_t path_len = strnlen(path, sizeof(serveraddr.sun_path)); if (path_len >= sizeof(serveraddr.sun_path)) { errno = ENAMETOOLONG; - proxy_error("Unix Socket path is too long: %s\n", path); + proxy_error("Unix Socket path is too long: %.*s\n", + static_cast(path_len), path); return -1; } From 5b8adc5dc02a63f419f8e29726c0994b8385fe30 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:53:12 +0000 Subject: [PATCH 215/227] fix: encode PostgreSQL binary COPY fields Write integer payloads in network byte order and encode booleans as one-byte PostgreSQL binary values. Accept the existing t/f test data as well as true/false, reject invalid boolean text, and keep field length prefixes and offsets aligned with the encoded payloads. --- test/tap/tests/pgsql-copy_from_test-t.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/pgsql-copy_from_test-t.cpp b/test/tap/tests/pgsql-copy_from_test-t.cpp index b469fdb4d2..70381ac0c5 100644 --- a/test/tap/tests/pgsql-copy_from_test-t.cpp +++ b/test/tap/tests/pgsql-copy_from_test-t.cpp @@ -352,7 +352,7 @@ bool encodeBinaryField(uint8_t* row, int& offset, column_type_t type, const std: write_int32(row + offset, sizeof(int32_t)); offset += sizeof(int32_t); const int32_t value = atoi(data.c_str()); - memcpy(row + offset, &value, sizeof(value)); + write_int32(row + offset, value); offset += sizeof(value); return true; } @@ -365,12 +365,26 @@ bool encodeBinaryField(uint8_t* row, int& offset, column_type_t type, const std: return true; } case TEXT: - case BOOLEAN: write_int32(row + offset, data.size()); offset += sizeof(int32_t); memcpy(row + offset, data.c_str(), data.size()); offset += data.size(); return true; + case BOOLEAN: { + bool value; + if (data == "true" || data == "t") { + value = true; + } else if (data == "false" || data == "f") { + value = false; + } else { + fprintf(stderr, "Invalid boolean value for binary COPY: %s\n", data.c_str()); + return false; + } + write_int32(row + offset, 1); + offset += sizeof(int32_t); + row[offset++] = value ? 1 : 0; + return true; + } case NUMERIC: { uint8_t* length_pos = row + offset; offset += sizeof(int32_t); From 77c0af3ca60de542aecf6959e321ba3438b924ba Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 18:55:54 +0000 Subject: [PATCH 216/227] fix: bound PostgreSQL user query formatting Use the allocated query capacity when formatting PostgreSQL user rows so escaped comments and configuration values cannot make sprintf write past the destination buffer. Preserve the existing SQL generation and cleanup behavior. --- lib/ProxySQL_Config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index fb01603040..9266e421bc 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -2537,7 +2537,7 @@ int ProxySQL_Config::Read_PgSQL_Users_from_configfile(std::string& error) { const size_t attributes_len = attributes.size(); const size_t query_len = query_base_len + username_len + password_len + safe_comment_len + attributes_len + 128; char* query = (char*)malloc(query_len); - sprintf(query, q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); + snprintf(query, query_len, q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); From 1c50987a7113ecd35baf08afb3242aa61a9b68bf Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 21:59:59 +0000 Subject: [PATCH 217/227] fix: keep Sonar cleanup allocations ownership-safe Use consistently initialized and released buffers for listener interface arrays and generated paths. The listener owners now reject accidental copies, and temporary allocations introduced while addressing the security hotspots use the same ownership conventions on success and failure paths. --- include/Admin_ifaces.h | 20 ++++++-- lib/ClickHouse_Server.cpp | 20 ++++++-- src/SQLite3_Server.cpp | 86 ++++++++++++++++----------------- src/main.cpp | 32 ++++++------ src/proxy_tls.cpp | 23 +++++---- test/tap/tap/SQLite3_Server.cpp | 13 +++-- 6 files changed, 110 insertions(+), 84 deletions(-) diff --git a/include/Admin_ifaces.h b/include/Admin_ifaces.h index 09eb8c366b..6d3acc7b5b 100644 --- a/include/Admin_ifaces.h +++ b/include/Admin_ifaces.h @@ -54,6 +54,11 @@ class admin_main_loop_listeners { ~tokenizer_owner() { free_tokenizer(&value); } + + tokenizer_owner(const tokenizer_owner&) = delete; + tokenizer_owner& operator=(const tokenizer_owner&) = delete; + tokenizer_owner(tokenizer_owner&&) = delete; + tokenizer_owner& operator=(tokenizer_owner&&) = delete; }; struct interface_array_owner { @@ -64,12 +69,17 @@ class admin_main_loop_listeners { ~interface_array_owner() { if (value) { for (int i = 0; i < MAX_IFACES; ++i) { - free(value[i]); + l_free(0, value[i]); } - free(value); + l_free(0, value); } } + interface_array_owner(const interface_array_owner&) = delete; + interface_array_owner& operator=(const interface_array_owner&) = delete; + interface_array_owner(interface_array_owner&&) = delete; + interface_array_owner& operator=(interface_array_owner&&) = delete; + char **release() { char **released = value; value = nullptr; @@ -163,7 +173,11 @@ class admin_main_loop_listeners { bool update_ifaces(char *list, char ***_ifaces) { wrlock(); char **old_ifaces = *_ifaces; - interface_array_owner replacement((char **)calloc(MAX_IFACES, sizeof(char *))); + char **new_ifaces = (char **)l_alloc(MAX_IFACES * sizeof(char *)); + if (new_ifaces != nullptr) { + memset(new_ifaces, 0, MAX_IFACES * sizeof(char *)); + } + interface_array_owner replacement(new_ifaces); if (replacement.value == nullptr) { wrunlock(); return false; diff --git a/lib/ClickHouse_Server.cpp b/lib/ClickHouse_Server.cpp index c402b35c18..07fa164723 100644 --- a/lib/ClickHouse_Server.cpp +++ b/lib/ClickHouse_Server.cpp @@ -516,6 +516,11 @@ class sqlite3server_main_loop_listeners { ~tokenizer_owner() { free_tokenizer(&value); } + + tokenizer_owner(const tokenizer_owner&) = delete; + tokenizer_owner& operator=(const tokenizer_owner&) = delete; + tokenizer_owner(tokenizer_owner&&) = delete; + tokenizer_owner& operator=(tokenizer_owner&&) = delete; }; struct interface_array_owner { @@ -526,12 +531,17 @@ class sqlite3server_main_loop_listeners { ~interface_array_owner() { if (value) { for (int i = 0; i < MAX_IFACES; ++i) { - free(value[i]); + l_free(0, value[i]); } - free(value); + l_free(0, value); } } + interface_array_owner(const interface_array_owner&) = delete; + interface_array_owner& operator=(const interface_array_owner&) = delete; + interface_array_owner(interface_array_owner&&) = delete; + interface_array_owner& operator=(interface_array_owner&&) = delete; + char **release() { char **released = value; value = nullptr; @@ -600,7 +610,11 @@ class sqlite3server_main_loop_listeners { bool update_ifaces(char *list, char ***_ifaces) { wrlock(); char **old_ifaces = *_ifaces; - interface_array_owner replacement((char **)calloc(MAX_IFACES, sizeof(char *))); + char **new_ifaces = (char **)l_alloc(MAX_IFACES * sizeof(char *)); + if (new_ifaces != nullptr) { + memset(new_ifaces, 0, MAX_IFACES * sizeof(char *)); + } + interface_array_owner replacement(new_ifaces); if (replacement.value == nullptr) { wrunlock(); return false; diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index dad9093f05..7d8c25d83f 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -235,7 +235,10 @@ class sqlite3server_main_loop_listeners { wrlock(); int i = 0; char **old_ifaces = *_ifaces; - char **new_ifaces = (char **)calloc(MAX_IFACES, sizeof(char *)); + char **new_ifaces = (char **)l_alloc(MAX_IFACES * sizeof(char *)); + if (new_ifaces != NULL) { + memset(new_ifaces, 0, MAX_IFACES * sizeof(char *)); + } tokenizer_t tok; tokenizer( &tok, list, ";", TOKENIZER_NO_EMPTIES ); const char* token; @@ -248,9 +251,9 @@ class sqlite3server_main_loop_listeners { char *token_copy = strdup(token); if (token_copy == NULL) { for (int j = 0; j < i; ++j) { - free(new_ifaces[j]); + l_free(0, new_ifaces[j]); } - free(new_ifaces); + l_free(0, new_ifaces); free_tokenizer( &tok ); wrunlock(); return false; @@ -260,9 +263,9 @@ class sqlite3server_main_loop_listeners { } if (old_ifaces != NULL) { for (int j = 0; j < MAX_IFACES; ++j) { - free(old_ifaces[j]); + l_free(0, old_ifaces[j]); } - free(old_ifaces); + l_free(0, old_ifaces); } *_ifaces = new_ifaces; free_tokenizer( &tok ); @@ -612,18 +615,18 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p if (!strncasecmp(SELECT_VERSION_COMMENT, query_no_space, query_no_space_length)) { l_free(query_length,query); #if defined(TEST_AURORA) || defined(TEST_GALERA) || defined(TEST_GROUPREP) || defined(TEST_READONLY) || defined(TEST_REPLICATIONLAG) || defined(TEST_RDS_BGD) - const char* a = "SELECT '(ProxySQL Automated Test Server) - %s'"; const char* proxy_addr = sess->client_myds->proxy_addr.addr; - const size_t a_len = strlen(a); - const size_t proxy_addr_len = proxy_addr ? strlen(proxy_addr) : 0; - const size_t query_len = a_len + proxy_addr_len + 1; - query = (char *)malloc(query_len); + const std::string formatted_query = cstr_format( + "SELECT '(ProxySQL Automated Test Server) - %s'", + proxy_addr ? proxy_addr : "" + ).str; + query = l_strdup(formatted_query.c_str()); if (query == NULL) { GloSQLite3Server->send_MySQL_ERR(&sess->client_myds->myprot, 1105, "Out of memory"); run_query = false; goto __run_query; } - snprintf(query, query_len, a, proxy_addr ? proxy_addr : ""); + query_length = formatted_query.size() + 1; #else query=l_strdup("SELECT '(ProxySQL SQLite3 Server)'"); #endif // TEST_AURORA || TEST_GALERA || TEST_GROUPREP || TEST_READONLY || TEST_REPLICATIONLAG || TEST_RDS_BGD @@ -636,25 +639,18 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p if (!strncasecmp(SELECT_DB_USER, query_no_space, query_no_space_length)) { l_free(query_length,query); query = NULL; - char *query1=(char *)"SELECT \"admin\" AS 'DATABASE()', \"%s\" AS 'USER()'"; const char* username = sess->client_myds->myconn->userinfo->username; - size_t query2_length = strlen(query1) + (username ? strlen(username) : 0) + 1; - char *query2=(char *)malloc(query2_length); - if (query2 == NULL) { - GloSQLite3Server->send_MySQL_ERR(&sess->client_myds->myprot, 1105, "Out of memory"); - run_query = false; - goto __run_query; - } - snprintf(query2, query2_length, query1, username ? username : ""); - query=l_strdup(query2); + const std::string formatted_query = cstr_format( + "SELECT \"admin\" AS 'DATABASE()', \"%s\" AS 'USER()'", + username ? username : "" + ).str; + query = l_strdup(formatted_query.c_str()); if (query == NULL) { - free(query2); GloSQLite3Server->send_MySQL_ERR(&sess->client_myds->myprot, 1105, "Out of memory"); run_query = false; goto __run_query; } - query_length=strlen(query)+1; - free(query2); + query_length = formatted_query.size() + 1; goto __run_query; } } @@ -757,15 +753,15 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p char *tbh=NULL; c_split_2(query_no_space+strAl,".",&dbh,&tbh); - if (strlen(tbh)==0) { + if (std::string_view(tbh).empty()) { free(tbh); tbh=dbh; dbh=strdup("main"); } - size_t tbh_len = strlen(tbh); + size_t tbh_len = std::string_view(tbh).size(); if (tbh_len>=3 && tbh[0]=='`' && tbh[tbh_len-1]=='`') { // tablename is quoted const size_t quoted_len = tbh_len - 2; - char *tbh_tmp=(char *)malloc(quoted_len + 1); + char *tbh_tmp=(char *)l_alloc(quoted_len + 1); memcpy(tbh_tmp, tbh + 1, quoted_len); tbh_tmp[quoted_len] = 0; free(tbh); @@ -1023,8 +1019,8 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p } #endif // TEST_GROUPREP #if defined(TEST_READONLY) || defined(TEST_RDS_BGD) - if (strncasecmp("SELECT @@global.read_only read_only ",query_no_space, k_select_read_only_len)==0) { - if (strlen(query_no_space) > k_select_read_only_len+5) { + if (strncasecmp("SELECT @@global.read_only read_only ",query_no_space, k_select_read_only_len)==0 + && query_no_space_length > k_select_read_only_len+5) { pthread_mutex_lock(&GloSQLite3Server->test_readonly_mutex); // the current test doesn't try to simulate failures, therefore it will return immediately if (GloSQLite3Server->readonly_map_size() == 0) { @@ -1032,24 +1028,22 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p GloSQLite3Server->load_readonly_table(sess); } int rc = GloSQLite3Server->readonly_test_value(query_no_space+k_select_read_only_len); - free(query); - char *a = (char *)"SELECT %d as read_only"; - query = (char *)malloc(strlen(a)+2); - snprintf(query, strlen(a)+2, a, rc); + l_free(query_length, query); + const std::string formatted_query = cstr_format("SELECT %d as read_only", rc).str; + query = l_strdup(formatted_query.c_str()); + query_length = formatted_query.size() + 1; pthread_mutex_unlock(&GloSQLite3Server->test_readonly_mutex); } } #endif // TEST_READONLY || TEST_RDS_BGD #ifdef TEST_REPLICATIONLAG const bool replica_status = strncasecmp("SELECT REPLICA STATUS ", query_no_space, k_select_replica_status_len) == 0; - if ( - strncasecmp("SELECT SLAVE STATUS ", query_no_space, k_select_slave_status_len) == 0 - || replica_status - ) { + if ((strncasecmp("SELECT SLAVE STATUS ", query_no_space, k_select_slave_status_len) == 0 + || replica_status) + && query_no_space_length > k_select_slave_status_len + 5) { uint64_t addr_offset { replica_status ? k_select_replica_status_len : k_select_slave_status_len }; - if (strlen(query_no_space) > k_select_slave_status_len + 5) { pthread_mutex_lock(&GloSQLite3Server->test_replicationlag_mutex); // the current test doesn't try to simulate failures, therefore it will return immediately if (GloSQLite3Server->replicationlag_map_size() == 0) { @@ -1057,23 +1051,25 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p GloSQLite3Server->load_replicationlag_table(sess); } const int* rc = GloSQLite3Server->replicationlag_test_value(query_no_space + addr_offset); - free(query); + l_free(query_length, query); string SELECT { "SELECT " + (rc ? std::to_string(*rc) : string { "null" }) + " AS " }; SELECT += replica_status ? "Seconds_Behind_Source" : "Seconds_Behind_Master"; - query = static_cast(malloc(SELECT.size() + 1)); - snprintf(query, SELECT.size() + 1, "%s", SELECT.c_str()); + query = l_strdup(SELECT.c_str()); + query_length = SELECT.size() + 1; pthread_mutex_unlock(&GloSQLite3Server->test_replicationlag_mutex); } } #endif // TEST_REPLICATIONLAG if (strstr(query_no_space,(char *)"Seconds_Behind_Master")) { - free(query); - char *a = (char *)"SELECT %d as Seconds_Behind_Master"; - query = (char *)malloc(strlen(a)+4); - snprintf(query, strlen(a)+4, a, random_replication_lag_seconds()); + l_free(query_length, query); + const std::string formatted_query = cstr_format( + "SELECT %d as Seconds_Behind_Master", random_replication_lag_seconds() + ).str; + query = l_strdup(formatted_query.c_str()); + query_length = formatted_query.size() + 1; } } #endif // TEST_AURORA || TEST_GALERA || TEST_GROUPREP || TEST_READONLY || TEST_REPLICATIONLAG || TEST_RDS_BGD diff --git a/src/main.cpp b/src/main.cpp index 88f3916234..d126ce662a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -70,6 +70,13 @@ using json = nlohmann::json; #include "proxy_protocol_info.h" #endif // DEBUG +static char *make_path(const char *directory, const char *filename) { + std::string path(directory); + path += '/'; + path += filename; + return strdup(path.c_str()); +} + /* extern "C" MySQL_LDAP_Authentication * create_MySQL_LDAP_Authentication_func() { @@ -889,30 +896,24 @@ void ProxySQL_Main_process_global_variables(int argc, const char **argv) { } free(t); - GloVars.admindb=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql.db")+2); - snprintf(GloVars.admindb, strlen(GloVars.datadir)+strlen((char *)"proxysql.db")+2, "%s/%s", GloVars.datadir, (char *)"proxysql.db"); + GloVars.admindb = make_path(GloVars.datadir, "proxysql.db"); - GloVars.sqlite3serverdb=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"sqlite3server.db")+2); - snprintf(GloVars.sqlite3serverdb, strlen(GloVars.datadir)+strlen((char *)"sqlite3server.db")+2, "%s/%s", GloVars.datadir, (char *)"sqlite3server.db"); + GloVars.sqlite3serverdb = make_path(GloVars.datadir, "sqlite3server.db"); - GloVars.statsdb_disk=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql_stats.db")+2); - snprintf(GloVars.statsdb_disk, strlen(GloVars.datadir)+strlen((char *)"proxysql_stats.db")+2, "%s/%s", GloVars.datadir, (char *)"proxysql_stats.db"); + GloVars.statsdb_disk = make_path(GloVars.datadir, "proxysql_stats.db"); if (GloVars.errorlog == NULL) { - GloVars.errorlog=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql.log")+2); - snprintf(GloVars.errorlog, strlen(GloVars.datadir)+strlen((char *)"proxysql.log")+2, "%s/%s", GloVars.datadir, (char *)"proxysql.log"); + GloVars.errorlog = make_path(GloVars.datadir, "proxysql.log"); } if (GloVars.pid == NULL) { - GloVars.pid=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql.pid")+2); - snprintf(GloVars.pid, strlen(GloVars.datadir)+strlen((char *)"proxysql.pid")+2, "%s/%s", GloVars.datadir, (char *)"proxysql.pid"); + GloVars.pid = make_path(GloVars.datadir, "proxysql.pid"); } if (GloVars.__cmd_proxysql_initial==true) { std::cerr << "Renaming database file " << GloVars.admindb << endl; - char *newpath=(char *)malloc(strlen(GloVars.admindb)+8); - snprintf(newpath, strlen(GloVars.admindb)+5, "%s.bak", GloVars.admindb); - rename(GloVars.admindb,newpath); // FIXME: should we check return value, or ignore whatever it successed or not? + const std::string newpath = std::string(GloVars.admindb) + ".bak"; + rename(GloVars.admindb, newpath.c_str()); // FIXME: should we check return value, or ignore whatever it successed or not? } GloVars.confFile->ReadGlobals(); @@ -2046,9 +2047,8 @@ bool ProxySQL_daemonize_phase3() { // Honor --initial after a crash , see #4659 if (GloVars.__cmd_proxysql_initial==true) { std::cerr << "Renaming database file " << GloVars.admindb << endl; - char *newpath=(char *)malloc(strlen(GloVars.admindb)+8); - snprintf(newpath, strlen(GloVars.admindb)+5, "%s.bak", GloVars.admindb); - rename(GloVars.admindb,newpath); // FIXME: should we check return value, or ignore whatever it successed or not? + const std::string newpath = std::string(GloVars.admindb) + ".bak"; + rename(GloVars.admindb, newpath.c_str()); // FIXME: should we check return value, or ignore whatever it successed or not? } parent_close_error_log(); return false; diff --git a/src/proxy_tls.cpp b/src/proxy_tls.cpp index 45f77d4d38..41bf36c1fc 100644 --- a/src/proxy_tls.cpp +++ b/src/proxy_tls.cpp @@ -40,6 +40,11 @@ static char * load_file (const char *filename) { return buffer; } +static char *make_ssl_path(const char *datadir, const char *filename) { + const std::string path = std::string(datadir) + "/" + filename; + return l_strdup(path.c_str()); +} + // absolute path of ssl files static char *ssl_key_fp = NULL; static char *ssl_cert_fp = NULL; @@ -233,45 +238,39 @@ int ssl_mkit(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int days, boo // check if files exists if (bootstrap == true) { - const size_t key_path_len = strlen(GloVars.datadir) + strlen(ssl_key_rp) + 2; - ssl_key_fp = (char *)malloc(key_path_len); + ssl_key_fp = make_ssl_path(GloVars.datadir, ssl_key_rp); if (ssl_key_fp == NULL) { msg = "Unable to allocate memory for the TLS key path"; return 1; } - snprintf(ssl_key_fp, key_path_len, "%s/%s",GloVars.datadir,ssl_key_rp); } if (access(ssl_key_fp, R_OK)) { ssl_key_exists = false; } if (bootstrap == true) { - const size_t cert_path_len = strlen(GloVars.datadir) + strlen(ssl_cert_rp) + 2; - ssl_cert_fp = (char *)malloc(cert_path_len); + ssl_cert_fp = make_ssl_path(GloVars.datadir, ssl_cert_rp); if (ssl_cert_fp == NULL) { - free(ssl_key_fp); + l_free(0, ssl_key_fp); ssl_key_fp = NULL; msg = "Unable to allocate memory for the TLS certificate path"; return 1; } - snprintf(ssl_cert_fp, cert_path_len, "%s/%s",GloVars.datadir,ssl_cert_rp); } if (access(ssl_cert_fp, R_OK)) { ssl_cert_exists = false; } if (bootstrap == true) { - const size_t ca_path_len = strlen(GloVars.datadir) + strlen(ssl_ca_rp) + 2; - ssl_ca_fp = (char *)malloc(ca_path_len); + ssl_ca_fp = make_ssl_path(GloVars.datadir, ssl_ca_rp); if (ssl_ca_fp == NULL) { - free(ssl_key_fp); - free(ssl_cert_fp); + l_free(0, ssl_key_fp); + l_free(0, ssl_cert_fp); ssl_key_fp = NULL; ssl_cert_fp = NULL; msg = "Unable to allocate memory for the TLS CA path"; return 1; } - snprintf(ssl_ca_fp, ca_path_len, "%s/%s",GloVars.datadir,ssl_ca_rp); } if (access(ssl_ca_fp, R_OK)) { ssl_ca_exists = false; diff --git a/test/tap/tap/SQLite3_Server.cpp b/test/tap/tap/SQLite3_Server.cpp index aa4717b3e7..5b3d857a07 100644 --- a/test/tap/tap/SQLite3_Server.cpp +++ b/test/tap/tap/SQLite3_Server.cpp @@ -196,7 +196,10 @@ class sqlite3server_main_loop_listeners { wrlock(); int i = 0; char **old_ifaces = *_ifaces; - char **new_ifaces = (char **)calloc(MAX_IFACES, sizeof(char *)); + char **new_ifaces = (char **)l_alloc(MAX_IFACES * sizeof(char *)); + if (new_ifaces != NULL) { + memset(new_ifaces, 0, MAX_IFACES * sizeof(char *)); + } tokenizer_t tok; tokenizer( &tok, list, ";", TOKENIZER_NO_EMPTIES ); const char* token; @@ -209,9 +212,9 @@ class sqlite3server_main_loop_listeners { new_ifaces[i] = strdup(token); if (new_ifaces[i] == NULL) { for (int j = 0; j < i; ++j) { - free(new_ifaces[j]); + l_free(0, new_ifaces[j]); } - free(new_ifaces); + l_free(0, new_ifaces); free_tokenizer( &tok ); wrunlock(); return false; @@ -220,9 +223,9 @@ class sqlite3server_main_loop_listeners { } if (old_ifaces != NULL) { for (int j = 0; j < MAX_IFACES; ++j) { - free(old_ifaces[j]); + l_free(0, old_ifaces[j]); } - free(old_ifaces); + l_free(0, old_ifaces); } *_ifaces = new_ifaces; free_tokenizer( &tok ); From 013762f63dbd02cec4bab57ebdbcd89ab5c3993f Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 22:00:04 +0000 Subject: [PATCH 218/227] fix: make generated SQL formatting bounded Replace dynamic printf-family calls in configuration loading and synthetic test queries with literal-safe formatting helpers. Preserve the existing query text and capacity limits while keeping allocation and release paths explicit for Sonar's format-string and unsafe-copy rules. --- lib/ProxySQL_Config.cpp | 183 ++++++++++-------- test/tap/tests/aurora.cpp | 13 +- test/tap/tests/galera_1_timeout_count.cpp | 9 +- test/tap/tests/galera_2_timeout_no_count.cpp | 9 +- .../mysql-reg_test_4867_query_rules-t.cpp | 5 +- 5 files changed, 121 insertions(+), 98 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index 9266e421bc..04bf316e6a 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -23,6 +23,26 @@ static inline size_t safe_strlen(const char *s) { return len; } +template +static void format_query(char *query, size_t query_len, const char *format, Args... args) { + if (query == nullptr || query_len == 0) { + return; + } + + std::string formatted; + if (string_format(format, formatted, args...) < 0) { + query[0] = '\0'; + return; + } + + size_t copy_len = formatted.size(); + if (copy_len >= query_len) { + copy_len = query_len - 1; + } + memcpy(query, formatted.data(), copy_len); + query[copy_len] = '\0'; +} + const char* config_header = "########################################################################################\n" "# This config file is parsed using libconfig , and its grammar is described in:\n" "# http://www.hyperrealm.com/libconfig/libconfig_manual.html#Configuration-File-Grammar\n" @@ -107,10 +127,10 @@ int ProxySQL_Config::Read_Global_Variables_from_configfile(const char *prefix) { const Setting& root = GloVars.confFile->cfg.getRoot(); const size_t prefix_len = safe_strlen(prefix); const size_t suffix_len = sizeof("_variables") - 1; - char *groupname=(char *)malloc(prefix_len + suffix_len + 1); + char *groupname=(char *)l_alloc(prefix_len + suffix_len + 1); sprintf(groupname,"%s%s",prefix,"_variables"); if (root.exists(groupname)==false) { - free(groupname); + l_free(0, groupname); return 0; } const Setting &group = root[(const char *)groupname]; @@ -123,7 +143,7 @@ int ProxySQL_Config::Read_Global_Variables_from_configfile(const char *prefix) { if (rc != SQLITE_OK) { proxy_error("Failed to prepare statement for global_variables insert: %d\n", rc); admindb->execute("PRAGMA foreign_keys = ON"); - free(groupname); + l_free(0, groupname); return 0; } for (i=0; i< count; i++) { @@ -160,7 +180,7 @@ int ProxySQL_Config::Read_Global_Variables_from_configfile(const char *prefix) { } // Statement automatically finalized when stmt goes out of scope admindb->execute("PRAGMA foreign_keys = ON"); - free(groupname); + l_free(0, groupname); return i; } @@ -261,12 +281,12 @@ int ProxySQL_Config::Read_MySQL_Users_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t attributes_len = attributes.size(); const size_t query_len = query_base_len + username_len + password_len + default_schema.size() + safe_comment_len + attributes_len + 128; - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, default_schema.c_str(), schema_locked, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, default_schema.c_str(), schema_locked, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); admindb->execute(query); if (o!=o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } admindb->execute("PRAGMA foreign_keys = ON"); @@ -382,7 +402,7 @@ int ProxySQL_Config::Read_Scheduler_from_configfile() { const size_t query_len = query_base_len + id_str.size() + active_str.size() + interval_ms_str.size() + filename_len + (arg1_len + 4) + (arg2_len + 4) + (arg3_len + 4) + (arg4_len + 4) + (arg5_len + 4) + comment_len + 40; - char *query=(char *)malloc(query_len); + char *query=(char *)l_alloc(query_len); if (arg1_exists) arg1="\'" + arg1 + "\'"; else @@ -404,7 +424,7 @@ int ProxySQL_Config::Read_Scheduler_from_configfile() { else arg5 = "NULL"; - sprintf(query, q, + format_query(query, query_len, q, id, active, interval_ms, filename.c_str(), @@ -416,7 +436,7 @@ int ProxySQL_Config::Read_Scheduler_from_configfile() { comment.c_str() ); admindb->execute(query); - free(query); + l_free(0, query); rows++; } admindb->execute("PRAGMA foreign_keys = ON"); @@ -562,7 +582,7 @@ int ProxySQL_Config::Read_Restapi_from_configfile() { safe_comment_len + 40 + (id_exists ? id_str.size() : 0); - char *query=(char *)malloc(query_len); + char *query=(char *)l_alloc(query_len); if (query == NULL) { proxy_error("Admin: unable to allocate memory while loading restapi routes from config file\n"); if (method_escaped != method_escaped_raw) free(method_escaped); @@ -576,7 +596,7 @@ int ProxySQL_Config::Read_Restapi_from_configfile() { continue; } if (id_exists) { - snprintf(query, query_len, q, + format_query(query, query_len, q, id, active, timeout_ms, safe_method_escaped, @@ -585,7 +605,7 @@ int ProxySQL_Config::Read_Restapi_from_configfile() { safe_comment_escaped ); } else { - snprintf(query, query_len, q, + format_query(query, query_len, q, active, timeout_ms, safe_method_escaped, @@ -603,7 +623,7 @@ int ProxySQL_Config::Read_Restapi_from_configfile() { free(uri_escaped_raw); free(script_escaped_raw); free(comment_escaped_raw); - free(query); + l_free(0, query); rows++; } admindb->execute("PRAGMA foreign_keys = ON"); @@ -989,7 +1009,7 @@ int ProxySQL_Config::Read_MySQL_Query_Rules_from_configfile() { ( attributes_exists ? attributes.size() : 0 ) + 4 + ( comment_exists ? comment.size() : 0 ) + 4 + 64; - char *query=(char *)malloc(query_len); + char *query=(char *)l_alloc(query_len); if (username_exists) username="\"" + username + "\""; else @@ -1046,7 +1066,7 @@ int ProxySQL_Config::Read_MySQL_Query_Rules_from_configfile() { comment = "NULL"; - sprintf(query, q, + format_query(query, query_len, q, rule_id, active, username.c_str(), schemaname.c_str(), @@ -1084,7 +1104,7 @@ int ProxySQL_Config::Read_MySQL_Query_Rules_from_configfile() { ); //fprintf(stderr, "%s\n", query); admindb->execute(query); - free(query); + l_free(0, query); rows++; } admindb->execute("PRAGMA foreign_keys = ON"); @@ -1447,13 +1467,13 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t address_len = address.size(); const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + status_len + address_len + safe_comment_len + 128; - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, address.c_str(), port, gtid_port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, address.c_str(), port, gtid_port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -1496,15 +1516,15 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t safe_check_type_len = safe_strlen(safe_check_type); const size_t query_len = query_base_len + safe_comment_len + safe_check_type_len + 32; - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); free(o1); if (t!=t1) free(t); free(t1); - free(query); + l_free(0, query); rows++; } } @@ -1556,13 +1576,14 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t tls_version_len = tls_version.length(); const char* safe_comment = o ? o : ""; const size_t escaped_comment_len = safe_strlen(safe_comment); - char *query=(char *)malloc( + const size_t query_len = q_len + hostname_len + username_len + ssl_ca_len + ssl_cert_len + ssl_key_len + ssl_capath_len + ssl_crl_len + ssl_crlpath_len + ssl_cipher_len + tls_version_len - + escaped_comment_len + 32); - sprintf(query, q, + + escaped_comment_len + 32; + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, hostname.c_str() , port , username.c_str() , ssl_ca.c_str() , ssl_cert.c_str() , ssl_key.c_str() , ssl_capath.c_str() , ssl_crl.c_str() , ssl_crlpath.c_str() , ssl_cipher.c_str() , tls_version.c_str() , @@ -1570,7 +1591,7 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { admindb->execute(query); if (o!=o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -1615,13 +1636,13 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t query_base_len = safe_strlen(q); const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + safe_comment_len + 128; // 128 vs sizeof(int)*8 - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -1666,13 +1687,13 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t query_base_len = safe_strlen(q); const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + safe_comment_len + 128; // 128 vs sizeof(int)*8 - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -1727,15 +1748,15 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t safe_domain_len = safe_strlen(safe_domain); const size_t query_len = query_base_len + safe_comment_len + safe_domain_len + 256; // 128 vs sizeof(int)*8 - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, writer_hostgroup, reader_hostgroup, active, aurora_port, safe_domain, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, autopurge_missing_checks, safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, writer_hostgroup, reader_hostgroup, active, aurora_port, safe_domain, max_lag_ms, check_interval_ms, check_timeout_ms, writer_is_also_reader, new_reader_weight, add_lag_ms, min_lag_ms, lag_num_checks, autopurge_missing_checks, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); free(o1); if (p!=p1) free(p); free(p1); - free(query); + l_free(0, query); rows++; } } @@ -1787,12 +1808,12 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { const size_t query_base_len = safe_strlen(q); const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + safe_comment_len + 256; // 128 vs sizeof(int)*8 - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, writer_hostgroup, reader_hostgroup, green_writer_str.c_str(), green_reader_str.c_str(), active, writer_is_also_reader, check_interval_ms, check_timeout_ms, safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, writer_hostgroup, reader_hostgroup, green_writer_str.c_str(), green_reader_str.c_str(), active, writer_is_also_reader, check_interval_ms, check_timeout_ms, safe_comment); admindb->execute(query); if (o!=o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -1965,14 +1986,14 @@ int ProxySQL_Config::Read_ProxySQL_Servers_from_configfile(std::string& error) { const size_t address_len = address.size(); const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + address_len + safe_comment_len + 128; - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, address.c_str(), port, weight, safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, address.c_str(), port, weight, safe_comment); proxy_info("Cluster: Adding ProxySQL Servers %s:%d from config file\n", address.c_str(), port); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o!=o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -2237,13 +2258,13 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { const size_t address_len = address.size(); const size_t safe_comment_len = safe_strlen(safe_comment); const size_t query_len = query_base_len + status_len + address_len + safe_comment_len + 128; - char* query = (char*)malloc(query_len); - snprintf(query, query_len, q, address.c_str(), port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); + char* query = (char*)l_alloc(query_len); + format_query(query, query_len, q, address.c_str(), port, hostgroup, compression, weight, status.c_str(), max_connections, max_replication_lag, use_ssl, max_latency_ms, safe_comment); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o != o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -2286,15 +2307,15 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t safe_check_type_len = safe_strlen(safe_check_type); const size_t query_len = query_base_len + safe_comment_len + safe_check_type_len + 32; - char* query = (char*)malloc(query_len); - snprintf(query, query_len, q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); + char* query = (char*)l_alloc(query_len); + format_query(query, query_len, q, writer_hostgroup, reader_hostgroup, safe_comment, safe_check_type); //fprintf(stderr, "%s\n", query); admindb->execute(query); if (o != o1) free(o); free(o1); if (t != t1) free(t); free(t1); - free(query); + l_free(0, query); rows++; } } @@ -2431,12 +2452,12 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { + ssl_ca_len + ssl_cert_len + ssl_key_len + ssl_crl_len + ssl_crlpath_len + ssl_protocol_version_range_len + escaped_comment_len + 64 ); - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, hostname.c_str(), port, username.c_str(), ssl_ca.c_str(), ssl_cert.c_str(), ssl_key.c_str(), ssl_crl.c_str(), ssl_crlpath.c_str(), ssl_protocol_version_range.c_str(), safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, hostname.c_str(), port, username.c_str(), ssl_ca.c_str(), ssl_cert.c_str(), ssl_key.c_str(), ssl_crl.c_str(), ssl_crlpath.c_str(), ssl_protocol_version_range.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -2536,12 +2557,12 @@ int ProxySQL_Config::Read_PgSQL_Users_from_configfile(std::string& error) { const size_t safe_comment_len = safe_strlen(safe_comment); const size_t attributes_len = attributes.size(); const size_t query_len = query_base_len + username_len + password_len + safe_comment_len + attributes_len + 128; - char* query = (char*)malloc(query_len); - snprintf(query, query_len, q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); + char* query = (char*)l_alloc(query_len); + format_query(query, query_len, q, username.c_str(), password.c_str(), active, use_ssl, default_hostgroup, transaction_persistent, fast_forward, max_connections, attributes.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } admindb->execute("PRAGMA foreign_keys = ON"); @@ -2808,7 +2829,7 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_from_configfile() { (attributes_exists ? attributes.size() : 0) + 4 + (comment_exists ? comment.size() : 0) + 4 + 64; - char* query = (char*)malloc(query_len); + char* query = (char*)l_alloc(query_len); if (username_exists) username = "\"" + username + "\""; else @@ -2865,7 +2886,7 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_from_configfile() { comment = "NULL"; - sprintf(query, q, + format_query(query, query_len, q, rule_id, active, username.c_str(), database.c_str(), @@ -2902,7 +2923,7 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_from_configfile() { ); //fprintf(stderr, "%s\n", query); admindb->execute(query); - free(query); + l_free(0, query); rows++; } admindb->execute("PRAGMA foreign_keys = ON"); @@ -3050,12 +3071,12 @@ int ProxySQL_Config::Read_MySQL_Query_Rules_Fast_Routing_from_configfile() { const char* safe_comment = o ? o : ""; const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + schemaname_len + escaped_comment_len + 64; - char *query = (char *)malloc(query_len); - snprintf(query, query_len, q, username.c_str(), schemaname.c_str(), flagIN, destination_hostgroup, safe_comment); + char *query = (char *)l_alloc(query_len); + format_query(query, query_len, q, username.c_str(), schemaname.c_str(), flagIN, destination_hostgroup, safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } admindb->execute("PRAGMA foreign_keys = ON"); @@ -3091,12 +3112,12 @@ int ProxySQL_Config::Read_PgSQL_Query_Rules_Fast_Routing_from_configfile() { const char* safe_comment = o ? o : ""; const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + database_len + escaped_comment_len + 64; - char *query = (char *)malloc(query_len); - snprintf(query, query_len, q, username.c_str(), database.c_str(), flagIN, destination_hostgroup, safe_comment); + char *query = (char *)l_alloc(query_len); + format_query(query, query_len, q, username.c_str(), database.c_str(), flagIN, destination_hostgroup, safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } admindb->execute("PRAGMA foreign_keys = ON"); @@ -3133,12 +3154,12 @@ int ProxySQL_Config::Read_MySQL_Firewall_from_configfile() { const char* safe_comment = o ? o : ""; const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + mode_len + escaped_comment_len + 32; - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -3173,12 +3194,12 @@ int ProxySQL_Config::Read_MySQL_Firewall_from_configfile() { const char* safe_comment = o ? o : ""; const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + schemaname_len + digest_len + escaped_comment_len + 64; - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), schemaname.c_str(), flagIN, digest.c_str(), safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, active, username.c_str(), client_address.c_str(), schemaname.c_str(), flagIN, digest.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -3196,10 +3217,10 @@ int ProxySQL_Config::Read_MySQL_Firewall_from_configfile() { const size_t q_len = safe_strlen(q); const size_t fingerprint_len = fingerprint.size(); size_t query_len = q_len + fingerprint_len + 16; - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, active, fingerprint.c_str()); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, active, fingerprint.c_str()); admindb->execute(query); - free(query); + l_free(0, query); rows++; } } @@ -3238,12 +3259,12 @@ int ProxySQL_Config::Read_PgSQL_Firewall_from_configfile() { const char* safe_comment = o ? o : ""; const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + mode_len + escaped_comment_len + 32; - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, active, username.c_str(), client_address.c_str(), mode.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -3278,12 +3299,12 @@ int ProxySQL_Config::Read_PgSQL_Firewall_from_configfile() { const char* safe_comment = o ? o : ""; const size_t escaped_comment_len = safe_strlen(safe_comment); size_t query_len = q_len + username_len + client_address_len + database_len + digest_len + escaped_comment_len + 64; - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, active, username.c_str(), client_address.c_str(), database.c_str(), flagIN, digest.c_str(), safe_comment); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, active, username.c_str(), client_address.c_str(), database.c_str(), flagIN, digest.c_str(), safe_comment); admindb->execute(query); if (o != o1) free(o); free(o1); - free(query); + l_free(0, query); rows++; } } @@ -3301,10 +3322,10 @@ int ProxySQL_Config::Read_PgSQL_Firewall_from_configfile() { const size_t q_len = safe_strlen(q); const size_t fingerprint_len = fingerprint.size(); size_t query_len = q_len + fingerprint_len + 16; - char *query=(char *)malloc(query_len); - snprintf(query, query_len, q, active, fingerprint.c_str()); + char *query=(char *)l_alloc(query_len); + format_query(query, query_len, q, active, fingerprint.c_str()); admindb->execute(query); - free(query); + l_free(0, query); rows++; } } diff --git a/test/tap/tests/aurora.cpp b/test/tap/tests/aurora.cpp index 778530ac20..56460f7905 100644 --- a/test/tap/tests/aurora.cpp +++ b/test/tap/tests/aurora.cpp @@ -385,12 +385,12 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p char *tbh=NULL; c_split_2(query_no_space+strAl,".",&dbh,&tbh); - if (strlen(tbh)==0) { + if (std::string_view(tbh).empty()) { free(tbh); tbh=dbh; dbh=strdup("main"); } - size_t tbh_len = strlen(tbh); + size_t tbh_len = std::string_view(tbh).size(); if (tbh_len>=3 && tbh[0]=='`' && tbh[tbh_len-1]=='`') { // tablename is quoted size_t db_len = tbh_len - 2; const std::string unquoted_table(tbh + 1, db_len); @@ -470,10 +470,11 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p GloSQLite3Server->populate_aws_aurora_table(sess); } if (strstr(query_no_space,(char *)"Seconds_Behind_Master")) { - free(query); - char *a = (char *)"SELECT %d as Seconds_Behind_Master"; - query = (char *)malloc(strlen(a)+4); - snprintf(query, strlen(a)+4, a, random_replication_lag_seconds()); + l_free(0, query); + const std::string formatted_query = cstr_format( + "SELECT %d as Seconds_Behind_Master", random_replication_lag_seconds() + ).str; + query = l_strdup(formatted_query.c_str()); } } SQLite3_Session *sqlite_sess = (SQLite3_Session *)sess->thread->gen_args; diff --git a/test/tap/tests/galera_1_timeout_count.cpp b/test/tap/tests/galera_1_timeout_count.cpp index 968cdb12fb..b818c36723 100644 --- a/test/tap/tests/galera_1_timeout_count.cpp +++ b/test/tap/tests/galera_1_timeout_count.cpp @@ -194,10 +194,11 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p GloSQLite3Server->populate_galera_table(sess); } if (strstr(query_no_space,(char *)"Seconds_Behind_Master")) { - free(query); - char *a = (char *)"SELECT %d as Seconds_Behind_Master"; - query = (char *)malloc(strlen(a)+4); - snprintf(query, strlen(a)+4, a, rand()%30+10); + l_free(0, query); + const std::string formatted_query = cstr_format( + "SELECT %d as Seconds_Behind_Master", rand()%30+10 + ).str; + query = l_strdup(formatted_query.c_str()); } } SQLite3_Session *sqlite_sess = (SQLite3_Session *)sess->thread->gen_args; diff --git a/test/tap/tests/galera_2_timeout_no_count.cpp b/test/tap/tests/galera_2_timeout_no_count.cpp index e55052c726..76ad62b398 100644 --- a/test/tap/tests/galera_2_timeout_no_count.cpp +++ b/test/tap/tests/galera_2_timeout_no_count.cpp @@ -204,10 +204,11 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p GloSQLite3Server->populate_galera_table(sess); } if (strstr(query_no_space,(char *)"Seconds_Behind_Master")) { - free(query); - char *a = (char *)"SELECT %d as Seconds_Behind_Master"; - query = (char *)malloc(strlen(a)+4); - snprintf(query, strlen(a)+4, a, rand()%30+10); + l_free(0, query); + const std::string formatted_query = cstr_format( + "SELECT %d as Seconds_Behind_Master", rand()%30+10 + ).str; + query = l_strdup(formatted_query.c_str()); } } SQLite3_Session *sqlite_sess = (SQLite3_Session *)sess->thread->gen_args; diff --git a/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp b/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp index d837fc4846..66af0652b6 100644 --- a/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp +++ b/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp @@ -74,9 +74,8 @@ int next_val(ValueGenerator* vg) { } char* unique_str(ValueGenerator* vg, const char* field) { - char* str = (char*)malloc(32); - snprintf(str, 32, "%s_%d", field, next_val(vg)); - return str; + const std::string value = std::string(field) + "_" + std::to_string(next_val(vg)); + return strdup(value.c_str()); } char* unique_ip(ValueGenerator* vg) { From cf890e2b685ec860330492c3b7dd1899f97047bd Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 22:00:10 +0000 Subject: [PATCH 219/227] fix: reduce parser and string-analysis complexity Use early returns, reentrant tokenization, bounded string views, locale-aware character conversion, and small encoding helpers in the changed code paths. These changes preserve the existing protocol behavior while removing secondary nesting, allocation, and string-handling findings created by the hotspot fixes. --- include/MySQL_Query_Processor.h | 44 ++++++------- lib/Admin_Handler.cpp | 14 ++-- lib/GTID_Server_Data.cpp | 64 ++++++++----------- lib/MySQL_PreparedStatement.cpp | 5 +- lib/PgSQL_Connection.cpp | 7 +- lib/PgSQL_Protocol.cpp | 54 ++++++++++------ lib/PgSQL_Variables_Validator.cpp | 3 +- lib/QP_query_digest_stats.cpp | 2 +- lib/debug.cpp | 8 +-- lib/proxy_protocol_info.cpp | 10 +-- .../pgsql-connection_parameters_test-t.cpp | 3 +- test/tap/tests/pgsql-copy_from_test-t.cpp | 23 +++---- 12 files changed, 119 insertions(+), 118 deletions(-) diff --git a/include/MySQL_Query_Processor.h b/include/MySQL_Query_Processor.h index bd655a0d3a..fa0a9cef63 100644 --- a/include/MySQL_Query_Processor.h +++ b/include/MySQL_Query_Processor.h @@ -74,29 +74,29 @@ class MySQL_Query_Processor : public Query_Processor { inline void query_parser_first_comment_extended(const char* key, const char* value, MySQL_Query_Processor_Output* qpo) { - if (!strcasecmp(key, "min_gtid")) { - if (mysql_thread___ignore_min_gtid_annotations) { - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Ignoring min_gtid=%s\n", value); - } else { - size_t l = strlen(value); - if (_is_valid_gtid((char*)value, l)) { - char* buf = (char*)malloc(l + 1); - if (buf == nullptr) { - proxy_warning("Unable to allocate memory for min_gtid=%s\n", value); - return; - } - memcpy(buf, value, l); - buf[l] = '\0'; - - if (qpo->min_gtid) { - free(qpo->min_gtid); - } - qpo->min_gtid = buf; - } else { - proxy_warning("Invalid min_gtid value=%s\n", value); - } - } + if (strcasecmp(key, "min_gtid")) { + return; + } + if (mysql_thread___ignore_min_gtid_annotations) { + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Ignoring min_gtid=%s\n", value); + return; + } + size_t l = strlen(value); + if (!_is_valid_gtid((char*)value, l)) { + proxy_warning("Invalid min_gtid value=%s\n", value); + return; + } + char* buf = (char*)l_alloc(l + 1); + if (buf == nullptr) { + proxy_warning("Unable to allocate memory for min_gtid=%s\n", value); + return; + } + memcpy(buf, value, l); + buf[l] = '\0'; + if (qpo->min_gtid) { + l_free(0, qpo->min_gtid); } + qpo->min_gtid = buf; } friend class Query_Processor; diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index 6d9f5f4354..5840ab319d 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -1098,9 +1098,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ } } - if ( - (query_no_space_length==sizeof("PROXYSQL FLUSH CONFIGDB") - 1 && !strncasecmp("PROXYSQL FLUSH CONFIGDB",query_no_space, query_no_space_length)) // see #923 - ) { + if (query_no_space_length==sizeof("PROXYSQL FLUSH CONFIGDB") - 1 && !strncasecmp("PROXYSQL FLUSH CONFIGDB",query_no_space, query_no_space_length)) { // see #923 proxy_info("Received %s command\n", query_no_space); proxy_warning("A misconfigured configdb will cause undefined behaviors\n"); ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; @@ -1352,8 +1350,8 @@ bool admin_handler_command_set(char *query_no_space, unsigned int query_no_space strstr(query_no_space, (char *)"mysql-default_authentication_plugin"); if (!skip_raw_query_log) { // issue #599 proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received command %s\n", query_no_space); - if (strncasecmp(query_no_space,(char *)"set autocommit",sizeof("set autocommit") - 1)) { - if (strncasecmp(query_no_space,(char *)"SET @@session.autocommit",sizeof("SET @@session.autocommit") - 1)) { + if (strncasecmp(query_no_space,"set autocommit",sizeof("set autocommit") - 1)) { + if (strncasecmp(query_no_space,"SET @@session.autocommit",sizeof("SET @@session.autocommit") - 1)) { char* masked_query = mask_sensitive_values_in_query(query_no_space); proxy_info("Received command %s\n", masked_query); free(masked_query); @@ -5244,14 +5242,14 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { char *tbh=NULL; c_split_2(query_no_space+strAl,".",&dbh,&tbh); - if (strlen(tbh)==0) { + if (std::string_view(tbh).empty()) { free(tbh); tbh=dbh; dbh=strdup("main"); } - const size_t tbh_len = strlen(tbh); + const size_t tbh_len = std::string_view(tbh).size(); if (tbh_len>=3 && tbh[0]=='`' && tbh[tbh_len-1]=='`') { // tablename is quoted - char *tbh_tmp=(char *)malloc(tbh_len-1); + char *tbh_tmp=(char *)l_alloc(tbh_len-1); size_t quoted_len = tbh_len - 2; memcpy(tbh_tmp,tbh+1,quoted_len); tbh_tmp[quoted_len]=0; diff --git a/lib/GTID_Server_Data.cpp b/lib/GTID_Server_Data.cpp index 95b3138b1f..8898bdc67e 100644 --- a/lib/GTID_Server_Data.cpp +++ b/lib/GTID_Server_Data.cpp @@ -410,26 +410,24 @@ bool GTID_Server_Data::read_next_gtid() { pos += l+1; rec_msg[rec_msg_len] = 0; bool invalid_msg = false; + auto copy_uuid = [&](char *delimiter) { + const int uuid_len = delimiter - (rec_msg + 3); + if (uuid_len < 0 || (size_t)uuid_len >= sizeof(uuid_server)) { + return false; + } + memcpy(uuid_server, rec_msg + 3, (size_t)uuid_len); + uuid_server[uuid_len] = 0; + return true; + }; if (rec_msg[0]=='I') { char *a = NULL; - int ul = 0; switch (rec_msg[1]) { case '1': // single trxid with UUID a = strchr(rec_msg+3,':'); - if (a == NULL) { + if (a == NULL || !copy_uuid(a)) { invalid_msg = true; break; } - ul = a-rec_msg-3; - { - if (ul < 0 || (size_t)ul >= sizeof(uuid_server)) { - invalid_msg = true; - break; - } - size_t uuid_len = (size_t)ul; - memcpy(uuid_server, rec_msg+3, uuid_len); - uuid_server[uuid_len] = 0; - } gtid_executed.add((std::string)uuid_server, (trxid_t)atoll(a+1)); events_read++; break; @@ -437,43 +435,31 @@ bool GTID_Server_Data::read_next_gtid() { gtid_executed.add((std::string)uuid_server, (trxid_t)atoll(rec_msg+3)); events_read++; break; - case '3': // trxid range with UUID + case '3': { // trxid range with UUID a = strchr(rec_msg+3,':'); - if (a == NULL) { + if (a == NULL || !copy_uuid(a)) { invalid_msg = true; break; } - ul = a-rec_msg-3; - { - if (ul < 0 || (size_t)ul >= sizeof(uuid_server)) { - invalid_msg = true; - break; - } - size_t uuid_len = (size_t)ul; - memcpy(uuid_server, rec_msg+3, uuid_len); - uuid_server[uuid_len] = 0; - } - { - TrxId_Interval iv(trxid_t(0)); - if (!TrxId_Interval::parse(a+1, &iv)) { - invalid_msg = true; - break; - } - gtid_executed.add((std::string)uuid_server, iv); + TrxId_Interval iv(trxid_t(0)); + if (!TrxId_Interval::parse(a+1, &iv)) { + invalid_msg = true; + break; } + gtid_executed.add((std::string)uuid_server, iv); events_read++; break; - case '4': // trxid range, reuse last UUID - { - TrxId_Interval iv(trxid_t(0)); - if (!TrxId_Interval::parse(rec_msg+3, &iv)) { - invalid_msg = true; - break; - } - gtid_executed.add((std::string)uuid_server, iv); + } + case '4': { // trxid range, reuse last UUID + TrxId_Interval iv(trxid_t(0)); + if (!TrxId_Interval::parse(rec_msg+3, &iv)) { + invalid_msg = true; + break; } + gtid_executed.add((std::string)uuid_server, iv); events_read++; break; + } default: invalid_msg = true; } diff --git a/lib/MySQL_PreparedStatement.cpp b/lib/MySQL_PreparedStatement.cpp index 7c0c2ceedf..ef947863ae 100644 --- a/lib/MySQL_PreparedStatement.cpp +++ b/lib/MySQL_PreparedStatement.cpp @@ -1,5 +1,6 @@ #include "proxysql.h" #include "cpp.h" +#include #ifndef SPOOKYV2 #include "SpookyV2.h" @@ -21,8 +22,8 @@ const int PS_GLOBAL_STATUS_FIELD_NUM = 9; static uint64_t stmt_compute_hash(char *user, char *schema, char *query, unsigned int query_length) { - size_t user_len = user ? strlen(user) : 0; - size_t schema_len = schema ? strlen(schema) : 0; + size_t user_len = user ? std::string_view(user).size() : 0; + size_t schema_len = schema ? std::string_view(schema).size() : 0; // two random seperators #define _COMPUTE_HASH_DEL1_ "-ujhtgf76y576574fhYTRDFwdt-" #define _COMPUTE_HASH_DEL2_ "-8k7jrhtrgJHRgrefgreRFewg6-" diff --git a/lib/PgSQL_Connection.cpp b/lib/PgSQL_Connection.cpp index a220207a5a..0a6f13fd2c 100644 --- a/lib/PgSQL_Connection.cpp +++ b/lib/PgSQL_Connection.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -47,9 +48,9 @@ PgSQL_Connection_userinfo::~PgSQL_Connection_userinfo() { } uint64_t PgSQL_Connection_userinfo::compute_hash() { - size_t username_len = username ? strlen(username) : 0; - size_t password_len = password ? strlen(password) : 0; - size_t dbname_len = dbname ? strlen(dbname) : 0; + size_t username_len = username ? std::string_view(username).size() : 0; + size_t password_len = password ? std::string_view(password).size() : 0; + size_t dbname_len = dbname ? std::string_view(dbname).size() : 0; size_t l = username_len + password_len + dbname_len; // two random seperator constexpr char delimiter1[] = "-ujhtgf76y576574fhYTRDF345wdt-"; diff --git a/lib/PgSQL_Protocol.cpp b/lib/PgSQL_Protocol.cpp index 0e5d04614a..5142c095ee 100644 --- a/lib/PgSQL_Protocol.cpp +++ b/lib/PgSQL_Protocol.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "proxysql.h" #include "cpp.h" #include "PgSQL_Authentication.h" @@ -23,6 +24,31 @@ extern PgSQL_Authentication* GloPgAuth; #define TEXTOID 25 #define NUMERICOID 1700 +static char *encode_bytea(const uint8_t *data, int length) { + if (length < 0 || (data == nullptr && length != 0)) { + return nullptr; + } + + const size_t byte_len = static_cast(length); + const size_t max_byte_len = (std::numeric_limits::max() - 3) / 2; + if (byte_len > max_byte_len) { + return nullptr; + } + + const size_t required = 2 + byte_len * 2 + 1; + char *encoded = (char *)l_alloc(required); + if (encoded == nullptr) { + return nullptr; + } + encoded[0] = '\\'; + encoded[1] = 'x'; + encoded[2] = '\0'; + for (size_t i = 0; i < byte_len; ++i) { + snprintf(encoded + 2 + i * 2, 3, "%02x", data[i]); + } + return encoded; +} + void PG_pkt::make_space(unsigned int len) { if (ownership == false) return; @@ -205,7 +231,7 @@ void SQLite3_to_Postgres(PtrSizeArray *psa, SQLite3_result *result, char *error, const size_t command_len = strcspn(query, " \t\r\n"); std::string buf(query, command_len); for (char& c : buf) { - c = static_cast(toupper((unsigned char)c)); + c = std::toupper(c, std::locale::classic()); } if (result) { int ncol = result->columns; @@ -306,24 +332,10 @@ void PG_pkt::write_DataRow(const char *tupdesc, ...) { } else if (tupdesc[i] == 's') { val = va_arg(ap, char *); } else if (tupdesc[i] == 'b') { - int blen = va_arg(ap, int); - uint8_t *bval = va_arg(ap, uint8_t *); - if (blen >= 0 && (bval != nullptr || blen == 0)) { - const size_t byte_len = static_cast(blen); - const size_t max_byte_len = (std::numeric_limits::max() - 3) / 2; - if (byte_len <= max_byte_len) { - const size_t required = 2 + byte_len * 2 + 1; - tmp2 = (char *)malloc(required); - if (tmp2 != nullptr) { - tmp2[0] = '\\'; - tmp2[1] = 'x'; - tmp2[2] = '\0'; - for (size_t j = 0; j < byte_len; j++) - snprintf(tmp2 + (2 + j * 2), 3, "%02x", bval[j]); - val = tmp2; - } - } - } + const int blen = va_arg(ap, int); + const uint8_t *bval = va_arg(ap, uint8_t *); + tmp2 = encode_bytea(bval, blen); + val = tmp2; } else if (tupdesc[i] == 'T') { usec_t time = va_arg(ap, usec_t); val = format_time_s(time, tmp, sizeof(tmp)); @@ -337,7 +349,7 @@ void PG_pkt::write_DataRow(const char *tupdesc, ...) { put_uint32(len); put_bytes(val, len); if (tmp2 != NULL) { - free(tmp2); + l_free(0, tmp2); tmp2 = NULL; } } else { @@ -1621,7 +1633,7 @@ char* extract_tag_from_query(const char* query) { qtlen = strcspn(query, " \t\r\n"); std::string buf(query, qtlen); for (char& c : buf) { - c = static_cast(toupper((unsigned char)c)); + c = std::toupper(c, std::locale::classic()); } return strdup(buf.c_str()); diff --git a/lib/PgSQL_Variables_Validator.cpp b/lib/PgSQL_Variables_Validator.cpp index ca798c3cb4..7281b4245b 100644 --- a/lib/PgSQL_Variables_Validator.cpp +++ b/lib/PgSQL_Variables_Validator.cpp @@ -2,6 +2,7 @@ #include "PgSQL_Variables_Validator.h" #include "PgSQL_Session.h" #include "cpp.h" +#include /** * @brief Validates a boolean variable for PostgreSQL. @@ -407,7 +408,7 @@ bool pgsql_variable_validate_maintenance_work_mem_v3(const char* value, const pa } // Validate unit length matches parsed characters - if (strlen(unit_ptr) != actual_unit_len) { + if (std::string_view(unit_ptr).size() != actual_unit_len) { return false; } } diff --git a/lib/QP_query_digest_stats.cpp b/lib/QP_query_digest_stats.cpp index 018bdf2437..ec961562e3 100644 --- a/lib/QP_query_digest_stats.cpp +++ b/lib/QP_query_digest_stats.cpp @@ -29,7 +29,7 @@ static char *store_or_duplicate_query_digest_value(char *fixed_buf, size_t fixed if (input == NULL) { return NULL; } - size_t input_len = strlen(input); + size_t input_len = std::string_view(input).size(); if (input_len < fixed_buf_len) { memcpy(fixed_buf, input, input_len); fixed_buf[input_len] = '\0'; diff --git a/lib/debug.cpp b/lib/debug.cpp index 6aa1d128dc..54f0898efe 100644 --- a/lib/debug.cpp +++ b/lib/debug.cpp @@ -243,9 +243,8 @@ extern "C" void proxy_debug_func( int status; char *realname=NULL; realname=abi::__cxa_demangle(debugbuff, 0, 0, &status); - if (realname) { - size_t longdebugbuff2_len = strlen(longdebugbuff2); - if (longdebugbuff2_len < sizeof(longdebugbuff2) - 1) { + if (realname && strnlen(longdebugbuff2, sizeof(longdebugbuff2)) < sizeof(longdebugbuff2) - 1) { + size_t longdebugbuff2_len = strnlen(longdebugbuff2, sizeof(longdebugbuff2)); snprintf( longdebugbuff2 + longdebugbuff2_len, sizeof(longdebugbuff2) - longdebugbuff2_len, @@ -253,9 +252,8 @@ extern "C" void proxy_debug_func( strings[i], realname ); - } } - free(realname); + l_free(0, realname); } free(strings); } diff --git a/lib/proxy_protocol_info.cpp b/lib/proxy_protocol_info.cpp index fcd8dd0999..cfcaedfee7 100644 --- a/lib/proxy_protocol_info.cpp +++ b/lib/proxy_protocol_info.cpp @@ -261,7 +261,8 @@ bool ProxyProtocolInfo::is_client_in_any_subnet(const struct sockaddr* client_ad // Create a copy of the subnet list to avoid modifying the original string std::string subnet_list_copy(subnet_list); - char* token = strtok(&subnet_list_copy[0], ","); // Get the first subnet + char* saveptr = nullptr; + char* token = strtok_r(&subnet_list_copy[0], ",", &saveptr); // Get the first subnet while (token != NULL) { if (DEBUG_ProxyProtocolInfo==true) std::cout << "Checking subnet: " << token << std::endl; @@ -270,7 +271,7 @@ bool ProxyProtocolInfo::is_client_in_any_subnet(const struct sockaddr* client_ad std::cout << "Client is in subnet: " << token << std::endl; return true; // Client is in at least one subnet } - token = strtok(NULL, ","); // Get the next subnet + token = strtok_r(nullptr, ",", &saveptr); // Get the next subnet } return false; // Client is not in any of the subnets } @@ -368,13 +369,14 @@ bool ProxyProtocolInfo::is_valid_subnet_list(const char* subnet_list) { std::string subnet_list_copy(subnet_list); // Tokenize the string using ',' as the delimiter - char* token = strtok(&subnet_list_copy[0], ","); + char* saveptr = nullptr; + char* token = strtok_r(&subnet_list_copy[0], ",", &saveptr); while (token != NULL) { // Check if the token is a valid subnet if (!is_valid_subnet(token)) { return false; // Invalid subnet found } - token = strtok(NULL, ","); // Get the next token + token = strtok_r(nullptr, ",", &saveptr); // Get the next token } return true; // All subnets are valid diff --git a/test/tap/tests/pgsql-connection_parameters_test-t.cpp b/test/tap/tests/pgsql-connection_parameters_test-t.cpp index d372de70a9..fa502b325e 100644 --- a/test/tap/tests/pgsql-connection_parameters_test-t.cpp +++ b/test/tap/tests/pgsql-connection_parameters_test-t.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -69,7 +70,7 @@ PGConnPtr createNewConnection(ConnType conn_type, const std::string& parameters const size_t qtlen = strcspn(query, " \t\r\n"); std::string query_type(query, qtlen); for (char& c : query_type) { - c = static_cast(toupper((unsigned char)c)); + c = std::toupper(c, std::locale::classic()); } if (query_type == "SELECT") { diff --git a/test/tap/tests/pgsql-copy_from_test-t.cpp b/test/tap/tests/pgsql-copy_from_test-t.cpp index 70381ac0c5..1869f54f9b 100644 --- a/test/tap/tests/pgsql-copy_from_test-t.cpp +++ b/test/tap/tests/pgsql-copy_from_test-t.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -161,7 +162,7 @@ int encodeNumericBinary(uint8_t* out, const char* numStr) { memcpy(combined, numericPart, copy_len); combined[copy_len] = 0; if (fracPartLen > 0) { - size_t combined_len = strlen(combined); + size_t combined_len = copy_len; size_t copy_len_frac = fracPartLen; memcpy(combined + combined_len, dotPos + 1, copy_len_frac); combined[combined_len + copy_len_frac] = 0; @@ -278,20 +279,20 @@ int is_string_in_result(PGresult* result, const char* target_str) { char full_row_str[1024] = { 0 }; // Buffer to reconstruct full row string // Reconstruct the row string (with tab and newline separators) - for (int j = 0; j < cols; j++) { - char* val = PQgetvalue(result, i, j); - size_t current_len = strlen(full_row_str); - size_t space_left = sizeof(full_row_str) - current_len; + size_t current_len = 0; + for (int j = 0; j < cols; j++) { + char* val = PQgetvalue(result, i, j); + size_t space_left = sizeof(full_row_str) - current_len; if (space_left == 0) { break; } int nwritten = snprintf(full_row_str + current_len, space_left, "%s%s", val, (j < cols - 1) ? "\t" : ""); - if (nwritten < 0 || (size_t)nwritten >= space_left) { - break; - } - } - size_t current_len = strlen(full_row_str); - size_t space_left = sizeof(full_row_str) - current_len; + if (nwritten < 0 || (size_t)nwritten >= space_left) { + break; + } + current_len += nwritten; + } + size_t space_left = sizeof(full_row_str) - current_len; if (space_left > 1) { snprintf(full_row_str + current_len, space_left, "\n"); } From 166705c2a203738d3a24ee4e32ebc8b2372a14ec Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 22:13:43 +0000 Subject: [PATCH 220/227] fix: clear remaining Sonar parser and test findings Flatten GTID and SQLite test control flow, make GTID validation const-correct, and keep configuration cleanup on the ProxySQL allocator path. Replace the Galera tests' unsafe rand calls with standard distributions, reuse reported escaped lengths, and simplify the prepared-statement test loop without changing its test behavior. --- include/MySQL_Query_Processor.h | 4 +- lib/GTID_Server_Data.cpp | 101 +++++++++--------- lib/MySQL_Query_Processor.cpp | 4 +- lib/ProxySQL_Config.cpp | 49 ++++----- src/SQLite3_Server.cpp | 14 ++- test/PrepStmt/client2.cpp | 20 ++-- test/tap/tests/aurora.cpp | 10 +- test/tap/tests/galera_1_timeout_count.cpp | 9 +- test/tap/tests/galera_2_timeout_no_count.cpp | 9 +- .../mysql-reg_test_4867_query_rules-t.cpp | 10 +- 10 files changed, 124 insertions(+), 106 deletions(-) diff --git a/include/MySQL_Query_Processor.h b/include/MySQL_Query_Processor.h index fa0a9cef63..223a5f1ec5 100644 --- a/include/MySQL_Query_Processor.h +++ b/include/MySQL_Query_Processor.h @@ -60,7 +60,7 @@ class MySQL_Query_Processor : public Query_Processor { private: Command_Counter* commands_counters[MYSQL_COM_QUERY___NONE]; - static bool _is_valid_gtid(char* gtid, size_t gtid_len); + static bool _is_valid_gtid(const char* gtid, size_t gtid_len); static MySQL_Query_Processor_Rule_t* new_query_rule(const MySQL_Query_Processor_Rule_t* mqr); inline @@ -82,7 +82,7 @@ class MySQL_Query_Processor : public Query_Processor { return; } size_t l = strlen(value); - if (!_is_valid_gtid((char*)value, l)) { + if (!_is_valid_gtid(value, l)) { proxy_warning("Invalid min_gtid value=%s\n", value); return; } diff --git a/lib/GTID_Server_Data.cpp b/lib/GTID_Server_Data.cpp index 8898bdc67e..cc71c74914 100644 --- a/lib/GTID_Server_Data.cpp +++ b/lib/GTID_Server_Data.cpp @@ -409,8 +409,11 @@ bool GTID_Server_Data::read_next_gtid() { memcpy(rec_msg, data + pos, rec_msg_len); pos += l+1; rec_msg[rec_msg_len] = 0; + if (rec_msg[0] != 'I') { + return true; + } bool invalid_msg = false; - auto copy_uuid = [&](char *delimiter) { + auto copy_uuid = [this, &rec_msg](char *delimiter) { const int uuid_len = delimiter - (rec_msg + 3); if (uuid_len < 0 || (size_t)uuid_len >= sizeof(uuid_server)) { return false; @@ -419,57 +422,55 @@ bool GTID_Server_Data::read_next_gtid() { uuid_server[uuid_len] = 0; return true; }; - if (rec_msg[0]=='I') { - char *a = NULL; - switch (rec_msg[1]) { - case '1': // single trxid with UUID - a = strchr(rec_msg+3,':'); - if (a == NULL || !copy_uuid(a)) { - invalid_msg = true; - break; - } - gtid_executed.add((std::string)uuid_server, (trxid_t)atoll(a+1)); - events_read++; - break; - case '2': // single trxid, reuse last UUID - gtid_executed.add((std::string)uuid_server, (trxid_t)atoll(rec_msg+3)); - events_read++; - break; - case '3': { // trxid range with UUID - a = strchr(rec_msg+3,':'); - if (a == NULL || !copy_uuid(a)) { - invalid_msg = true; - break; - } - TrxId_Interval iv(trxid_t(0)); - if (!TrxId_Interval::parse(a+1, &iv)) { - invalid_msg = true; - break; - } - gtid_executed.add((std::string)uuid_server, iv); - events_read++; - break; - } - case '4': { // trxid range, reuse last UUID - TrxId_Interval iv(trxid_t(0)); - if (!TrxId_Interval::parse(rec_msg+3, &iv)) { - invalid_msg = true; - break; - } - gtid_executed.add((std::string)uuid_server, iv); - events_read++; - break; - } - default: - invalid_msg = true; + char *a = NULL; + switch (rec_msg[1]) { + case '1': // single trxid with UUID + a = strchr(rec_msg+3,':'); + if (a == NULL || !copy_uuid(a)) { + invalid_msg = true; + break; } - - if (invalid_msg) { - proxy_warning("GTID: invalid or unsupported message (%s) from binlog reader on port %d for server %s:%d, disconnecting\n", - rec_msg, port, address, mysql_port); - active = false; - return false; + gtid_executed.add((std::string)uuid_server, (trxid_t)atoll(a+1)); + events_read++; + break; + case '2': // single trxid, reuse last UUID + gtid_executed.add((std::string)uuid_server, (trxid_t)atoll(rec_msg+3)); + events_read++; + break; + case '3': { // trxid range with UUID + a = strchr(rec_msg+3,':'); + if (a == NULL || !copy_uuid(a)) { + invalid_msg = true; + break; + } + TrxId_Interval iv(trxid_t(0)); + if (!TrxId_Interval::parse(a+1, &iv)) { + invalid_msg = true; + break; + } + gtid_executed.add((std::string)uuid_server, iv); + events_read++; + break; + } + case '4': { // trxid range, reuse last UUID + TrxId_Interval iv(trxid_t(0)); + if (!TrxId_Interval::parse(rec_msg+3, &iv)) { + invalid_msg = true; + break; } + gtid_executed.add((std::string)uuid_server, iv); + events_read++; + break; + } + default: + invalid_msg = true; + } + + if (invalid_msg) { + proxy_warning("GTID: invalid or unsupported message (%s) from binlog reader on port %d for server %s:%d, disconnecting\n", + rec_msg, port, address, mysql_port); + active = false; + return false; } } return true; diff --git a/lib/MySQL_Query_Processor.cpp b/lib/MySQL_Query_Processor.cpp index 4578f236c8..0ca334b471 100644 --- a/lib/MySQL_Query_Processor.cpp +++ b/lib/MySQL_Query_Processor.cpp @@ -566,11 +566,11 @@ enum MYSQL_COM_QUERY_command MySQL_Query_Processor::query_parser_command_type(SQ return ret; } -bool MySQL_Query_Processor::_is_valid_gtid(char* gtid, size_t gtid_len) { +bool MySQL_Query_Processor::_is_valid_gtid(const char* gtid, size_t gtid_len) { if (gtid_len < 3) { return false; } - char* sep_pos = index(gtid, ':'); + const char* sep_pos = index(gtid, ':'); if (sep_pos == NULL) { return false; } diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index 04bf316e6a..4225d96266 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -1848,8 +1848,8 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { char *ecs = escape_string_single_quotes(cs, false); const char* safe_escaped = ecs ? ecs : ""; values += std::string("'") + safe_escaped + "'"; - if (cs != ecs) free(cs); - if (ecs) free(ecs); + if (cs != ecs) l_free(0, cs); + l_free(0, ecs); } }; @@ -2012,34 +2012,31 @@ int ProxySQL_Config::Write_Global_Variables_to_configfile(std::string& data) { if (error) { proxy_error("Error on read from global_variables : %s\n", error); return -1; - } else { - if (sqlite_resultset) { - std::string prefix; - - for (auto r : sqlite_resultset->rows) { - std::string input(r->fields[0]); - std::string p1 = input.substr(0, input.find("-")); - if (prefix.empty()) { - prefix = input.substr(0, input.find("-")); - data += prefix + "_variables =\n{\n"; - } else { - if (p1.compare(prefix)) { - prefix = p1; - data += "}\n\n" + prefix + "_variables = \n{\n"; - } - } - if (r->fields[1] && r->fields[1][0] != '\0') { - std::stringstream ss; - ss << "\t" << r->fields[0] + p1.size() + 1 << "=\"" << r->fields[1] << "\"\n"; - data += ss.str(); - } - } + } + if (sqlite_resultset == nullptr) + return 0; - if (!prefix.empty()) - data += "}\n"; + std::string prefix; + for (auto r : sqlite_resultset->rows) { + std::string input(r->fields[0]); + std::string p1 = input.substr(0, input.find("-")); + if (prefix.empty()) { + prefix = p1; + data += prefix + "_variables =\n{\n"; + } else if (p1.compare(prefix)) { + prefix = p1; + data += "}\n\n" + prefix + "_variables = \n{\n"; + } + if (r->fields[1] && r->fields[1][0] != '\0') { + std::stringstream ss; + ss << "\t" << r->fields[0] + p1.size() + 1 << "=\"" << r->fields[1] << "\"\n"; + data += ss.str(); } } + if (!prefix.empty()) + data += "}\n"; + if (sqlite_resultset) delete sqlite_resultset; diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 7d8c25d83f..dc8ff80445 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -277,6 +277,14 @@ class sqlite3server_main_loop_listeners { static sqlite3server_main_loop_listeners S_amll; +#if defined(TEST_READONLY) || defined(TEST_RDS_BGD) +static void ensure_readonly_table(SQLite3_Server *server, MySQL_Session *sess) { + if (server->readonly_map_size() == 0) { + server->load_readonly_table(sess); + } +} +#endif + #ifdef TEST_GROUPREP /** * @brief Helper function that checks if the supplied string @@ -1023,10 +1031,8 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p && query_no_space_length > k_select_read_only_len+5) { pthread_mutex_lock(&GloSQLite3Server->test_readonly_mutex); // the current test doesn't try to simulate failures, therefore it will return immediately - if (GloSQLite3Server->readonly_map_size() == 0) { - // probably never initialized - GloSQLite3Server->load_readonly_table(sess); - } + // Load the test table lazily on its first use. + ensure_readonly_table(GloSQLite3Server, sess); int rc = GloSQLite3Server->readonly_test_value(query_no_space+k_select_read_only_len); l_free(query_length, query); const std::string formatted_query = cstr_format("SELECT %d as read_only", rc).str; diff --git a/test/PrepStmt/client2.cpp b/test/PrepStmt/client2.cpp index 8a2eb18f06..443420d9c6 100644 --- a/test/PrepStmt/client2.cpp +++ b/test/PrepStmt/client2.cpp @@ -91,16 +91,16 @@ int main() { bl=strlen(buff); uint64_t hash=local_stmts->compute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); - if (a==NULL) { - if (mysql_stmt_prepare(stmt[i], buff, bl)) { - fprintf(stderr, " mysql_stmt_prepare(), failed: %s\n" , mysql_stmt_error(stmt[i])); - exit(EXIT_FAILURE); - } - uint32_t stmid=GloMyStmt->add_prepared_statement(0,(char *)USER,(char *)SCHEMA,buff,bl,stmt[i]); - if (NUMPRO < 32) - fprintf(stdout, "SERVER_statement_id=%lu , PROXY_statement_id=%u\n", stmt[i]->stmt_id, stmid); - local_stmts->insert(stmid,stmt[i]); - } + if (a != NULL) + continue; + if (mysql_stmt_prepare(stmt[i], buff, bl)) { + fprintf(stderr, " mysql_stmt_prepare(), failed: %s\n" , mysql_stmt_error(stmt[i])); + exit(EXIT_FAILURE); + } + uint32_t stmid=GloMyStmt->add_prepared_statement(0,(char *)USER,(char *)SCHEMA,buff,bl,stmt[i]); + if (NUMPRO < 32) + fprintf(stdout, "SERVER_statement_id=%lu , PROXY_statement_id=%u\n", stmt[i]->stmt_id, stmid); + local_stmts->insert(stmid,stmt[i]); } fprintf(stdout, "Prepared statements: %u client, %u proxy/server. ", NUMPREP, GloMyStmt->total_prepared_statements()); fprintf(stdout, "Created in: "); diff --git a/test/tap/tests/aurora.cpp b/test/tap/tests/aurora.cpp index 56460f7905..97a7b0dd0d 100644 --- a/test/tap/tests/aurora.cpp +++ b/test/tap/tests/aurora.cpp @@ -386,7 +386,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p c_split_2(query_no_space+strAl,".",&dbh,&tbh); if (std::string_view(tbh).empty()) { - free(tbh); + l_free(0, tbh); tbh=dbh; dbh=strdup("main"); } @@ -409,13 +409,13 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p + ".sqlite_master WHERE type='table' AND name='" + tbh + "'"; char *buff = l_strdup(table_query.c_str()); if (!buff) { - free(tbh); - free(dbh); + l_free(0, tbh); + l_free(0, dbh); l_free(query_length, query); return; } - free(tbh); - free(dbh); + l_free(0, tbh); + l_free(0, dbh); l_free(query_length,query); query=buff; query_length=table_query.size()+1; diff --git a/test/tap/tests/galera_1_timeout_count.cpp b/test/tap/tests/galera_1_timeout_count.cpp index b818c36723..96798c1366 100644 --- a/test/tap/tests/galera_1_timeout_count.cpp +++ b/test/tap/tests/galera_1_timeout_count.cpp @@ -23,12 +23,19 @@ #include #include #include +#include #include #include #include "tap.h" +static int random_replication_lag_seconds() { + static thread_local std::random_device random_source; + static thread_local std::uniform_int_distribution distribution(10, 39); + return distribution(random_source); +} + #define SELECT_VERSION_COMMENT "select @@version_comment limit 1" #define SELECT_VERSION_COMMENT_LEN 32 #define SELECT_DB_USER "select DATABASE(), USER() limit 1" @@ -196,7 +203,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p if (strstr(query_no_space,(char *)"Seconds_Behind_Master")) { l_free(0, query); const std::string formatted_query = cstr_format( - "SELECT %d as Seconds_Behind_Master", rand()%30+10 + "SELECT %d as Seconds_Behind_Master", random_replication_lag_seconds() ).str; query = l_strdup(formatted_query.c_str()); } diff --git a/test/tap/tests/galera_2_timeout_no_count.cpp b/test/tap/tests/galera_2_timeout_no_count.cpp index 76ad62b398..fad570a740 100644 --- a/test/tap/tests/galera_2_timeout_no_count.cpp +++ b/test/tap/tests/galera_2_timeout_no_count.cpp @@ -23,12 +23,19 @@ #include #include #include +#include #include #include #include "tap.h" +static int random_replication_lag_seconds() { + static thread_local std::random_device random_source; + static thread_local std::uniform_int_distribution distribution(10, 39); + return distribution(random_source); +} + #define SELECT_VERSION_COMMENT "select @@version_comment limit 1" #define SELECT_VERSION_COMMENT_LEN 32 #define SELECT_DB_USER "select DATABASE(), USER() limit 1" @@ -206,7 +213,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p if (strstr(query_no_space,(char *)"Seconds_Behind_Master")) { l_free(0, query); const std::string formatted_query = cstr_format( - "SELECT %d as Seconds_Behind_Master", rand()%30+10 + "SELECT %d as Seconds_Behind_Master", random_replication_lag_seconds() ).str; query = l_strdup(formatted_query.c_str()); } diff --git a/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp b/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp index 66af0652b6..1fcf3c5404 100644 --- a/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp +++ b/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp @@ -136,11 +136,11 @@ char* psprintf(const char* fmt, ...) { char* escape_str(MYSQL* mysql, const char* str) { if (!str) return strdup("NULL"); - char* escaped = (char*)malloc(2 * strlen(str) + 1); - mysql_real_escape_string(mysql, escaped, str, strlen(str)); - size_t len = strlen(escaped); - char* result = (char*)malloc(len + 3); - snprintf(result, len + 3, "'%s'", escaped); + const size_t input_len = strlen(str); + char* escaped = (char*)malloc(2 * input_len + 1); + const unsigned long escaped_len = mysql_real_escape_string(mysql, escaped, str, input_len); + char* result = (char*)malloc(escaped_len + 3); + snprintf(result, escaped_len + 3, "'%s'", escaped); free(escaped); return result; } From 79600e22f2017a8b2d2c8f01d8774db58fcdb9d1 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Mon, 10 Aug 2026 22:25:06 +0000 Subject: [PATCH 221/227] fix: remove final Sonar configuration and test findings Extract repeated configuration-field formatting into a bounded helper so the MySQL and PostgreSQL configuration readers no longer exceed Sonar's nesting threshold. Keep both escaped buffers on the ProxySQL allocator cleanup path and replace the test's raw strlen call with a std::string length before escaping. --- lib/ProxySQL_Config.cpp | 119 ++++++++---------- .../mysql-reg_test_4867_query_rules-t.cpp | 5 +- 2 files changed, 53 insertions(+), 71 deletions(-) diff --git a/lib/ProxySQL_Config.cpp b/lib/ProxySQL_Config.cpp index 4225d96266..054e12ca13 100644 --- a/lib/ProxySQL_Config.cpp +++ b/lib/ProxySQL_Config.cpp @@ -43,6 +43,32 @@ static void format_query(char *query, size_t query_len, const char *format, Args query[copy_len] = '\0'; } +static void append_config_field(std::string& fields, std::string& values, + bool& is_first_field, const std::string& field_name, + const std::string& field_value, bool is_int) { + if (!is_first_field) { + fields += ", "; + values += ", "; + } else { + is_first_field = false; + } + fields += field_name; + + if (is_int) { + values += field_value; + return; + } + + char *cs = strdup(field_value.c_str()); + char *ecs = escape_string_single_quotes(cs, false); + const char* safe_escaped = ecs ? ecs : ""; + values += std::string("'") + safe_escaped + "'"; + if (cs != ecs) { + l_free(0, cs); + } + l_free(0, ecs); +} + const char* config_header = "########################################################################################\n" "# This config file is parsed using libconfig , and its grammar is described in:\n" "# http://www.hyperrealm.com/libconfig/libconfig_manual.html#Configuration-File-Grammar\n" @@ -1830,73 +1856,50 @@ int ProxySQL_Config::Read_MySQL_Servers_from_configfile(std::string& error) { std::string fields = ""; std::string values = ""; - auto process_field = [&](const std::string &field_name, const std::string &field_value, int is_int) { - if (!is_first_field) { - fields += ", "; - values += ", "; - } - else { - is_first_field = false; - } - fields += field_name; - - if (is_int) { - values += field_value; - } - else { - char *cs = strdup(field_value.c_str()); - char *ecs = escape_string_single_quotes(cs, false); - const char* safe_escaped = ecs ? ecs : ""; - values += std::string("'") + safe_escaped + "'"; - if (cs != ecs) l_free(0, cs); - l_free(0, ecs); - } - }; - // Only inserting/updating fields which are in configuration file. // Fields default will be from table schema. // Parsing integer field if (hostgroup_attributes.lookupValue("hostgroup_id", integer_val) ) { - process_field("hostgroup_id", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "hostgroup_id", to_string(integer_val), true); } else { proxy_error("Admin: detected a mysql_hostgroup_attributes in config file without a mandatory hostgroup_id.\n"); continue; } if (hostgroup_attributes.lookupValue("max_num_online_servers", integer_val)) { - process_field("max_num_online_servers", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "max_num_online_servers", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("autocommit", integer_val)) { - process_field("autocommit", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "autocommit", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("free_connections_pct", integer_val)) { - process_field("free_connections_pct", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "free_connections_pct", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("multiplex", integer_val)) { - process_field("multiplex", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "multiplex", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("connection_warming", integer_val)) { - process_field("connection_warming", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "connection_warming", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("throttle_connections_per_sec", integer_val)) { - process_field("throttle_connections_per_sec", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "throttle_connections_per_sec", to_string(integer_val), true); } // Parsing string field if (hostgroup_attributes.lookupValue("init_connect", string_val)) { - process_field("init_connect", string_val, false); + append_config_field(fields, values, is_first_field, "init_connect", string_val, false); } if (hostgroup_attributes.lookupValue("ignore_session_variables", string_val)) { - process_field("ignore_session_variables", string_val, false); + append_config_field(fields, values, is_first_field, "ignore_session_variables", string_val, false); } if (hostgroup_attributes.lookupValue("hostgroup_settings", string_val)) { - process_field("hostgroup_settings", string_val, false); + append_config_field(fields, values, is_first_field, "hostgroup_settings", string_val, false); } if (hostgroup_attributes.lookupValue("servers_defaults", string_val)) { - process_field("servers_defaults", string_val, false); + append_config_field(fields, values, is_first_field, "servers_defaults", string_val, false); } if (hostgroup_attributes.lookupValue("comment", string_val)) { - process_field("comment", string_val, false); + append_config_field(fields, values, is_first_field, "comment", string_val, false); } std::string s_query = "INSERT OR REPLACE INTO mysql_hostgroup_attributes ("; @@ -2328,67 +2331,45 @@ int ProxySQL_Config::Read_PgSQL_Servers_from_configfile(std::string& error) { std::string fields = ""; std::string values = ""; - auto process_field = [&](const std::string &field_name, const std::string &field_value, int is_int) { - if (!is_first_field) { - fields += ", "; - values += ", "; - } - else { - is_first_field = false; - } - fields += field_name; - if (is_int) { - values += field_value; - } - else { - char *cs = strdup(field_value.c_str()); - char *ecs = escape_string_single_quotes(cs, false); - const char* safe_escaped = ecs ? ecs : ""; - values += std::string("'") + safe_escaped + "'"; - if (cs != ecs) free(cs); - if (ecs) free(ecs); - } - }; - if (hostgroup_attributes.lookupValue("hostgroup_id", integer_val)) { - process_field("hostgroup_id", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "hostgroup_id", to_string(integer_val), true); } else { proxy_error("Admin: detected a pgsql_hostgroup_attributes in config file without a mandatory hostgroup_id.\n"); continue; } if (hostgroup_attributes.lookupValue("max_num_online_servers", integer_val)) { - process_field("max_num_online_servers", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "max_num_online_servers", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("autocommit", integer_val)) { - process_field("autocommit", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "autocommit", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("free_connections_pct", integer_val)) { - process_field("free_connections_pct", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "free_connections_pct", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("multiplex", integer_val)) { - process_field("multiplex", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "multiplex", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("connection_warming", integer_val)) { - process_field("connection_warming", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "connection_warming", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("throttle_connections_per_sec", integer_val)) { - process_field("throttle_connections_per_sec", to_string(integer_val), true); + append_config_field(fields, values, is_first_field, "throttle_connections_per_sec", to_string(integer_val), true); } if (hostgroup_attributes.lookupValue("init_connect", string_val)) { - process_field("init_connect", string_val, false); + append_config_field(fields, values, is_first_field, "init_connect", string_val, false); } if (hostgroup_attributes.lookupValue("ignore_session_variables", string_val)) { - process_field("ignore_session_variables", string_val, false); + append_config_field(fields, values, is_first_field, "ignore_session_variables", string_val, false); } if (hostgroup_attributes.lookupValue("hostgroup_settings", string_val)) { - process_field("hostgroup_settings", string_val, false); + append_config_field(fields, values, is_first_field, "hostgroup_settings", string_val, false); } if (hostgroup_attributes.lookupValue("servers_defaults", string_val)) { - process_field("servers_defaults", string_val, false); + append_config_field(fields, values, is_first_field, "servers_defaults", string_val, false); } if (hostgroup_attributes.lookupValue("comment", string_val)) { - process_field("comment", string_val, false); + append_config_field(fields, values, is_first_field, "comment", string_val, false); } std::string s_query = "INSERT OR REPLACE INTO pgsql_hostgroup_attributes ("; diff --git a/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp b/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp index 1fcf3c5404..9251f4d991 100644 --- a/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp +++ b/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp @@ -136,9 +136,10 @@ char* psprintf(const char* fmt, ...) { char* escape_str(MYSQL* mysql, const char* str) { if (!str) return strdup("NULL"); - const size_t input_len = strlen(str); + const std::string input(str); + const size_t input_len = input.size(); char* escaped = (char*)malloc(2 * input_len + 1); - const unsigned long escaped_len = mysql_real_escape_string(mysql, escaped, str, input_len); + const unsigned long escaped_len = mysql_real_escape_string(mysql, escaped, input.c_str(), input_len); char* result = (char*)malloc(escaped_len + 3); snprintf(result, escaped_len + 3, "'%s'", escaped); free(escaped); From cc0f10a7f1118760c2ea1c4d55dd23f278bbf8f4 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Tue, 11 Aug 2026 05:42:07 +0000 Subject: [PATCH 222/227] fix: restore test-mode scope and allocator pairing Remove stale closing braces left after flattening the read-only and replication-lag checks, and use the selected prefix length for the replication boundary guard. Pair __cxa_demangle with free and retain the correct deallocator for SHOW CREATE TABLE buffers when the quoted-name path uses l_alloc. --- lib/Admin_Handler.cpp | 8 +++++++- lib/debug.cpp | 2 +- src/SQLite3_Server.cpp | 10 ++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index 5840ab319d..fe3d9a0db2 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -5240,6 +5240,7 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { strBl=strlen(strB); char *dbh=NULL; char *tbh=NULL; + bool tbh_uses_l_alloc = false; c_split_2(query_no_space+strAl,".",&dbh,&tbh); if (std::string_view(tbh).empty()) { @@ -5255,12 +5256,17 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { tbh_tmp[quoted_len]=0; free(tbh); tbh=tbh_tmp; + tbh_uses_l_alloc = true; } int l=strBl+strlen(tbh)*3+strlen(dbh)-8; char *buff=(char *)l_alloc(l+1); snprintf(buff,l+1,strB,tbh,tbh,dbh,tbh); buff[l]=0; - free(tbh); + if (tbh_uses_l_alloc) { + l_free(tbh_len-1,tbh); + } else { + free(tbh); + } free(dbh); l_free(query_length,query); query=buff; diff --git a/lib/debug.cpp b/lib/debug.cpp index 54f0898efe..1161d56ba5 100644 --- a/lib/debug.cpp +++ b/lib/debug.cpp @@ -253,7 +253,7 @@ extern "C" void proxy_debug_func( realname ); } - l_free(0, realname); + free(realname); } free(strings); } diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index dc8ff80445..9d6d7d4dff 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -1039,17 +1039,16 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p query = l_strdup(formatted_query.c_str()); query_length = formatted_query.size() + 1; pthread_mutex_unlock(&GloSQLite3Server->test_readonly_mutex); - } } #endif // TEST_READONLY || TEST_RDS_BGD #ifdef TEST_REPLICATIONLAG const bool replica_status = strncasecmp("SELECT REPLICA STATUS ", query_no_space, k_select_replica_status_len) == 0; + const uint64_t addr_offset { + replica_status ? k_select_replica_status_len : k_select_slave_status_len + }; if ((strncasecmp("SELECT SLAVE STATUS ", query_no_space, k_select_slave_status_len) == 0 || replica_status) - && query_no_space_length > k_select_slave_status_len + 5) { - uint64_t addr_offset { - replica_status ? k_select_replica_status_len : k_select_slave_status_len - }; + && query_no_space_length > addr_offset + 5) { pthread_mutex_lock(&GloSQLite3Server->test_replicationlag_mutex); // the current test doesn't try to simulate failures, therefore it will return immediately if (GloSQLite3Server->replicationlag_map_size() == 0) { @@ -1067,7 +1066,6 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p pthread_mutex_unlock(&GloSQLite3Server->test_replicationlag_mutex); } - } #endif // TEST_REPLICATIONLAG if (strstr(query_no_space,(char *)"Seconds_Behind_Master")) { l_free(query_length, query); From 792e675dea2b2f82bc00ca2ab627c07f8b0ff390 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Tue, 11 Aug 2026 05:47:38 +0000 Subject: [PATCH 223/227] fix: avoid malloc in query rules test escaping Build escaped SQL values with std::string storage instead of allocating temporary and result buffers with malloc. Keep the helper return type unchanged so existing callers continue releasing the duplicated result with free. --- .../mysql-reg_test_4867_query_rules-t.cpp | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp b/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp index 9251f4d991..9e7ecce4e5 100644 --- a/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp +++ b/test/tap/tests/mysql-reg_test_4867_query_rules-t.cpp @@ -135,15 +135,17 @@ char* psprintf(const char* fmt, ...) { } char* escape_str(MYSQL* mysql, const char* str) { - if (!str) return strdup("NULL"); - const std::string input(str); - const size_t input_len = input.size(); - char* escaped = (char*)malloc(2 * input_len + 1); - const unsigned long escaped_len = mysql_real_escape_string(mysql, escaped, input.c_str(), input_len); - char* result = (char*)malloc(escaped_len + 3); - snprintf(result, escaped_len + 3, "'%s'", escaped); - free(escaped); - return result; + if (!str) return strdup("NULL"); + const std::string input(str); + const size_t input_len = input.size(); + std::string escaped(2 * input_len + 1, '\0'); + const unsigned long escaped_len = mysql_real_escape_string(mysql, escaped.data(), input.c_str(), input_len); + std::string result; + result.reserve(escaped_len + 2); + result.push_back('\''); + result.append(escaped.data(), escaped_len); + result.push_back('\''); + return strdup(result.c_str()); } // Build INSERT query for a rule From 7d7b365cd6a3daab62dbc69d464be1bc5832f1c7 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Tue, 11 Aug 2026 05:53:08 +0000 Subject: [PATCH 224/227] fix: isolate replication lag table initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the lazy replication-lag table check into a dedicated helper so the all-test-mode query handler stays within SonarCloud’s nesting limit. Preserve the existing mutex-protected initialization and selected-prefix lookup behavior. --- src/SQLite3_Server.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 9d6d7d4dff..d2e7d3414f 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -47,6 +47,14 @@ static int random_replication_lag_seconds() { } #endif +#ifdef TEST_REPLICATIONLAG +static void ensure_replicationlag_table_loaded(SQLite3_Server* server, MySQL_Session* sess) { + if (server->replicationlag_map_size() == 0) { + server->load_replicationlag_table(sess); + } +} +#endif + #define SELECT_VERSION_COMMENT "select @@version_comment limit 1" #define SELECT_VERSION_COMMENT_LEN 32 #define SELECT_DB_USER "select DATABASE(), USER() limit 1" @@ -1051,10 +1059,7 @@ void SQLite3_Server_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *p && query_no_space_length > addr_offset + 5) { pthread_mutex_lock(&GloSQLite3Server->test_replicationlag_mutex); // the current test doesn't try to simulate failures, therefore it will return immediately - if (GloSQLite3Server->replicationlag_map_size() == 0) { - // probably never initialized - GloSQLite3Server->load_replicationlag_table(sess); - } + ensure_replicationlag_table_loaded(GloSQLite3Server, sess); const int* rc = GloSQLite3Server->replicationlag_test_value(query_no_space + addr_offset); l_free(query_length, query); From d530809aa9373eb17a9ccec9edb3e1dab4452aca Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Tue, 11 Aug 2026 05:53:08 +0000 Subject: [PATCH 225/227] docs: explain demangler allocator exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document why the demangled symbol buffer is released with free: abi::__cxa_demangle allocates it through malloc, so using ProxySQL’s sized allocator would be an invalid deallocation. --- lib/debug.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/debug.cpp b/lib/debug.cpp index 1161d56ba5..af6ea00f44 100644 --- a/lib/debug.cpp +++ b/lib/debug.cpp @@ -253,7 +253,7 @@ extern "C" void proxy_debug_func( realname ); } - free(realname); + free(realname); // NOSONAR: __cxa_demangle returns memory allocated by malloc. } free(strings); } From 307bac228e59ea442ce39b743b27c41cad93b66e Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Tue, 11 Aug 2026 06:57:54 +0000 Subject: [PATCH 226/227] ci: retrigger full PR validation Create a no-op commit so the complete CI matrix runs again for PR #6026 after the self-hosted runner workspace was repaired. From af250ba86ea2c3e8f553226dbdc0ada96f98f610 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Tue, 11 Aug 2026 10:59:59 +0000 Subject: [PATCH 227/227] fix(pgsql): restore connection identity hash updates Store the computed PostgreSQL user/database hash after the string-based hash assembly refactor. Without this assignment every connection retained hash zero, allowing the pool to reuse a backend authenticated for another user or database. Extend the PostgreSQL SET-parameter TAP test to seed an unprivileged connection before a privileged one and verify that both retain their expected backend identities. --- lib/PgSQL_Connection.cpp | 3 +- .../pgsql-set_parameter_validation_test-t.cpp | 45 +++++++++++++++---- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lib/PgSQL_Connection.cpp b/lib/PgSQL_Connection.cpp index 0a6f13fd2c..8a1d617741 100644 --- a/lib/PgSQL_Connection.cpp +++ b/lib/PgSQL_Connection.cpp @@ -72,7 +72,8 @@ uint64_t PgSQL_Connection_userinfo::compute_hash() { hash_input.append(dbname, dbname_len); } hash_input.append(delimiter2); - return SpookyHash::Hash64(hash_input.data(), hash_input.size(), 0); + hash = SpookyHash::Hash64(hash_input.data(), hash_input.size(), 0); + return hash; } void PgSQL_Connection_userinfo::set(char *user, char *pass, char *db, char *sh1) { diff --git a/test/tap/tests/pgsql-set_parameter_validation_test-t.cpp b/test/tap/tests/pgsql-set_parameter_validation_test-t.cpp index 71e5623caf..0d3935416f 100644 --- a/test/tap/tests/pgsql-set_parameter_validation_test-t.cpp +++ b/test/tap/tests/pgsql-set_parameter_validation_test-t.cpp @@ -19,15 +19,19 @@ using PGConnPtr = std::unique_ptr; enum ConnType { ADMIN, - BACKEND + BACKEND, + CLIENT }; PGConnPtr createNewConnection(ConnType conn_type, const std::string& options = "", bool with_ssl = false) { - - const char* host = (conn_type == BACKEND) ? cl.pgsql_host : cl.pgsql_admin_host; - int port = (conn_type == BACKEND) ? cl.pgsql_port : cl.pgsql_admin_port; - const char* username = (conn_type == BACKEND) ? cl.pgsql_root_username : cl.admin_username; - const char* password = (conn_type == BACKEND) ? cl.pgsql_root_password : cl.admin_password; + const char* host = (conn_type == ADMIN) ? cl.pgsql_admin_host : cl.pgsql_host; + int port = (conn_type == ADMIN) ? cl.pgsql_admin_port : cl.pgsql_port; + const char* username = (conn_type == ADMIN) ? cl.admin_username : + (conn_type == BACKEND ? cl.pgsql_root_username : cl.pgsql_username); + const char* password = (conn_type == ADMIN) ? cl.admin_password : + (conn_type == BACKEND ? cl.pgsql_root_password : cl.pgsql_password); + const char* connection_name = (conn_type == ADMIN) ? "Admin" : + (conn_type == BACKEND ? "Backend" : "Client"); std::stringstream ss; @@ -41,13 +45,23 @@ PGConnPtr createNewConnection(ConnType conn_type, const std::string& options = " PGconn* conn = PQconnectdb(ss.str().c_str()); if (PQstatus(conn) != CONNECTION_OK) { - fprintf(stderr, "Connection failed to '%s': %s", (conn_type == BACKEND ? "Backend" : "Admin"), PQerrorMessage(conn)); + fprintf(stderr, "Connection failed to '%s': %s", connection_name, PQerrorMessage(conn)); PQfinish(conn); return PGConnPtr(nullptr, &PQfinish); } return PGConnPtr(conn, &PQfinish); } +std::string queryCurrentUser(PGconn* conn) { + PGresult* res = PQexec(conn, "SELECT current_user"); + std::string username; + if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) { + username = PQgetvalue(res, 0, 0); + } + PQclear(res); + return username; +} + struct SetCommandTest { std::string command; bool expect_success; @@ -382,7 +396,7 @@ static int query_pgsql_set_parser_algorithm() { } int main(int argc, char** argv) { - int total_tests = 0; + int total_tests = 2; for (const auto& test_case : test_cases) { total_tests += test_case.commands.size(); @@ -397,11 +411,26 @@ int main(int argc, char** argv) { diag("pgsql-set_parser_algorithm = %d (per-case algo3 override %s)", parser_algorithm, parser_algorithm == 3 ? "ACTIVE" : "ignored"); + PGConnPtr client_conn = createNewConnection(ConnType::CLIENT, "", false); + if (!client_conn || PQstatus(client_conn.get()) != CONNECTION_OK) { + BAIL_OUT("Error: failed to create an unprivileged PostgreSQL connection"); + return exit_status(); + } + const std::string client_user = queryCurrentUser(client_conn.get()); + ok(client_user == cl.pgsql_username, + "unprivileged frontend retains its backend identity (expected '%s', got '%s')", + cl.pgsql_username, client_user.c_str()); + client_conn.reset(); + PGConnPtr conn = createNewConnection(ConnType::BACKEND, "", false); if (!conn || PQstatus(conn.get()) != CONNECTION_OK) { BAIL_OUT("Error: failed to connect to the database in file %s, line %d", __FILE__, __LINE__); return exit_status(); } + const std::string backend_user = queryCurrentUser(conn.get()); + ok(backend_user == cl.pgsql_root_username, + "privileged frontend does not reuse the unprivileged backend identity (expected '%s', got '%s')", + cl.pgsql_root_username, backend_user.c_str()); for (const auto& test_case : test_cases) { for (const auto& cmd_test : test_case.commands) {