QODBC: Fix crash when a prepared statement is deleted after the db was removed

When a prepared statement is still alive after the database was removed
with QSqlDatabase::removeDatabase(), the cleanup routine is trying to
access the driver which is no longer alive which results in a crash.

Fixes: QTBUG-79019
Change-Id: I4630e3b947a12b23ed062f015abc373fc0e246c1
Reviewed-by: Andy Shaw <andy.shaw@qt.io>
This commit is contained in:
Christian Ehrlicher 2019-10-06 12:56:34 +02:00
parent 2ec93e29f7
commit ed7dd9a6ed
2 changed files with 18 additions and 19 deletions

View File

@ -221,18 +221,18 @@ public:
int disconnectCount; int disconnectCount;
bool hasSQLFetchScroll; bool hasSQLFetchScroll;
bool isStmtHandleValid(); bool isStmtHandleValid() const;
void updateStmtHandleState(); void updateStmtHandleState();
}; };
bool QODBCResultPrivate::isStmtHandleValid() bool QODBCResultPrivate::isStmtHandleValid() const
{ {
return disconnectCount == drv_d_func()->disconnectCount; return drv_d_func() && disconnectCount == drv_d_func()->disconnectCount;
} }
void QODBCResultPrivate::updateStmtHandleState() void QODBCResultPrivate::updateStmtHandleState()
{ {
disconnectCount = drv_d_func()->disconnectCount; disconnectCount = drv_d_func() ? drv_d_func()->disconnectCount : 0;
} }
static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode = 0) static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode = 0)
@ -975,7 +975,7 @@ QODBCResult::QODBCResult(const QODBCDriver *db)
QODBCResult::~QODBCResult() QODBCResult::~QODBCResult()
{ {
Q_D(QODBCResult); Q_D(QODBCResult);
if (d->hStmt && d->isStmtHandleValid() && driver()->isOpen()) { if (d->hStmt && d->isStmtHandleValid() && driver() && driver()->isOpen()) {
SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt); SQLRETURN r = SQLFreeHandle(SQL_HANDLE_STMT, d->hStmt);
if (r != SQL_SUCCESS) if (r != SQL_SUCCESS)
qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ") qSqlWarning(QLatin1String("QODBCDriver: Unable to free statement handle ")

View File

@ -196,8 +196,7 @@ private slots:
void task_250026_data() { generic_data("QODBC"); } void task_250026_data() { generic_data("QODBC"); }
void task_250026(); void task_250026();
void task_205701_data() { generic_data("QMYSQL"); } void crashQueryOnCloseDatabase();
void task_205701();
void task_233829_data() { generic_data("QPSQL"); } void task_233829_data() { generic_data("QPSQL"); }
void task_233829(); void task_233829();
@ -311,6 +310,8 @@ void tst_QSqlQuery::init()
void tst_QSqlQuery::cleanup() void tst_QSqlQuery::cleanup()
{ {
if (QTest::currentTestFunction() == QLatin1String("crashQueryOnCloseDatabase"))
return;
QFETCH( QString, dbName ); QFETCH( QString, dbName );
QSqlDatabase db = QSqlDatabase::database( dbName ); QSqlDatabase db = QSqlDatabase::database( dbName );
CHECK_DATABASE( db ); CHECK_DATABASE( db );
@ -3448,19 +3449,17 @@ void tst_QSqlQuery::task_250026()
QCOMPARE( q.value( 0 ).toString().length(), data1026.length() ); QCOMPARE( q.value( 0 ).toString().length(), data1026.length() );
} }
void tst_QSqlQuery::task_205701() void tst_QSqlQuery::crashQueryOnCloseDatabase()
{ {
QSqlDatabase qsdb = QSqlDatabase::addDatabase("QMYSQL", "atest"); for (const auto &dbName : qAsConst(dbs.dbNames)) {
qsdb.setHostName("test"); QSqlDatabase clonedDb = QSqlDatabase::cloneDatabase(
qsdb.setDatabaseName("test"); QSqlDatabase::database(dbName), "crashTest");
qsdb.setUserName("test"); qDebug() << "Testing crash in sqlquery dtor for driver" << clonedDb.driverName();
qsdb.setPassword("test"); QVERIFY(clonedDb.open());
qsdb.open(); QSqlQuery q(clonedDb);
clonedDb.close();
// { QSqlDatabase::removeDatabase("crashTest");
QSqlQuery query(qsdb); }
// }
QSqlDatabase::removeDatabase("atest");
} }
#ifdef NOT_READY_YET #ifdef NOT_READY_YET