Clear the current thread data for the main thread

This avoids crashes accessing deleted memory when creating a QObject
after the last QObject had been deleted, like a qDebug() in global
destructors.

==41000== Invalid read of size 4
==41000==    at 0x5F01ED5: bool QBasicAtomicOps<4>::ref<int>(int&) (qatomic_x86.h:208)
==41000==    by 0x5F01309: QBasicAtomicInteger<int>::ref() (qbasicatomic.h:147)
==41000==    by 0x5F24051: QThreadData::ref() (qthread.cpp:100)
==41000==    by 0x614A984: QObject::QObject(QObject*) (qobject.cpp:681)
==41000==  Address 0x6ee73f0 is 0 bytes inside a block of size 152 free'd
==41000==    at 0x4A0736C: operator delete(void*) (vg_replace_malloc.c:480)
==41000==    by 0x5F240BF: QThreadData::deref() (qthread.cpp:109)
==41000==    by 0x6113F6B: QCoreApplicationData::~QCoreApplicationData() (qcoreapplication.cpp:268)

The comment right above the change in qthread.cpp looks eerily similar
to the problem I'm trying to fix. However, the actual change that
introduced the change is not in the Qt public history, so we can't
know for sure what the problem was then.

Change-Id: I0dba895b041fe6cf81e6f8939ca85035cd00aad1
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
This commit is contained in:
Thiago Macieira 2013-01-18 14:39:00 +08:00 committed by The Qt Project
parent 0666975d6c
commit 950b35cf97
6 changed files with 24 additions and 7 deletions

View File

@ -300,13 +300,6 @@ struct QCoreApplicationData {
#ifndef QT_NO_LIBRARY
delete app_libpaths;
#endif
// cleanup the QAdoptedThread created for the main() thread
if (QCoreApplicationPrivate::theMainThread) {
QThreadData *data = QThreadData::get2(QCoreApplicationPrivate::theMainThread);
QCoreApplicationPrivate::theMainThread = 0;
data->deref(); // deletes the data and the adopted thread
}
}
#ifdef Q_OS_BLACKBERRY

View File

@ -76,6 +76,7 @@ QThreadData::~QThreadData()
// the problem...
if (this->thread == QCoreApplicationPrivate::theMainThread) {
QCoreApplicationPrivate::theMainThread = 0;
QThreadData::clearCurrentThreadData();
}
QThread *t = thread;

View File

@ -223,6 +223,7 @@ public:
~QThreadData();
static QThreadData *current();
static void clearCurrentThreadData();
static QThreadData *get2(QThread *thread)
{ Q_ASSERT_X(thread != 0, "QThread", "internal error"); return thread->d_func()->data; }

View File

@ -204,6 +204,11 @@ static void clear_thread_data()
pthread_setspecific(current_thread_data_key, 0);
}
void QThreadData::clearCurrentThreadData()
{
clear_thread_data();
}
QThreadData *QThreadData::current()
{
QThreadData *data = get_thread_data();

View File

@ -96,6 +96,11 @@ Q_DESTRUCTOR_FUNCTION(qt_free_tls)
/*
QThreadData
*/
void QThreadData::clearCurrentThreadData()
{
TlsSetValue(qt_current_thread_data_tls_index, 0);
}
QThreadData *QThreadData::current()
{
qt_create_tls();

View File

@ -769,6 +769,18 @@ void tst_QCoreApplication::testQuitLock()
app.exec();
}
static void createQObjectOnDestruction()
{
// Make sure that we can create a QObject after the last QObject has been
// destroyed (especially after QCoreApplication has).
//
// Before the fixes, this would cause a dangling pointer dereference. If
// the problem comes back, it's possible that the following causes no
// effect.
QObject obj;
obj.thread()->setProperty("testing", 1);
}
Q_DESTRUCTOR_FUNCTION(createQObjectOnDestruction)
QTEST_APPLESS_MAIN(tst_QCoreApplication)
#include "tst_qcoreapplication.moc"