MySQL++

Memory error with
Login

Memory error with

(1) By Angelo (aacirino) on 2020-02-29 13:11:27 [link] [source]

Hi, I am on macOS 10.12.6 with MySQL++ and all its development environment installed with brew. Currently MySQL++ 3.2.5_1, MySQL 8.0.18, clang 9.0.1 and unit testing with Catch v2.11.1. Writing my unit tests I stumbled on a memory allocation error that seems to originate in libmysqlclient as can be seen below:

=================================================================
==86865==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x6250000280e0 in thread T0
    #0 0x103c872c6 in wrap_free (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x432c6)
    #1 0x10347edb3 in MEM_ROOT::Clear() (libmysqlclient.21.dylib:x86_64+0x5fdb3)
    #2 0x10342941f in mysql_free_result (libmysqlclient.21.dylib:x86_64+0xa41f)
    #3 0x1026c17d8 in mysqlpp::RefCountedPointerDestroyer<MYSQL_RES>::operator()(MYSQL_RES*) const (senna:x86_64+0x1005947d8)
    #4 0x1026c1640 in mysqlpp::RefCountedPointer<MYSQL_RES, mysqlpp::RefCountedPointerDestroyer<MYSQL_RES> >::~RefCountedPointer() (senna:x86_64+0x100594640)
    #5 0x1026c1394 in mysqlpp::RefCountedPointer<MYSQL_RES, mysqlpp::RefCountedPointerDestroyer<MYSQL_RES> >::~RefCountedPointer() (senna:x86_64+0x100594394)
    #6 0x1026c1321 in mysqlpp::UseQueryResult::~UseQueryResult() (senna:x86_64+0x100594321)
    #7 0x1026bfdc4 in mysqlpp::UseQueryResult::~UseQueryResult() (senna:x86_64+0x100592dc4)
    #8 0x10268100f in senna::server::load_investors()::$_2 mysqlpp::Query::for_each<senna::t_investor, senna::server::load_investors()::$_2>(senna::t_investor const&, senna::server::load_investors()::$_2) (senna:x86_64+0x10055400f)
    #9 0x102680510 in senna::server::load_investors() (senna:x86_64+0x100553510)
    #10 0x1026020a3 in ____C_A_T_C_H____T_E_S_T____0() database.cpp:112
    #11 0x1021da828 in Catch::TestInvokerAsFunction::invoke() const catch.hpp:14054
    #12 0x1021abb79 in Catch::TestCase::invoke() const catch.hpp:13947
    #13 0x1021ab7fb in Catch::RunContext::invokeActiveTestCase() catch.hpp:12809
    #14 0x10219c376 in Catch::RunContext::runCurrentTest(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) catch.hpp:12782
    #15 0x102197d78 in Catch::RunContext::runTest(Catch::TestCase const&) catch.hpp:12543
    #16 0x1021be7d5 in Catch::(anonymous namespace)::TestGroup::execute() catch.hpp:13137
    #17 0x1021bb61d in Catch::Session::runInternal() catch.hpp:13343
    #18 0x1021ba418 in Catch::Session::run() catch.hpp:13299
    #19 0x10224f3fe in int Catch::Session::run<char>(int, char const* const*) catch.hpp:13021
    #20 0x10224efc1 in main catch.hpp:17182
    #21 0x7fffcf2fb234 in start (libdyld.dylib:x86_64+0x5234)

0x6250000280e0 is located 32 bytes to the left of 8200-byte region [0x625000028100,0x62500002a108)
allocated by thread T0 here:
    #0 0x103c8717d in wrap_malloc (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x4317d)
    #1 0x104cdfd21 in my_raw_malloc(unsigned long, int) (libmysqlclient.21.dylib:x86_64+0x61d21)
    #2 0x104cdd0f6 in MEM_ROOT::AllocBlock(unsigned long) (libmysqlclient.21.dylib:x86_64+0x5f0f6)
    #3 0x104cdd146 in MEM_ROOT::AllocSlow(unsigned long) (libmysqlclient.21.dylib:x86_64+0x5f146)
    #4 0x104c89d23 in cli_read_metadata_ex (libmysqlclient.21.dylib:x86_64+0xbd23)
    #5 0x104c91b6a in read_com_query_metadata(MYSQL*, unsigned char*, unsigned long) (libmysqlclient.21.dylib:x86_64+0x13b6a)
    #6 0x104c90e7e in cli_read_query_result(MYSQL*) (libmysqlclient.21.dylib:x86_64+0x12e7e)
    #7 0x104c8dd03 in mysql_real_query (libmysqlclient.21.dylib:x86_64+0xfd03)
    #8 0x103b31347 in mysqlpp::DBDriver::execute(char const*, unsigned long) (libmysqlpp.3.dylib:x86_64+0x7347)
    #9 0x103b389f2 in mysqlpp::Query::use(char const*, unsigned long) (libmysqlpp.3.dylib:x86_64+0xe9f2)
    #10 0x103b3889a in mysqlpp::Query::use(mysqlpp::SQLTypeAdapter const&) (libmysqlpp.3.dylib:x86_64+0xe89a)
    #11 0x102680bdb in senna::server::load_investors()::$_2 mysqlpp::Query::for_each<senna::t_investor, senna::server::load_investors()::$_2>(senna::t_investor const&, senna::server::load_investors()::$_2) (senna:x86_64+0x100553bdb)
    #12 0x102680510 in senna::server::load_investors() (senna:x86_64+0x100553510)
    #13 0x1026020a3 in ____C_A_T_C_H____T_E_S_T____0() database.cpp:112
    #14 0x1021da828 in Catch::TestInvokerAsFunction::invoke() const catch.hpp:14054
    #15 0x1021abb79 in Catch::TestCase::invoke() const catch.hpp:13947
    #16 0x1021ab7fb in Catch::RunContext::invokeActiveTestCase() catch.hpp:12809
    #17 0x10219c376 in Catch::RunContext::runCurrentTest(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) catch.hpp:12782
    #18 0x102197d78 in Catch::RunContext::runTest(Catch::TestCase const&) catch.hpp:12543
    #19 0x1021be7d5 in Catch::(anonymous namespace)::TestGroup::execute() catch.hpp:13137
    #20 0x1021bb61d in Catch::Session::runInternal() catch.hpp:13343
    #21 0x1021ba418 in Catch::Session::run() catch.hpp:13299
    #22 0x10224f3fe in int Catch::Session::run<char>(int, char const* const*) catch.hpp:13021
    #23 0x10224efc1 in main catch.hpp:17182
    #24 0x7fffcf2fb234 in start (libdyld.dylib:x86_64+0x5234)

SUMMARY: AddressSanitizer: bad-free (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x432c6) in wrap_free
==86865==ABORTING
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
senna is a Catch v2.11.1 host application.

It happens in a very simple SELECT clause:

auto result = query.for_each(t_investor{}, [](const t_investor &i) { std::cerr << i.name << "\n"; });

This simple query in a DB with only two test rows is returning absurdly high numbers for the quantity of selected rows, like running this simple code

  auto s = "SELECT * FROM `senna`.`t_investor`"s;
  auto query = conn_.query("SELECT * FROM `senna`.`t_investor`");
  auto result = query.execute();
  std::cerr << result.rows() << "\n";

The output is 18446744073709551615 with this error reported by ubsanitizer:

/usr/local/opt/llvm/include/c++/v1/__locale:250:12: runtime error: downcast of address 0x7fffd80187b0 which does not point to an object of type 'const std::__1::ctype<char>'
0x7fffd80187b0: note: object is of type 'std::__1::ctype<char>'
 00 00 00 00  b8 17 01 d8 ff 7f 00 00  01 00 00 00 00 00 00 00  8c dc 26 d8 ff 7f 00 00  00 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'std::__1::ctype<char>'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/local/opt/llvm/include/c++/v1/__locale:250:12 in
/usr/local/opt/llvm/include/c++/v1/ios:777:51: runtime error: member call on address 0x7fffd80187b0 which does not point to an object of type 'std::__1::ctype<char>'
0x7fffd80187b0: note: object is of type 'std::__1::ctype<char>'
 00 00 00 00  b8 17 01 d8 ff 7f 00 00  01 00 00 00 00 00 00 00  8c dc 26 d8 ff 7f 00 00  00 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'std::__1::ctype<char>'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/local/opt/llvm/include/c++/v1/ios:777:51 in
/usr/local/opt/llvm/include/c++/v1/__locale:681:16: runtime error: member call on address 0x7fffd80187b0 which does not point to an object of type 'std::__1::ctype<char>'
0x7fffd80187b0: note: object is of type 'std::__1::ctype<char>'
 00 00 00 00  b8 17 01 d8 ff 7f 00 00  01 00 00 00 00 00 00 00  8c dc 26 d8 ff 7f 00 00  00 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'std::__1::ctype<char>'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/local/opt/llvm/include/c++/v1/__locale:681:16 in

Maybe this is a libmysqlclient issue with the DB collation that is set to UTF8 and so the char type should be wchar or something. I googled it but found no clue about setting the DB collation within mysqlclient lib.

If this is an issue that can be solved within MySQL++ I am willing to devote my time to fix it. Otherwise I would like to know what can be done to solve it.

Cheers,

Angelo

(2) By Warren Young (tangent) on 2020-02-29 16:57:28 [link] [source] in reply to 1

a memory allocation error that seems to originate in libmysqlclient

That's the underlying C runtime library, not MySQL++, thus not our code.

It is quite possible that altering the way you use MySQL++ can solve it, but if the issue is entirely within libmysqlclient, you'll need to contact whoever maintains that particular version of the library. Given that it's "MySQL 8" from Homebrew, I'm guessing Oracle, though of course then you'd probably need a support contract to get their attention.

AddressSanitizer: attempting free on address which was not malloc()-ed:

...and then ASAN goes on to point the finger at ~UseQueryResult(), which suggests a double free.

I'm pretty sure MySQL++ doesn't do that itself. Run ASAN on the examples if you need to prove that it does.

It is more likely to be something in the way you're using the library to cause it, else this support channel would be flooded with many others complaining of the same problem.

If you find yourself coming to a culprit and can't see why it would matter, port the problem code to one of the examples and see if the problem reoccurs there. If so, we'll be far more likely to study a modification to one of them than to try to understand an outside program we can't likely even build, much less run.

More likely, in doing so you will discover a library misuse. Perhaps then a suggestion about a documentation improvement would help.

a very simple SELECT clause

First, what you show isn't in fact a SQL SELECT statement.

Second, you don't declare your data types: I have no idea what a t_investor is.

Again I suggest porting the logic you suspect to one of the examples, so we can work with a common set of data types, DB structures, etc.

The output is 18446744073709551615

That's a 0xFFFFFFFFFFFFFFFF cast to unsigned by the return type of Result.rows(), or -1 on a 64-bit two's complement host CPU.

Therefore, you must have exceptions disabled and are now reading a garbage return because your execute() failed.

Always check error returns when not using exceptions!

I googled it but found no clue about setting the DB collation within mysqlclient lib.

You don't; the collation is a property of the DB table.

See also the chapter in the MySQL++ user manual on Unicode.

(3) By Warren Young (tangent) on 2020-02-29 17:07:48 and edited on 2020-02-29 17:08:07 [history] [link] [source] in reply to 2

That's the underlying C runtime library

I meant to say that it's the underlying MySQL C library. I definitely do not mean to blame libc here. :)

Always check error returns when not using exceptions!

I should have added that you can do that here by wrapping your cerr line with:

if (result) { ...

That is, testing a MySQL++ Result object in bool context tells you whether there are errors.

You would then want to call the various error reporting functions on the Result, Query, or Connection object in the else clause to find out what went wrong with the query.

The bottom line is that calling Result.rows() while the object is in an error state is the bug here, not in rows() itself. That's why it returns ulonglong: the return should always be 0 or greater.

(5) By Angelo (aacirino) on 2020-03-01 14:01:24 and edited on 2020-03-01 14:02:58 [history] [link] [source] in reply to 2

Hi, I changed the way I am using MySQL++ and solved my issue. Regarding your points, t_investor is a ssqls:

sql_create_8(t_investor, 1, 8,
  mysqlpp::sql_char, name,
  mysqlpp::sql_char, phone,
  mysqlpp::sql_char, email,
  mysqlpp::sql_double, funds,
  mysqlpp::sql_double, risk,
  mysqlpp::sql_double, stop_loss,
  mysqlpp::sql_int, leverage,
  mysqlpp::sql_double, initial_funds
)

and from the header this code auto result = query.for_each(t_investor{}, [](const t_investor &i) { std::cerr << i.name << "\n"; }); is in fact

/// \brief Run a functor for every row in a table
	///
	/// Just like for_each(Function), except that it builds a
	/// "select * from TABLE" query using the SQL table name from
	/// the SSQLS instance you pass.
	///
	/// \param ssqls the SSQLS instance to get a table name from
	/// \param fn the functor called for each row
	///
	/// \return a copy of the passed functor
template <class SSQLS, typename Function>
	Function for_each(const SSQLS& ssqls, Function fn)
	{	
		std::string query("select * from `");
		query += ssqls.table();
      query += '`';
		mysqlpp::UseQueryResult res = use(query);
		if (res) {
			mysqlpp::NoExceptions ne(res);
			while (mysqlpp::Row row = res.fetch_row()) {
				fn(row);
			}
		}

		return fn;
	}

That's why I regarded it as a simple SQL select but it fails with the memory allocation problem. In this specific case it doesn't seem an issue with the way I use MySQL++. So, if I am using the API as it is meant to be used, should we conclude that the problem is in the MySQL C library? Why does this for_each overload fails?

Cheers and thanx for your answer.

(7) By Warren Young (tangent) on 2020-03-02 19:44:22 and edited on 2020-03-02 20:15:42 [history] [link] [source] in reply to 5

I changed the way I am using MySQL++ and solved my issue.

Care to share your solution for others to find?

it fails with the memory allocation problem.

I re-configured the library like so:

$ ./configure CXXFLAGS='-fsanitize=address,undefined -std=c++11' \
               LDFLAGS='-fsanitize=address,undefined'

And then I ran the for_each example like so:

$ ./exrun for_each -utest -ptest

...and I got no complaints from ASAN or UBSAN. This is with Clang on macOS 10.14.

I then modified it to use a lambda instead of a functor object with the following diff and it still doesn't give me any complaint:

 		// Establish the connection to the database server.
 		mysqlpp::Connection con(mysqlpp::examples::db_name,
 				cmdline.server(), cmdline.user(), cmdline.pass());
 
 		// Gather and display the stats for the entire stock table
+		mysqlpp::sql_bigint items = 0;
+		mysqlpp::sql_double weight = 0, cost = 0;
 		mysqlpp::Query query = con.query();
-		std::cout << "There are " << query.for_each(stock(),
-				gather_stock_stats()) << '.' << std::endl;
+		query.for_each(stock(), [&](const stock &s) {
+			items  += s.num;
+			weight += (s.num * s.weight);
+			cost   += (s.num * s.price.data);
+		});
+		std::cout << "There are " <<
+			items << " items " <<
+			"weighing " << weight << " stone and " <<
+			"costing " << cost << " cowrie shells" <<
+			'.' << std::endl;


If you want to pursue this further, you need to show me that the example fails.

Wild guess: you're using the Homebrew version of MySQL++, so it is built without ASAN and UBSAN support, causing ASAN and UBSAN in your host program to be unable to track such matters into the library. Try building the library from source with the necessary build flags and then testing your program with that build of the library.

(9) By Angelo (aacirino) on 2020-03-03 02:08:42 [source] in reply to 7

Care to share your solution for others to find?

I abandoned query.for_each, query.execute and query.storein to retrieve data and am using exclusively query.store. The first three options give me a memory error, with or without the sanitizers, either ub, address or thread.

I will first finish my app due to business constraints and will then dedicate my time to this issue. I will surely build the library from source and try to figure out what happens in my installation.