1
0
mirror of https://github.com/mariadb-corporation/mariadb-connector-c.git synced 2025-08-08 14:02:17 +03:00

Fix for CONC-71: mysql_real_query crashes after server restart

- We now check socket status before net_flush (and return error if the socket is dead)
This commit is contained in:
holzboote@googlemail.com
2014-02-04 20:09:23 +01:00
parent 8c0a7fff46
commit db14156eb7
7 changed files with 109 additions and 41 deletions

View File

@@ -21,12 +21,16 @@ IF(NOT WIN32)
OPTION(WITH_OPENSSL "enables SSL support" ON) OPTION(WITH_OPENSSL "enables SSL support" ON)
ELSE() ELSE()
OPTION(WITH_OPENSSL "enables SSL support" OFF) OPTION(WITH_OPENSSL "enables SSL support" OFF)
OPTION(WITH_RTC "enables run time checks for debug builds" OFF)
ENDIF() ENDIF()
OPTION(WITH_SQLITE "Enables Sqlite support" OFF) OPTION(WITH_SQLITE "Enables Sqlite support" OFF)
OPTION(WITH_EXTERNAL_ZLIB "Enables use of external zlib" OFF) OPTION(WITH_EXTERNAL_ZLIB "Enables use of external zlib" OFF)
############### ###############
IF(WITH_RTC)
SET(RTC_OPTIONS "/RTC1 /RTCc")
ENDIF()
IF(MSVC) IF(MSVC)
# Speedup system tests # Speedup system tests
@@ -39,7 +43,7 @@ IF(MSVC)
IF (NOT COMPILER_FLAGS STREQUAL "") IF (NOT COMPILER_FLAGS STREQUAL "")
STRING(REPLACE "/MD" "/MT" COMPILER_FLAGS ${COMPILER_FLAGS}) STRING(REPLACE "/MD" "/MT" COMPILER_FLAGS ${COMPILER_FLAGS})
IF (CMAKE_BUILD_TYPE STREQUAL "Debug") IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
SET(COMPILER_FLAGS "${COMPILER_FLAGS} /RTC1 /RTCc") SET(COMPILER_FLAGS "${COMPILER_FLAGS} ${RTC_OPTIONS}")
STRING(REPLACE "/Zi" "/ZI" COMPILER_FLAGS ${COMPILER_FLAGS}) STRING(REPLACE "/Zi" "/ZI" COMPILER_FLAGS ${COMPILER_FLAGS})
ENDIF() ENDIF()
MESSAGE (STATUS "CMAKE_${COMPILER}_FLAGS_${BUILD_TYPE}= ${COMPILER_FLAGS}") MESSAGE (STATUS "CMAKE_${COMPILER}_FLAGS_${BUILD_TYPE}= ${COMPILER_FLAGS}")

View File

@@ -127,7 +127,6 @@ ELSE()
../zlib/compress.c ../zlib/compress.c
../zlib/crc32.c ../zlib/crc32.c
../zlib/deflate.c ../zlib/deflate.c
../zlib/example.c
../zlib/gzclose.c ../zlib/gzclose.c
../zlib/gzlib.c ../zlib/gzlib.c
../zlib/gzread.c ../zlib/gzread.c

View File

@@ -31,8 +31,8 @@
#include <signal.h> #include <signal.h>
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#ifdef MYSQL_SERVER #ifndef _WIN32
#include <violite.h> #include <poll.h>
#endif #endif
#define MAX_PACKET_LENGTH (256L*256L*256L-1) #define MAX_PACKET_LENGTH (256L*256L*256L-1)
@@ -114,8 +114,6 @@ int my_net_init(NET *net, Vio* vio)
{ {
if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME | MY_ZEROFILL)))) if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME | MY_ZEROFILL))))
return 1; return 1;
// if (net_buffer_length > max_allowed_packet)
// max_allowed_packet=net_buffer_length;
max_allowed_packet= net->max_packet_size= MAX(net_buffer_length, max_allowed_packet); max_allowed_packet= net->max_packet_size= MAX(net_buffer_length, max_allowed_packet);
net->buff_end=net->buff+(net->max_packet=net_buffer_length); net->buff_end=net->buff+(net->max_packet=net_buffer_length);
net->vio = vio; net->vio = vio;
@@ -166,13 +164,10 @@ static my_bool net_realloc(NET *net, size_t length)
DBUG_RETURN(1); DBUG_RETURN(1);
} }
pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length, MYF(MY_WME)))) if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length + 1, MYF(MY_WME))))
{ {
DBUG_PRINT("info", ("Out of memory")); DBUG_PRINT("info", ("Out of memory"));
net->error=1; net->error=1;
#ifdef MYSQL_SERVER
net->last_errno=ER_OUT_OF_RESOURCES;
#endif
DBUG_RETURN(1); DBUG_RETURN(1);
} }
net->buff=net->write_pos=buff; net->buff=net->write_pos=buff;
@@ -180,21 +175,45 @@ static my_bool net_realloc(NET *net, size_t length)
DBUG_RETURN(0); DBUG_RETURN(0);
} }
#ifdef DEBUG_SOCKET
static ssize_t net_check_if_data_available(Vio *vio) /* check if the socket is still alive */
static my_bool net_check_socket_status(my_socket sock)
{ {
ssize_t length= 0; #ifndef _WIN32
struct pollfd poll_fd;
if (vio->type != VIO_TYPE_SOCKET && #else
vio->type != VIO_TYPE_TCPIP) FD_SET sfds;
return 0; struct timeval tv= {0,0};
if (vio_read_peek(vio, (size_t *)&length))
return -1;
return length;
}
#endif #endif
int res;
#ifndef _WIN32
memset(&poll_fd, 0, sizeof(struct pollfd));
poll_fd.events= POLLPRI | POLLIN;
poll_fd.fd= sock;
res= poll(&poll_fd, 1, 0);
if (res <= 0) /* timeout or error */
return FALSE;
if (!(poll_fd.revents & (POLLIN | POLLPRI)))
return FALSE;
return TRUE;
#else
/* We can't use the WSAPoll function, it's broken :-(
(see Windows 8 Bugs 309411 - WSAPoll does not report failed connections)
Instead we need to use select function:
If TIMEVAL is initialized to {0, 0}, select will return immediately;
this is used to poll the state of the selected sockets.
*/
FD_ZERO(&sfds);
FD_SET(sock, &sfds);
res= select(sock + 1, &sfds, NULL, NULL, &tv);
if (res > 0 && FD_ISSET(sock, &sfds))
return TRUE;
return FALSE;
#endif
}
/* Remove unwanted characters from connection */ /* Remove unwanted characters from connection */
@@ -202,10 +221,19 @@ void net_clear(NET *net)
{ {
DBUG_ENTER("net_clear"); DBUG_ENTER("net_clear");
#ifdef DEBUG_SOCKET /* see conc-71: we need to check the socket status first:
DBUG_ASSERT(net_check_if_data_available(net->vio) < 2); if the socket is dead we set net->error, so net_flush
#endif will report an error */
while (net_check_socket_status(net->vio->sd))
{
/* vio_read returns size_t. so casting to long is required to check for -1 */
if ((long)vio_read(net->vio, (gptr)net->buff, (size_t) net->max_packet) <= 0)
{
net->error= 2;
DBUG_PRINT("info", ("socket disconnected"));
DBUG_VOID_RETURN;
}
}
net->compress_pkt_nr= net->pkt_nr=0; /* Ready for new command */ net->compress_pkt_nr= net->pkt_nr=0; /* Ready for new command */
net->write_pos=net->buff; net->write_pos=net->buff;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
@@ -810,13 +838,14 @@ ulong my_net_read(NET *net)
net->where_b=buffer_length; net->where_b=buffer_length;
if ((packet_length = my_real_read(net,(size_t *)&complen)) == packet_error) if ((packet_length = my_real_read(net,(size_t *)&complen)) == packet_error)
break; return packet_error;
if (my_uncompress((unsigned char*) net->buff + net->where_b, &packet_length, &complen)) if (my_uncompress((unsigned char*) net->buff + net->where_b, &packet_length, &complen))
{ {
len= packet_error; len= packet_error;
net->error=2; /* caller will close socket */ net->error=2; /* caller will close socket */
net->last_errno=ER_NET_UNCOMPRESS_ERROR; net->last_errno=ER_NET_UNCOMPRESS_ERROR;
break; break;
return packet_error;
} }
buffer_length+= complen; buffer_length+= complen;
} }
@@ -827,12 +856,9 @@ ulong my_net_read(NET *net)
len= current - start - 4; len= current - start - 4;
if (is_multi_packet) if (is_multi_packet)
len-= 4; len-= 4;
if (len != packet_error)
{
net->save_char= net->read_pos[len]; /* Must be saved */ net->save_char= net->read_pos[len]; /* Must be saved */
net->read_pos[len]=0; /* Safeguard for mysql_use_result */ net->read_pos[len]=0; /* Safeguard for mysql_use_result */
} }
}
#endif #endif
return (ulong)len; return (ulong)len;
} }

View File

@@ -30,6 +30,38 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "my_test.h" #include "my_test.h"
#include "ma_common.h" #include "ma_common.h"
static int test_conc71(MYSQL *my)
{
int rc;
MYSQL_RES *res;
MYSQL_ROW row;
char *query;
MYSQL *mysql;
/* uncomment if you want to test manually */
return SKIP;
mysql= mysql_init(NULL);
mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "utf8");
mysql_options(mysql, MYSQL_OPT_COMPRESS, 0);
mysql_options(mysql, MYSQL_INIT_COMMAND, "/*!40101 SET SQL_MODE='' */");
mysql_options(mysql, MYSQL_INIT_COMMAND, "/*!40101 set @@session.wait_timeout=28800 */");
FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema,
port, socketname, 0), mysql_error(my));
diag("kill server");
sleep(20);
rc= mysql_query(mysql, "SELECT 'foo' FROM DUAL");
check_mysql_rc(rc, mysql);
mysql_close(mysql);
return OK;
}
static int test_conc70(MYSQL *my) static int test_conc70(MYSQL *my)
{ {
int rc; int rc;
@@ -37,7 +69,7 @@ static int test_conc70(MYSQL *my)
MYSQL_ROW row; MYSQL_ROW row;
MYSQL *mysql= mysql_init(NULL); MYSQL *mysql= mysql_init(NULL);
mysql_query(my, "SET @a:=@@max_allowed_packet"); rc= mysql_query(my, "SET @a:=@@max_allowed_packet");
check_mysql_rc(rc, my); check_mysql_rc(rc, my);
mysql_query(my, "SET global max_allowed_packet=1024*1024*22"); mysql_query(my, "SET global max_allowed_packet=1024*1024*22");
@@ -84,7 +116,7 @@ static int test_conc68(MYSQL *my)
MYSQL_ROW row; MYSQL_ROW row;
MYSQL *mysql= mysql_init(NULL); MYSQL *mysql= mysql_init(NULL);
mysql_query(my, "SET @a:=@@max_allowed_packet"); rc= mysql_query(my, "SET @a:=@@max_allowed_packet");
check_mysql_rc(rc, my); check_mysql_rc(rc, my);
mysql_query(my, "SET global max_allowed_packet=1024*1024*22"); mysql_query(my, "SET global max_allowed_packet=1024*1024*22");
@@ -648,6 +680,7 @@ static int test_compressed(MYSQL *my)
} }
struct my_tests_st my_tests[] = { struct my_tests_st my_tests[] = {
{"test_conc71", test_conc71, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"test_conc70", test_conc70, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc70", test_conc70, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"test_conc68", test_conc68, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc68", test_conc68, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"test_compressed", test_compressed, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_compressed", test_compressed, TEST_CONNECTION_NONE, 0, NULL, NULL},

View File

@@ -65,6 +65,9 @@ static int test_logs(MYSQL *mysql)
int rc; int rc;
short id; short id;
rc= mysql_query(mysql, "SET session sql_mode=''");
check_mysql_rc(rc, mysql);
rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_logs"); rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_logs");
check_mysql_rc(rc, mysql); check_mysql_rc(rc, mysql);

View File

@@ -0,0 +1,3 @@
[conc-66]
user=conc66
password='test;#test'

View File

@@ -1,12 +1,12 @@
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/zlib) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/zlib)
SET(SOURCE_FILES adler32.c compress.c crc32.c deflate.c example.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c minigzip.c trees.c uncompr.c zutil.c) SET(SOURCE_FILES adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c minigzip.c trees.c uncompr.c zutil.c)
ADD_LIBRARY(zlib ${SOURCE_FILES}) ADD_LIBRARY(zlib ${SOURCE_FILES})
SET_TARGET_PROPERTIES(zlib PROPERTIES COMPILE_FLAGS SET_TARGET_PROPERTIES(zlib PROPERTIES COMPILE_FLAGS
"${CMAKE_SHARED_LIBRARY_C_FLAGS}") "${CMAKE_SHARED_LIBRARY_C_FLAGS}")
INSTALL(TARGETS INSTALL(TARGETS
zlib zlib
RUNTIME DESTINATION "lib" RUNTIME DESTINATION "lib"
LIBRARY DESTINATION "lib" LIBRARY DESTINATION "lib"