List:Connector/C++« Previous MessageNext Message »
From:Jean-Denis Muys Date:October 31 2009 4:55pm
Subject:memory corruption inside connector/c++: whose bug?
View as plain text  
Hi,

I have almost run out of hair to pull out, so I'd like a bit of help  
on this bug.

I am facing memory corruption in my code that uses connector/C++ 1.05:  
it looks like some memory is written to after it has been freed. I  
suspect some automatically invoked destructor, but I am now running  
out of idea. Here are the details:

If I delete the sql::ResultSet returned by  
sql::PreparedStatement::executeQuery(), then memory is corrupted in  
the chain of called destructors. I managed to trace it to this point  
where I see a free block bein written over:

#0	0x059fa6b9 in sql::mysql::MySQL_ResultBind::~MySQL_ResultBind at  
mysql_resultbind.cpp:119
#1	0x059f1aa7 in  
std::auto_ptr<sql::mysql::MySQL_ResultBind>::~auto_ptr at memory:259
#2	0x059f8433 in  
sql::mysql::MySQL_Prepared_ResultSet::~MySQL_Prepared_ResultSet at  
mysql_ps_resultset.cpp:104
#3	0x05a2b4d7 in reducer::execute at RKTable4D.records.cpp:94
#4	0x05a2b5ce in reducer::execute at RKTable4D.records.cpp:81
#5	0x05a2a03a in RK::TableMirror4D::getRemoteStamp at  
RKTable4D.records.cpp:116

This happens at the end of this routine:

MySQL_ResultBind::~MySQL_ResultBind()
{
	if (rbind.get()) {
		for (unsigned int i = 0; i < num_fields; ++i) {
			delete[] (char *) rbind[i].buffer;
		}
	}
}

Here is a screen shot of my debugger showing the freed object  
partially written over:

http://emberapp.com/jdmuys/images/xcode

Note: after being freed, blocks are filled by malloc() with a 0x55  
pattern.

You can see that the beginning of the block at address 0x03c8d880 is  
run over: length, is_null and buffer were written to. The step before,  
they were 0x55.

For completeness, here are the last 2 steps of this block's  
malloc_history:

ALLOC 0x3c8d880-0x3c8d8bf [size=64]: thread_b02e9000 [...]  
RK::TableMirror4D::getRemoteStamp(sql::Connection*, long) |  
reducer::execute(sql::Connection*, long) | reducer::execute 
(sql::Connection*) | sql::mysql::MySQL_Prepared_Statement::executeQuery 
() | sql::mysql::MySQL_Prepared_ResultSet::MySQL_Prepared_ResultSet 
(st_mysql_stmt*, sql::mysql::MySQL_ResultBind*,  
sql::ResultSet::enum_type, sql::mysql::MySQL_Prepared_Statement*,  
sql::mysql::util::my_shared_ptr<sql::mysql::MySQL_DebugLogger>*) |  
sql::mysql::MySQL_ResultBind::bindResult() | operator new[](unsigned  
long) | operator new(unsigned long) | malloc | malloc_zone_malloc
----
FREE  0x3c8d880-0x3c8d8bf [size=64]: thread_b02e9000 [...]  
RK::TableMirror4D::getRemoteStamp(sql::Connection*, long) |  
reducer::execute(sql::Connection*, long) | reducer::execute 
(sql::Connection*) |  
sql::mysql::MySQL_Prepared_ResultSet::~MySQL_Prepared_ResultSet() |  
std::auto_ptr<sql::mysql::MySQL_ResultBind>::~auto_ptr() |  
sql::mysql::MySQL_ResultBind::~MySQL_ResultBind() |  
sql::mysql::util::my_array_guard<st_mysql_bind>::~my_array_guard() |  
free

In summary, I am seeing Connector/C++ write into a block it's just  
freed!

The code that demonstrates this is the following snippet:

class reducer {
public:
	sql::Connection *_mySQL;		// the connection used to prepare the  
statement
	TableMirror4D	*_mirror;		// the mirror that owns this query
	std::string	_queryAsString;		// the cached text of the query
	sql::PreparedStatement *_preparedStmt;	// the prepared statement;
	sql::ResultSet *_results;
	reducer(TableMirror4D *mirror) {
		_mirror = mirror;
		_queryAsString = "";
		_mySQL = NULL;
		_preparedStmt = NULL;
		_results = NULL;
	}
	virtual string *getQueryAsString(char* comScheme = NULL)
	{
		if (this->_queryAsString == "" ) {
			stringstream sql;
			sql.str("");
			
			sql << "SELECT `STAMP` FROM `Wish List`";
			sql << " WHERE `RKid`=? and RKDeleted=0";
			
			this->_queryAsString = sql.str();
		}
		
		return &this->_queryAsString;
	}
	virtual void prepare(sql::Connection *conn)
	{
		if (conn != this->_mySQL) {
			if (this->_preparedStmt)
				delete this->_preparedStmt;
			string *q = this->getQueryAsString();
			this->_preparedStmt = conn->prepareStatement(*q);
			this->_mySQL = conn;
		}
	}
	virtual long execute(sql::Connection *conn, long condValue)
	{
		this->_preparedStmt->setInt(1, condValue);
		long result = this->execute(conn);
		return result;
	}
	virtual long execute(sql::Connection *conn)
	{
		this->_results = this->_preparedStmt->executeQuery();
		long result = 0;
		long rowCount = this->_results->rowsCount();
		if (rowCount > 0) {
			this->_results->first();
			result = this->_results->getUInt(1);
		}
		delete this->_results; // memory corruption goes away if I remove  
this line

		return result;	
	}
};

long TableMirror4D::getRemoteStamp(sql::Connection *conn, long rkid)
{
	long stamp = 0;
	static reducer *select = NULL;
	if (select == NULL)
		select = new reducer(this);
	zoneCheck();
	select->prepare(conn);
	zoneCheck();
	stamp = select->execute(conn, rkid);
}

Note the "static" is important: the memory corruption never happens  
through the first use of the reducer instance.
Note also that *no* exception are involved in this code.

I am suppose the bug is mine, but I really can't see what is wrong in  
my code above.

So could it be an obscure bug in connector/C++? Under which conditions  
could client code lead Connector/C++ to write into a block it has just  
freed?

Thanks for any help.

Jean-Denis




Thread
memory corruption inside connector/c++: whose bug?Jean-Denis Muys31 Oct
  • Re: memory corruption inside connector/c++: whose bug?Jean-Denis Muys31 Oct
    • Re: memory corruption inside connector/c++: whose bug?Jean-Denis Muys3 Nov