Bug #115710 Access violation writing location 0x0000000000000320 while opening connection
Submitted: 29 Jul 5:30 Modified: 12 Aug 19:29
Reporter: Hennadii Niemtsov Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / ODBC Severity:S3 (Non-critical)
Version:9.0.0.0 OS:Windows (Windows 10 Enterprise Version:10.0.19045)
Assigned to: CPU Architecture:Other (AMD64)
Tags: access violation

[29 Jul 5:30] Hennadii Niemtsov
Description:
In my C++ test console application running in VS2022 in Debug mode there are 2 threads which is trying to establish a connection to MySQL server (v8.0.38) DB running on the same laptop.

The connection is made via preconfigured System DSN using ODBC Data source (64 bit) Admin tool with chosen MySql ODBC 9.0 ANSI Driver and TCP connection via 3306 port with password authentication.

Occasionally it throws an exception:
Exception thrown at 0x00007FFC426FE6F8 (libmysql.dll) in TestMySqlOdbc.exe: 0xC0000005: Access violation writing location 0x0000000000000320.

Here is stack trace for 1 of 2 thread where exception is thrown:
Not Flagged	>	6156	0	Worker Thread	ucrtbased.dll thread	libmysql.dll!00007ffc42c4e6f8
libmysql.dll!00007ffc42c4e6f8()
myodbc9a.dll!DBC::connect(DataSource * dsrc) Line 341
myodbc9a.dll!MySQLConnect() Line 1112
myodbc9a.dll!SQLConnect() Line 172
odbc32.dll!00007ffd1ff04ea3()
odbc32.dll!00007ffd1feeaa2b()
odbc32.dll!00007ffd1feea627()
odbc32.dll!00007ffd1ff1e14f()
TestMySqlOdbc.exe!run(int i) Line 89
TestMySqlOdbc.exe!main::__l2::<lambda_2>::operator()() Line 120
TestMySqlOdbc.exe!std::invoke<`main'::`2'::<lambda_2>>(main::__l2::<lambda_2> && _Obj) Line 1705
TestMySqlOdbc.exe!std::thread::_Invoke<std::tuple<`main'::`2'::<lambda_2>>,0>(void * _RawVals) Line 60
ucrtbased.dll!00007ffc5abc3010()
kernel32.dll!00007ffd4d297374()
ntdll.dll!00007ffd4ec5cc91()

For more details the generated dmp file is attached.

Exception happens in connector.cc module in SQLRETURN DBC::connect(DataSource *dsrc) method.

it seems that mysql object is not initialized and points to NULL after line #318:

mysql = mysql_init(nullptr);

Specification says that return NULL is possible in case of insufficient memory.
Windows Task Manager shows that on my laptop it is about 86% of 16 Gb RAM in use.

Next lines of code without check for NULL it is trying to set the connection option calling mysql_options() with no valid mysql connection handler passed in 1st parameter. 

How to repeat:
- Create a database in the local instance of MySQL
- Create a DSN, in the ODBC Administration console (64 bit), using MySQL Connector 9.0 ANSI and pointing to the created database.
- Open attached C++ console project in VS2022 and run few times until get the exception (for me it happens in 1 of 10 times).

Suggested fix:
Please check the mysql connection handler for validity before calling any of mysql_options().

In case if mysql_init() returns null then set sql error and return from connect method like this:

  if (!mysql)
      return set_error("HY001", "Failed to initialize mysql", 0);
[29 Jul 5:53] Hennadii Niemtsov
VS2022 C++ project file to reproduce issue

Attachment: TestMySqlOdbc.zip (application/x-zip-compressed, text), 4.88 KiB.

[29 Jul 5:55] Hennadii Niemtsov
generated dump file

Attachment: TestMySqlOdbc_DMP.zip (application/x-zip-compressed, text), 15.16 MiB.

[30 Jul 4:22] MySQL Verification Team
Hello Hennadii Niemtsov,

Thank you for the bug report.
Verified as described.

Regards,
Ashwini Patil
[2 Aug 4:02] Hennadii Niemtsov
I found conditions when mysql_init() returns null. Here is explanation a little verbally so check attached PNG image for clarity.  

Here is what i found:

Initially variables: mysql_client_init and my_thread_global_init_done are set to FALSE.

Both threads are executing `mysql_server_init()`, but the first thread is the one that checks the state of `mysql_client_init` and changes it from `FALSE` to `TRUE`. As a result, the second thread follows a different execution path. If the second thread reads the `my_thread_global_init_done` variable before the first thread has set it to `TRUE`, the second thread will return a null pointer from `mysql_init()`.
[2 Aug 4:03] Hennadii Niemtsov
when mysql_init() returns null

Attachment: nullptr.png (image/png, text), 38.78 KiB.

[12 Aug 9:41] Bogdan Degtyariov
Posted by developer:
 
The issue happened because of concurrent calls to mysql_init() that called mysql_library_init() simultaneously. It was not thread safe
The patch makes sure mysql_library_init() is called before any mysql_init() calls. This makes mysql_init() thread safe.
Changes are pushed in the source tree.
[12 Aug 19:29] Philip Olson
Posted by developer:
 
Fixed as of the upcoming MySQL Connector/ODBC 9.1.0 release, and here's the proposed changelog entry from the documentation team:

The internal mysql_init() method used for making connections is now thread safe.

Thank you for the bug report.