Bug #116119 use onnector-c++ 9.0.0 to save base64 data to blob column, segment fault occur
Submitted: 16 Sep 2024 11:24 Modified: 18 Sep 2024 9:08
Reporter: jun zhang Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / C++ Severity:S3 (Non-critical)
Version: OS:Ubuntu
Assigned to: CPU Architecture:x86
Tags: mysql connector c++ 9.0.0, sql::PreparedStatement::setBlob

[16 Sep 2024 11:24] jun zhang
Description:
I create a table which the used to store the base64 data, which encoding from a image data from the clipboard under my developing qt c++ QWidget application.

I use the msyql-connector-c++ 9.0.0 to developing the mysql client.

but when the store process be triggerd, it will occur segmentfault. use gdb to debug and the stack info is like bellow:
~~~sh
(gdb) bt                                                                                                              
#0  0x00007ffff5ac8bf4 in std::basic_ios<char, std::char_traits<char> >::eof() const ()                               
    at /lib/x86_64-linux-gnu/libstdc++.so.6                                                                           
#1  0x00007ffff7a64ae6 in sql::mysql::LongDataSender::operator()(std::istream*) const                                 
    (this=0x7fffffffc340, my_blob=0x7fffffffc1f0) at ./jdbc/driver/mysql_prepared_statement.cpp:99           
#2  0x00007ffff7a69312 in std::__invoke_impl<bool, sql::mysql::LongDataSender&, std::istream*&>(std::__invoke_other, sql::mysql::LongDataSender&, std::istream*&) (__f=...) at /usr/include/c++/9/bits/invoke.h:60
#3  0x00007ffff7a68141 in std::__invoke<sql::mysql::LongDataSender&, std::istream*&>(sql::mysql::LongDataSender&, std::istream*&) (__fn=...) at /usr/include/c++/9/bits/invoke.h:95
#4  0x00007ffff7a667ba in std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<bool (*)(sql::mysql::LongDataSender&, std::variant<std::istream*, sql::SQLString*>&)>, std::tupl
e<std::variant<std::istream*, sql::SQLString*>&>, std::integer_sequence<unsigned long, 0ul> >::__visit_invoke_impl(sql::mysql::LongDataSender&, std::variant<std::istream*, sql::SQLString*>&) (__visitor=..., __va
rs#0=std::variant<> [no contained value])                                                                             
    at /usr/include/c++/9/variant:981
#5  0x00007ffff7a667fc in std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<bool (*)(sql::mysql::LongDataSender&, std::variant<std::istream*, sql::SQLString*>&)>, std::tupl
e<std::variant<std::istream*, sql::SQLString*>&>, std::integer_sequence<unsigned long, 0ul> >::__do_visit_invoke(sql::mysql::LongDataSender&, std::variant<std::istream*, sql::SQLString*>&) (__visitor=..., __vars
#0=std::variant<> [no contained value])                                                                               
    at /usr/include/c++/9/variant:989                                                                                 
#6  0x00007ffff7a6683e in std::__detail::__variant::__gen_vtable_impl<true, std::__detail::__variant::_Multi_array<bool (*)(sql::mysql::LongDataSender&, std::variant<std::istream*, sql::SQLString*>&)>, std::tupl
e<std::variant<std::istream*, sql::SQLString*>&>, std::integer_sequence<unsigned long, 0ul> >::__visit_invoke(sql::mysql::LongDataSender&, std::variant<std::istream*, sql::SQLString*>&) (__visitor=..., __vars#0=
std::variant<> [no contained value])                                                                                  
    at /usr/include/c++/9/variant:1005
#7  0x00007ffff7a669d9 in std::__do_visit<false, true, sql::mysql::LongDataSender&, std::variant<std::istream*, sql::SQLString*>&>(sql::mysql::LongDataSender&, std::variant<std::istream*, sql::SQLString*>&) (__v
isitor=...)                                                                                                           
    at /usr/include/c++/9/variant:1652
#8  0x00007ffff7a66a39 in std::visit<sql::mysql::LongDataSender&, std::variant<std::istream*, sql::SQLString*>&>(sql::mysql::LongDataSender&, std::variant<std::istream*, sql::SQLString*>&) (__visitor=...)
    at /usr/include/c++/9/variant:1663               
#9  0x00007ffff7a60903 in sql::mysql::MySQL_Prepared_Statement::sendLongDataBeforeParamBind() (this=0x55555d05ae70)
    at ./jdbc/driver/mysql_prepared_statement.cpp:450
#10 0x00007ffff7a60b8f in sql::mysql::MySQL_Prepared_Statement::do_query() (this=0x55555d05ae70)     
    at ./jdbc/driver/mysql_prepared_statement.cpp:478
#11 0x00007ffff7a61294 in sql::mysql::MySQL_Prepared_Statement::executeUpdate() (this=0x55555d05ae70)

...

~~~

How to repeat:
(1) create the table like bellow:
~~~sh
#define CREATE_IMAGE_TABLE \
    "CREATE TABLE IF NOT EXISTS imageTable(" \
    "IMAGEID INT AUTO_INCREMENT PRIMARY KEY," \
    "TIMESTAMP VARCHAR(255)," \
    "IMAGE BLOB);"
~~~

(2) make prepareStatment from bellow sql clause:
"insert into imageTable (timestamp, image) values(?, ?);"

(3) insert a image's base64 data which assign to a std::string, like bellow function:
~~~cpp
void storeImageBase64Data(const std::string& timestamp, const std::string& 
    baset64Data)
{
std::string sql("insert into imageTable (timestamp, image) values(?, ?);");

auto statement = connect_->prepareStatement(sql);
statement->setString(1, timestamp);

std::istringstream blobStream(baset64Data);
std::cout << "data:" << blobStream.str() << "\n";
statement->setBlob(2, &blobStream);
statement->executeUpdate();
}
~~~
I copy the output of "std::cout << "data:" << blobStream.str() << "\n";" and according the output, the base64 data is more than 2M bytes. and copy it to show on the side https://base64.guru/converter/decode/image to show the image, it can show the image.
[16 Sep 2024 12:47] MySQL Verification Team
HI Mr. zhang,

Thank you for your bug report.

However, there are many facts missing from your report that disables us from repeating your test case.

First of all, we need the entire C++ code that leads to the error.  Entire C++ code, so that we can compile it, run it and see for ourselves.

We require the image that leads to the error.

Also, we do not know what is crashing in this bug. Your program or MySQL server ????

If it is MySQL server, then you can just send SQL variant that does the same thing.

Next, You are using MySQL 9.00, while the current release is 9.0.1. Please, try that one.

We also need to know whether C/C++ is the same version as MySQL server.

Last, but not least, we need to know whether latest releases of MySQL 8.0 and 8.4 are also affected.

Thank you very much in advance for your full and comprehensive answers .......

Can't repeat.
[18 Sep 2024 0:31] Bogdan Degtyariov
According to the bug description the data length is ~2Mb, but BLOB column in MySQL can hold only 65535 bytes. It will not work the way you intended. After modifying column to MEDIUMBLOB the insert went fine.

Another consideration is the change in MySQL network protocol. Due to protocol incompatibility (which by the way was introduced in version 8.4) it is expected that MySQL Connector/C++ 8.4/9.0 fails to do certain operations on MySQL 8.1, 8.2 and 8.3 servers. The issue is around prepared statement being executed. MySQL network protocol and C API for Prepared Statements are using a new function mysql_stmt_bind_named_param() instead of mysql_stmt_bind_param().
The issues are expected with non-GA versions of the MySQL Server.

However, since 8.0.X is still GA + Long Term Supported, it received the protocol updates and MySQL Connector/C++ 9.X works fine with it.

Test code:

void connection::check_bug()
{
  logMsg("connection::getClientInfo() - MySQL_Connection::getClientInfo()");
  try
  {
    stmt.reset(con->createStatement());
    stmt->executeUpdate("DROP TABLE IF EXISTS imageTable");

    stmt->executeUpdate("CREATE TABLE IF NOT EXISTS imageTable("
      "IMAGEID INT AUTO_INCREMENT PRIMARY KEY,"
      "TIMESTAMP VARCHAR(255),"
      "IMAGE MEDIUMBLOB);");

    pstmt.reset(con->prepareStatement("insert into imageTable (timestamp, image) values(?, ?)"));
    pstmt->setString(1, "2024-09-18 10:02:22");

    std::ifstream imageFile("/home/dbs/Pictures/wallpaper.jpg", std::ios::binary);

    std::string fileContents((std::istreambuf_iterator<char>(imageFile)),
                              std::istreambuf_iterator<char>());

    imageFile.close();

    std::istringstream blobStream(fileContents);
    pstmt->setBlob(2, &blobStream);
    pstmt->executeUpdate();
  }
  catch (sql::SQLException &e)
  {
    logErr(e.what());
    logErr("SQLState: " + std::string(e.getSQLState()));
    fail(e.what(), __FILE__, __LINE__);
  }
}