Skip to content

Commit 4797ea0

Browse files
author
Nisha Gopalakrishnan
committed
BUG#17512527: LIST HANDLING INCORRECT IN MYSQL_PRUNE_STMT_LIST()
Analysis: --------- Invalid memory access maybe observed when using prepared statements if: a) The mysql client connection is lost after statement preparation is complete and b) There is at least one statement which is in initialized state but not prepared yet. When the client detects a closed connection, it calls end_server() to shutdown the connection. As part of the clean up, the mysql_prune_stmt_list() removes the statements which has transitioned beyond the initialized state and retains only the statements which are in a initialized state. During this processing, the initialized statements are moved from 'mysql->stmts' to a temporary 'pruned_list'. When moving the first 'INIT_DONE' element to the pruned_list, 'element->next' is set to NULL. Hence the rest of the list is never traversed and the statements which have transitioned beyond the initialized state are never invalidated. When the mysql_stmt_close() is called for the statement which is not invalidated; the statements list is updated in order to remove the statement. This would end up accessing freed memory(freed by the mysql_stmt_close() for a previous statement in the list). Fix: --- mysql_prune_stmt_list() called list_add() incorrectly to create a temporary list. The use case of list_add() is to add a single element to the front of the doubly linked list. mysql_prune_stmt_list() called list_add() by passing an entire list as the 'element'. mysql_prune_stmt_list() now uses list_delete() to remove the statement which has transitioned beyond the initialized phase. Thus the statement list would contain only elements where the the state of the statement is initialized. Note: Run the test with valgrind-mysqltest and leak-check=full option to see the invalid memory access.
1 parent f6f0d51 commit 4797ea0

2 files changed

Lines changed: 55 additions & 6 deletions

File tree

sql-common/client.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -3985,12 +3985,15 @@ static void mysql_close_free(MYSQL *mysql)
39853985
*/
39863986
static void mysql_prune_stmt_list(MYSQL *mysql)
39873987
{
3988-
LIST *element= mysql->stmts;
3989-
LIST *pruned_list= 0;
3988+
LIST *pruned_list= NULL;
39903989

3991-
for (; element; element= element->next)
3990+
while(mysql->stmts)
39923991
{
3993-
MYSQL_STMT *stmt= (MYSQL_STMT *) element->data;
3992+
LIST *element= mysql->stmts;
3993+
MYSQL_STMT *stmt;
3994+
3995+
mysql->stmts= list_delete(element, element);
3996+
stmt= (MYSQL_STMT *) element->data;
39943997
if (stmt->state != MYSQL_STMT_INIT_DONE)
39953998
{
39963999
stmt->mysql= 0;

tests/mysql_client_test.c

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
22

33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -19353,6 +19353,51 @@ static void test_bug17309863()
1935319353
}
1935419354
#endif
1935519355

19356+
19357+
/**
19358+
BUG#17512527: LIST HANDLING INCORRECT IN MYSQL_PRUNE_STMT_LIST()
19359+
*/
19360+
static void test_bug17512527()
19361+
{
19362+
MYSQL *conn1, *conn2;
19363+
MYSQL_STMT *stmt1, *stmt2;
19364+
const char *stmt1_txt= "SELECT NOW();";
19365+
const char *stmt2_txt= "SELECT 1;";
19366+
unsigned long thread_id;
19367+
char query[MAX_TEST_QUERY_LENGTH];
19368+
int rc;
19369+
19370+
conn1= client_connect(0, MYSQL_PROTOCOL_DEFAULT, 1);
19371+
conn2= client_connect(0, MYSQL_PROTOCOL_DEFAULT, 0);
19372+
19373+
stmt1 = mysql_stmt_init(conn1);
19374+
check_stmt(stmt1);
19375+
rc= mysql_stmt_prepare(stmt1, stmt1_txt, strlen(stmt1_txt));
19376+
check_execute(stmt1, rc);
19377+
19378+
thread_id= mysql_thread_id(conn1);
19379+
sprintf(query, "KILL %lu", thread_id);
19380+
if (thread_query(query))
19381+
exit(1);
19382+
19383+
/*
19384+
After the connection is killed, the connection is
19385+
re-established due to the reconnect flag.
19386+
*/
19387+
stmt2 = mysql_stmt_init(conn1);
19388+
check_stmt(stmt2);
19389+
19390+
rc= mysql_stmt_prepare(stmt2, stmt2_txt, strlen(stmt2_txt));
19391+
check_execute(stmt1, rc);
19392+
19393+
mysql_stmt_close(stmt2);
19394+
mysql_stmt_close(stmt1);
19395+
19396+
mysql_close(conn1);
19397+
mysql_close(conn2);
19398+
}
19399+
19400+
1935619401
static struct my_tests_st my_tests[]= {
1935719402
{ "disable_query_logs", disable_query_logs },
1935819403
{ "test_view_sp_list_fields", test_view_sp_list_fields },
@@ -19627,6 +19672,7 @@ static struct my_tests_st my_tests[]= {
1962719672
#ifndef EMBEDDED_LIBRARY
1962819673
{ "test_bug17309863", test_bug17309863},
1962919674
#endif
19675+
{ "test_bug17512527", test_bug17512527},
1963019676
{ 0, 0 }
1963119677
};
1963219678

0 commit comments

Comments
 (0)