Re: mysql_stmt_execute() trashing stack
Posted by:
John Deal
Date: August 07, 2011 12:18PM
More work on this. I have changed the setting of the one bind structure as follows. Some of these should have no effect for example trunction_flags since they are only used for output but I am desperate!
memset(bind, 0, sizeof(bind)); // Initialize MYSQL_BIND structure.
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].buffer = (char*) &user_id;
bind[0].buffer_length = sizeof(user_id);
lengths[0] = sizeof(user_id);
bind[0].length = &lengths[0];
bind[0].is_null = (my_bool*) 0;
bind[0].is_unsigned = true;
bind[0].error = &truncation_flags[0]; // DEBUG!:JRD
I have added the following stack guard code.
At beginning of method (I manually verified the mysql_stmt_bind_param() call does not write into the guard area. count is a static initialized to 0):
const int extra_length = 2000;
volatile unsigned int extra[extra_length]; // HACK!:JRD
// START DEBUG!:JRD
cout << "get_user_subscription_workplace_list() count: " << count << endl;
count++;
for (int index = 0; index < extra_length; index++)
{
extra[index] = ~0; // Set all bits to one.
}
// STOP DEBUG!:JRD
After the mysql_stmt_execute() call is the following:
// START DEBUG!:JRD
for (int index = 0; index < extra_length; index++)
{
if (extra[index] != (unsigned int) ~0)
{
cout << "AFTER: corruption at: " << index << " Value: "
<< extra[index] << endl;
}
}
// STOP DEBUG!:JRD
Setting compile flags to -g and -O0 I run this under valgrind and get the following:
bin$ sudo valgrind --leak-check=yes ./mira_server
==4766== Memcheck, a memory error detector
==4766== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==4766== Using Valgrind-3.5.0-Debian and LibVEX; rerun with -h for copyright info
==4766== Command: ./mira_server
==4766==
Lib name: Files
Dynamically opened library....
Loaded function pointers....
Loaded Files Utility
Running File Server
Running File Server loop
Accepted connection from: 127.0.0.1
bytes_transferred: 19
IN: L jdeal MyPassowrd
get_user_subscription_workplace_list() count: 0
[OUT]: LS
bytes_transferred: 3
IN: LW
get_user_subscription_workplace_list() count: 1
get_user_subscription_workplace_list() count: 2
get_user_subscription_workplace_list() count: 3
get_user_subscription_workplace_list() count: 4
bdeal is offline
get_user_subscription_workplace_list() count: 5
get_user_subscription_workplace_list() count: 6
get_user_subscription_workplace_list() count: 7
bdeal is offline
get_user_subscription_workplace_list() count: 8
AFTER: corruption at: 1936 Value: 4
AFTER: corruption at: 1937 Value: 0
AFTER: corruption at: 1938 Value: 4
AFTER: corruption at: 1939 Value: 0
AFTER: corruption at: 1940 Value: 4
AFTER: corruption at: 1941 Value: 0
AFTER: corruption at: 1942 Value: 4
AFTER: corruption at: 1943 Value: 0
==4766== Thread 3:
==4766== Invalid write of size 8
==4766== at 0x54A49FC: ??? (in /usr/lib/libmysqlclient_r.so.16.0.0)
==4766== by 0x54A7D3F: mysql_stmt_execute (in /usr/lib/libmysqlclient_r.so.16.0.0)
==4766== by 0x5177A5: miraserver::directory::directory_mysql::DbMySqlDirectory::get_workplace_subscription_list(unsigned int) (DbMySqlDirectory.cpp:267)
==4766== by 0x524000: miraserver::directory::directory_mysql::DbMySqlDirectory::find_workplace(unsigned int) (DbMySqlDirectory.cpp:2775)
==4766== by 0x51620F: miraserver::directory::DbDirectory::find_workplace(unsigned int) (DbDirectory.cpp:424)
==4766== by 0x5BBA15: miraserver::network::MsgListWorkplaces::run() (MsgListWorkplaces.h:52)
==4766== by 0x5B68E4: miraserver::network::Msg::Parse(miraserver::network::TcpConnection*, std::string*) (Msg.cpp:151)
==4766== by 0x5C5110: void boost::_bi::list2<boost::_bi::value<miraserver::network::TcpConnection*>, boost::_bi::value<std::string*> >::operator()<void (*)(miraserver::network::TcpConnection*, std::string*), boost::_bi::list0>(boost::_bi::type<void>, void (*&)(miraserver::network::TcpConnection*, std::string*), boost::_bi::list0&, int) (bind.hpp:313)
==4766== by 0x5C488A: boost::_bi::bind_t<void, void (*)(miraserver::network::TcpConnection*, std::string*), boost::_bi::list2<boost::_bi::value<miraserver::network::TcpConnection*>, boost::_bi::value<std::string*> > >::operator()() (bind_template.hpp:20)
==4766== by 0x5C4112: boost::detail::function::void_function_obj_invoker0<boost::_bi::bind_t<void, void (*)(miraserver::network::TcpConnection*, std::string*), boost::_bi::list2<boost::_bi::value<miraserver::network::TcpConnection*>, boost::_bi::value<std::string*> > >, void>::invoke(boost::detail::function::function_buffer&) (function_template.hpp:153)
==4766== by 0x4E88CA: boost::function0<void>::operator()() const (function_template.hpp:1013)
==4766== by 0x4E778C: miraserver::ThreadPool::run() (ThreadPool.h:105)
==4766== Address 0x82d9120 is just below the stack ptr. To suppress, use: --workaround-gcc296-bugs=yes
==4766==
bytes_transferred: 30
IN: LU 7
IN: LU 7
IN: LU 7
IN: LU 7
IN: LU 7
IN: LU 7
get_user_subscription_workplace_list() count: 9
get_user_subscription_workplace_list() count: 10
Notice that valgrind picks up that mysql_stmt_execute() is writing past an allocated memory block. Line 267 in my code is the mysql_stmt_execute() call. Also notice this method runs several times before the corruption is detected (it could be writing elsewhere but note that my stack check and valgrind both catch it at the same time).
BTW I increased the stack size of this session to ~32 MB. Also I did an experiment by removing the last field from the select list so MySql would retrieve one less field. The same results except there were only 3 pairs of corrupted writes and the indexes were slightly different. So the situation is sensitive to the amount of data being retrieved.
I am not saying this is a MySQL issue but I am very lost on what the problem could be. Any insights would be most welcomed! Thanks!