Fix undefined behavior in QSharedPointer::create()
Initialize a deleter for a new object, created by QSharedPointer::create(), only after the object is actually constructed. [ChangeLog][QtCore][QSharedPointer] Fixed undefined behavior when creating an object with QSharedPointer::create() and its conscructor throws an exception. Task-number: QTBUG-49824 Change-Id: I07f77a78ff468d9b45b8ef133278e8cdd96a0647 Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>bb10
parent
5662234afa
commit
d1210281e4
|
|
@ -260,6 +260,7 @@ namespace QtSharedPointer {
|
|||
internalSafetyCheckRemove(self);
|
||||
deleter(self);
|
||||
}
|
||||
static void noDeleter(ExternalRefCountData *) { }
|
||||
|
||||
static inline ExternalRefCountData *create(T **ptr, DestroyerFn destroy)
|
||||
{
|
||||
|
|
@ -433,11 +434,13 @@ public:
|
|||
# else
|
||||
typename Private::DestroyerFn destroy = &Private::deleter;
|
||||
# endif
|
||||
typename Private::DestroyerFn noDestroy = &Private::noDeleter;
|
||||
QSharedPointer result(Qt::Uninitialized);
|
||||
result.d = Private::create(&result.value, destroy);
|
||||
result.d = Private::create(&result.value, noDestroy);
|
||||
|
||||
// now initialize the data
|
||||
new (result.data()) T(std::forward<Args>(arguments)...);
|
||||
result.d->destroyer = destroy;
|
||||
result.d->setQObjectShared(result.value, true);
|
||||
# ifdef QT_SHAREDPOINTER_TRACK_POINTERS
|
||||
internalSafetyCheckAdd(result.d, result.value);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,8 @@ private slots:
|
|||
void qvariantCast();
|
||||
void sharedFromThis();
|
||||
|
||||
void constructorThrow();
|
||||
|
||||
void threadStressTest_data();
|
||||
void threadStressTest();
|
||||
void validConstructs();
|
||||
|
|
@ -2594,6 +2596,54 @@ void tst_QSharedPointer::sharedFromThis()
|
|||
QCOMPARE(Data::destructorCounter, destructions + 6);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
class ThrowData: public Data
|
||||
{
|
||||
public:
|
||||
static int childDestructorCounter;
|
||||
static int childGenerationCounter;
|
||||
|
||||
ThrowData()
|
||||
{
|
||||
childGenerationCounter++;
|
||||
throw QStringLiteral("Dummy exception");
|
||||
}
|
||||
|
||||
~ThrowData()
|
||||
{
|
||||
childDestructorCounter++;
|
||||
}
|
||||
};
|
||||
int ThrowData::childDestructorCounter = 0;
|
||||
int ThrowData::childGenerationCounter = 0;
|
||||
#endif // !QT_NO_EXCEPTIONS
|
||||
|
||||
void tst_QSharedPointer::constructorThrow()
|
||||
{
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
int generation = Data::generationCounter;
|
||||
int destructorCounter = Data::destructorCounter;
|
||||
|
||||
int childGeneration = ThrowData::childGenerationCounter;
|
||||
int childDestructorCounter = ThrowData::childDestructorCounter;
|
||||
|
||||
QSharedPointer<ThrowData> ptr;
|
||||
QVERIFY_EXCEPTION_THROWN(ptr = QSharedPointer<ThrowData>::create(), QString);
|
||||
QVERIFY(ptr.isNull());
|
||||
QCOMPARE(ThrowData::childGenerationCounter, childGeneration + 1);
|
||||
// destructor should never be called, if a constructor throws
|
||||
// an exception
|
||||
QCOMPARE(ThrowData::childDestructorCounter, childDestructorCounter);
|
||||
|
||||
QCOMPARE(Data::generationCounter, generation + 1);
|
||||
// but base class constructor doesn't throw, so base class destructor
|
||||
// should be called
|
||||
QCOMPARE(Data::destructorCounter, destructorCounter + 1);
|
||||
#else
|
||||
QSKIP("Needs exceptions");
|
||||
#endif // !QT_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
namespace ReentrancyWhileDestructing {
|
||||
struct IB
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue