Check for null driver() before trying to exec()

QSqlQuery::exec() took for granted that it can dereference driver(),
which should be true for all sane usage; however, it should not crash
if used misguidedly. Added regression test, based on bug report's
reproducer, which crashes without the fix.

Fixes: QTBUG-100037
Pick-to: 6.3 6.2 5.15 5.12
Change-Id: I94600bc60f89e82a1121b418144006a683921a38
Reviewed-by: Andy Shaw <andy.shaw@qt.io>
This commit is contained in:
Edward Welbourne 2022-01-24 12:44:38 +01:00
parent f172b964f6
commit 78eac57f3d
2 changed files with 37 additions and 2 deletions

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtSql module of the Qt Toolkit.
@ -406,6 +406,10 @@ bool QSqlQuery::exec(const QString& query)
QElapsedTimer t;
t.start();
#endif
if (!driver()) {
qWarning("QSqlQuery::exec: called before driver has been set up");
return false;
}
if (d->ref.loadRelaxed() != 1) {
bool fo = isForwardOnly();
*this = QSqlQuery(driver()->createResult());

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@ -70,6 +70,8 @@ private slots:
void query_exec();
void execErrorRecovery_data() { generic_data(); }
void execErrorRecovery();
void prematureExec_data() { generic_data(); }
void prematureExec();
void first_data() { generic_data(); }
void first();
void next_data() { generic_data(); }
@ -2878,6 +2880,35 @@ void tst_QSqlQuery::execErrorRecovery()
QVERIFY_SQL( q, exec() );
}
void tst_QSqlQuery::prematureExec()
{
QFETCH(QString, dbName);
// We only want the engine name, for addDatabase():
int cut = dbName.indexOf(QChar('@'));
if (cut < 0)
QSKIP("Failed to parse database type out of name");
dbName.truncate(cut);
cut = dbName.indexOf(QChar('_'));
if (cut >= 0)
dbName = dbName.sliced(cut + 1);
auto db = QSqlDatabase::addDatabase(dbName);
QSqlQuery q(db);
QTest::ignoreMessage(QtWarningMsg,
"QSqlDatabasePrivate::removeDatabase: connection "
"'qt_sql_default_connection' is still in use, all "
"queries will cease to work.");
QTest::ignoreMessage(QtWarningMsg,
"QSqlDatabasePrivate::addDatabase: duplicate connection name "
"'qt_sql_default_connection', old connection removed.");
auto otherDb = QSqlDatabase::addDatabase(dbName);
QTest::ignoreMessage(QtWarningMsg, "QSqlQuery::exec: called before driver has been set up");
// QTBUG-100037: shouldn't crash !
QVERIFY(!q.exec("select stuff from TheVoid"));
}
void tst_QSqlQuery::lastInsertId()
{
QFETCH( QString, dbName );