Bug #103968 C++, odbc, using multi-threaded access to mysql database crash
Submitted: 10 Jun 2021 8:10 Modified: 12 Nov 2021 12:59
Reporter: Bob Lee Email Updates:
Status: No Feedback Impact on me:
None 
Category:Connector / ODBC Severity:S3 (Non-critical)
Version:comunity 8.0.21 and comunity 5.7.32 OS:Windows (windows 10 家庭中文版)
Assigned to: MySQL Verification Team CPU Architecture:Any
Tags: C++, multithread, ODBC

[10 Jun 2021 8:10] Bob Lee
Description:
I have two mysql servers, one on the local(comunity 8.0.21,64bit,win10-64bit) and one(comunity 5.7.32,32bit,win7-32bit) on the other computer.In my program, there is a timer that connects to the two mysql servers every two seconds to test whether the two mysql servers are normally turned on.When the program runs for a period of time, the program suddenly crashes.
When it crashes, the stack is as follows:
ntdll!RtlpWaitOnCriticalSection()
ntdll!RtlpEnterCriticalSectionContended()
ntdll!RtlEnterCriticalSection()
[Inline frame]myodbc8a.dll!native_mutex_lock(_RTL_CRITICAL_SECTION *)row 90
[Inline frame]myodbc8a.dll!my_mutex_lock(my_mutex_t *)row 189
[Inline frame]myodbc8a.dll!inline_mysql_mutex_lock(mysql_mutex_t *)row 267
myodbc8a.dll!mysql_reset_server_public_key()row 847
myodbc8a.dll!sha256_password_deinit()row 70
myodbc8a.dll!mysql_client_plugin_deinit()row 368
myodbc8a.dll!mysql_server_end()row 198
...
and the thread at the time of the crash is:
ID 2404 combase.dll!CRpcThreadCache:RpcWorkerThreadEntry

How to repeat:
Platform:windows 10
IDE:VS2019
Programming language:C++ MFC
Driver:odbc 8.0 ansi driver 8.00.21.00

There are two mysql community server,the one(mysql community 8.0.21 64bit) is on the local computer(windows 10 64bit),the other one(mysql community 8.0.21 64bit) is on another computer(windows 7 32bit)

the code for repeat:
Create a MFC Dialog project named test,and then import booast 1.75.0 lib into this project,and import msado15.dll into it

the properties of this project is as follows:
release platform:x64
Advanced>use of MFC:Use MFC in a static library
Advanced>Character Set:Use multi-byte character set
C/C++>Runtime Library:Multi-threaded(/MT)

add this code:#import "msado15.dll" no_namespace rename("EOF","adoEOF")
into the bottom of the stdafx.h file.

in the CTestApp.cpp file which is automatically created by the IDE,add these codes:
BOOL CTestApp::InitInstance()
{
        INITCOMMONCONTROLSEX InitCtrls;
	InitCtrls.dwSize = sizeof(InitCtrls);
	InitCtrls.dwICC = ICC_WIN95_CLASSES;
	InitCommonControlsEx(&InitCtrls);
	CWinApp::InitInstance();
//the follow codes are added by yourself
	AfxInitRichEdit2();
	AfxEnableControlContainer();
	::AfxOleInit();
//end
        ...
        return FALSE;
}
in the testDlg.h which is created by yourself:
class testDlg : public CDialogEx
{
	DECLARE_DYNAMIC(testDlg)
        public:
	testDlg(CWnd* pParent = nullptr); 
	virtual ~testDlg();

       #ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_DIALOG_TEST };
       #endif
    private:

    protected:
	virtual void DoDataExchange(CDataExchange* pDX);  
	DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
//the CRichEditCtrl is used for displaying some information 
    CRichEditCtrl mRichEdit;
    static UINT testSqlMasterDB(LPVOID lp);
    static UINT testSqlSlaveDB(LPVOID lp);
}
this is the testDlg.cpp file:
#include "stdafx.h"
#include "CtestAPP.h"
#include "testDlg.h"
#include "afxdialogex.h"
IMPLEMENT_DYNAMIC(testDlg, CDialogEx)
testDlg::testDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG_TEST, pParent)
{
	
}

testDlg::~testDlg()
{
	CoUninitialize();
}
void testDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_RICHEDIT21, mRichEdit);
}
BEGIN_MESSAGE_MAP(PAGE7, CDialogEx)
  ON_WM_TIMER()
END_MESSAGE_MAP()
UINT testDlg::testSqlMasterDB(LPVOID lp)
{
        _ConnectionPtr m_pConnection;
	_CommandPtr m_pCmd;
	m_pConnection.CreateInstance(__uuidof(Connection));
	int i = 0;
	m_pCmd.CreateInstance(__uuidof(Command));
	try
	{
		m_pConnection->ConnectionTimeout = 5;
		HRESULT hr = m_pConnection->Open("DSN=MySqlODBC;server=another_computer_IP;database=smartbracelet", user, password, adModeUnknown);
		m_pConnection->Close();
		m_pConnection.Release();
		return 0;
	}
	catch (_com_error e)
	{
		m_pConnection.Release();
		return -1;
	}
}
UINT testDlg::testSqlSlaveDB(LPVOID lp)
{
        _ConnectionPtr m_pConnection;
	_CommandPtr m_pCmd;
	m_pConnection.CreateInstance(__uuidof(Connection));
	int i = 0;
	m_pCmd.CreateInstance(__uuidof(Command));
	try
	{
		m_pConnection->ConnectionTimeout = 5;
		HRESULT hr = m_pConnection->Open("DSN=MySqlODBC;server=127.0.0.1;database=smartbracelet", user, password, adModeUnknown);
		m_pConnection->Close();
		m_pConnection.Release();
		return 0;
	}
	catch (_com_error e)
	{
		m_pConnection.Release();
		return -1;
	}
}
BOOL testDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
SetTimer(10010, 1000, NULL);
return TRUE;
}
void testDlg::OnTimer(UINT_PTR nIDEvent)
{
	if (nIDEvent == 10086)
	{
		AfxBeginThread(testSqlMasterDB, this->GetSafeHwnd());
		AfxBeginThread(testSqlSlaveDB, this->GetSafeHwnd());
	}
CDialogEx::OnTimer(nIDEvent);
}
[10 Jun 2021 8:16] Bob Lee
This is the screenshot when the error occurred

Attachment: 屏幕截图(304).png (image/png, text), 190.36 KiB.

[10 Jun 2021 8:31] Bob Lee
because I have a  CRichEditCtrl ctrl in the window,it is initialized by:
AfxInitRichEdit2();
AfxEnableControlContainer();
::AfxOleInit();

the AfxOleInit() function contains the function CoInitialize(),so that it is no need to add the function CoInitializeEx() to initialize ado driver
[1 Jul 2021 6:08] Bob Lee
Please connect to two mysql servers at the same time in the sample project, after a period of time (sometimes a few hours, sometimes a few days), the problem will appear
[12 Oct 2021 12:59] MySQL Verification Team
Hello Bob Lee,

Thank you for the details.
It seems that the zip folder containing project details missing some of the information. Please attach complete test case and steps to reproduce this issue at our end.

Regards,
Ashwini Patil
[13 Nov 2021 1:00] Bugs System
No feedback was provided for this bug for over a month, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".
[22 Jan 15:32] Shlomo Margalit
I recreated the problem on my machine (mysql 8.0.30 driver) using 4 threads that constantly connect to the same mysql server, disconnect from the server, and then wait 100 milliseconds.

the problem is in mysql_client_plugin_init, the function may be called twice by 2 different threads, this causes the plugin list to contain the same plugins twice (maybe more in some scenarios).
because the plugin sha256_password is in the plugin list twice, mysql_client_plugin_deinit will call its deinit twice, causing the mutex g_public_key_mutex to be used after it was already destroyed.

please note that in addition to this problem mysql_client_plugin_init memsets the plugins array outside the LOCK_load_client_plugin mutex, this can also cause problems.
[22 Jan 21:15] Shlomo Margalit
a good solution for this could be to take the initialzation code from mysql-server/libmysql/libmysql.cc, function mysql_server_init and
create a class that runs it in the constructor, then creating a static instance of it will be guaranteed to be thread safe and run only once by the c++ 11 and above standard.

the code that initializes the thread by calling my_thread_init should be executed always, it doesn't do anything if the thread is already initialized.