Fix COM_STMT_SEND_LONG_DATA short packet handling

infra-mysql57-binlog
Rene Cannao 1 month ago committed by René Cannaò
parent 63007cfa39
commit 151169da9b

@ -9448,10 +9448,19 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C
void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_SEND_LONG_DATA(PtrSize_t& pkt) {
// FIXME: no input validation
if (pkt.size < 11) {
proxy_warning(
"Received malformed COM_STMT_SEND_LONG_DATA packet of %lu bytes\n",
static_cast<unsigned long>(pkt.size)
);
client_myds->DSS=STATE_SLEEP;
status=WAITING_CLIENT_DATA;
l_free(pkt.size,pkt.ptr);
return;
}
uint32_t stmt_global_id=0;
memcpy(&stmt_global_id,(char *)pkt.ptr+5,sizeof(uint32_t));
uint32_t stmt_param_id=0;
uint16_t stmt_param_id=0;
memcpy(&stmt_param_id,(char *)pkt.ptr+9,sizeof(uint16_t));
SLDH->add(stmt_global_id,stmt_param_id,(char *)pkt.ptr+11,pkt.size-11);
client_myds->DSS=STATE_SLEEP;

@ -190,6 +190,7 @@
"reg_test_3546-stmt_empty_params-t" : [ "legacy-g1","mysql84-g1","mysql-auto_increment_delay_multiplex=0-g1","mysql-multiplexing=false-g1","mysql-query_digests=0-g1","mysql-query_digests_keep_comment=1-g1" ],
"reg_test_3549-autocommit_tracking-t" : [ "legacy-g1","mysql84-g1","mysql-auto_increment_delay_multiplex=0-g1","mysql-multiplexing=false-g1","mysql-query_digests=0-g1","mysql-query_digests_keep_comment=1-g1" ],
"reg_test_3585-stmt_metadata-t" : [ "legacy-g1","mysql84-g1","mysql-auto_increment_delay_multiplex=0-g1","mysql-multiplexing=false-g1","mysql-query_digests=0-g1","mysql-query_digests_keep_comment=1-g1" ],
"reg_test_stmt_send_long_data_short_packet-t" : [ "mysql84-g1" ],
"reg_test_3591-restapi_num_fds-t" : [ "legacy-g1","mysql84-g1","mysql-auto_increment_delay_multiplex=0-g1","mysql-multiplexing=false-g1","mysql-query_digests=0-g1","mysql-query_digests_keep_comment=1-g1" ],
"reg_test_3603-stmt_metadata-t" : [ "legacy-g1","mysql84-g1","mysql-auto_increment_delay_multiplex=0-g1","mysql-multiplexing=false-g1","mysql-query_digests=0-g1","mysql-query_digests_keep_comment=1-g1" ],
"reg_test_3606-mysql_warnings-t" : [ "legacy-g1","mysql84-g1","mysql-auto_increment_delay_multiplex=0-g1","mysql-multiplexing=false-g1","mysql-query_digests=0-g1","mysql-query_digests_keep_comment=1-g1" ],

@ -0,0 +1,162 @@
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <sys/socket.h>
#include <unistd.h>
#include "mysql.h"
#include "tap.h"
#include "command_line.h"
namespace {
constexpr unsigned char kComStmtSendLongData = 0x18;
constexpr unsigned char kMalformedStmtSendLongDataPacket[] = {
0x01, 0x00, 0x00, 0x00,
kComStmtSendLongData
};
MYSQL* connect_proxy(const CommandLine& cl) {
MYSQL* mysql = mysql_init(nullptr);
if (mysql == nullptr) {
return nullptr;
}
if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, nullptr, cl.port, nullptr, 0)) {
mysql_close(mysql);
return nullptr;
}
return mysql;
}
bool query_returns_one(MYSQL* mysql) {
if (mysql_query(mysql, "SELECT 1")) {
return false;
}
MYSQL_RES* result = mysql_store_result(mysql);
if (result == nullptr) {
return false;
}
MYSQL_ROW row = mysql_fetch_row(result);
const bool ok = row != nullptr && row[0] != nullptr && strcmp(row[0], "1") == 0;
mysql_free_result(result);
return ok;
}
bool prepared_select_returns_42(MYSQL* mysql) {
MYSQL_STMT* stmt = mysql_stmt_init(mysql);
if (stmt == nullptr) {
return false;
}
const char query[] = "SELECT 42";
if (mysql_stmt_prepare(stmt, query, sizeof(query) - 1)) {
mysql_stmt_close(stmt);
return false;
}
if (mysql_stmt_execute(stmt)) {
mysql_stmt_close(stmt);
return false;
}
int value = 0;
unsigned long value_len = 0;
my_bool is_null = 0;
MYSQL_BIND bind {};
bind.buffer_type = MYSQL_TYPE_LONG;
bind.buffer = &value;
bind.buffer_length = sizeof(value);
bind.length = &value_len;
bind.is_null = &is_null;
if (mysql_stmt_bind_result(stmt, &bind)) {
mysql_stmt_close(stmt);
return false;
}
if (mysql_stmt_store_result(stmt)) {
mysql_stmt_close(stmt);
return false;
}
const int fetch_rc = mysql_stmt_fetch(stmt);
const bool ok = fetch_rc == 0 && is_null == 0 && value == 42;
mysql_stmt_free_result(stmt);
mysql_stmt_close(stmt);
return ok;
}
bool send_all(int fd, const unsigned char* data, size_t len) {
size_t total_sent = 0;
while (total_sent < len) {
const ssize_t sent = send(fd, data + total_sent, len - total_sent, MSG_NOSIGNAL);
if (sent <= 0) {
return false;
}
total_sent += sent;
}
return true;
}
bool send_malformed_stmt_send_long_data(MYSQL* mysql) {
return send_all(mysql->net.fd, kMalformedStmtSendLongDataPacket, sizeof(kMalformedStmtSendLongDataPacket));
}
} // namespace
int main(int argc, char** argv) {
CommandLine cl;
if (cl.getEnv()) {
diag("Failed to get the required environmental variables.");
return EXIT_FAILURE;
}
plan(6);
MYSQL* attack = connect_proxy(cl);
ok(attack != nullptr, "Authenticated frontend connection succeeds");
if (attack == nullptr) {
skip(5, "Cannot continue without an authenticated frontend connection");
return exit_status();
}
ok(query_returns_one(attack), "Baseline query succeeds before malformed COM_STMT_SEND_LONG_DATA");
ok(
send_malformed_stmt_send_long_data(attack),
"Malformed COM_STMT_SEND_LONG_DATA packet is written to the live connection"
);
usleep(200000);
ok(
prepared_select_returns_42(attack),
"Prepared statements still execute after malformed COM_STMT_SEND_LONG_DATA"
);
ok(
query_returns_one(attack),
"Same connection still serves queries after malformed COM_STMT_SEND_LONG_DATA"
);
MYSQL* fresh = connect_proxy(cl);
ok(fresh != nullptr && query_returns_one(fresh), "Fresh connection still succeeds after malformed COM_STMT_SEND_LONG_DATA");
if (fresh != nullptr) {
mysql_close(fresh);
}
mysql_close(attack);
return exit_status();
}
Loading…
Cancel
Save