From 3b9629e8bdbf23bb793c88f967b2cfe2b8256278 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 8 Jan 2015 14:57:09 +0100 Subject: QStateMachine: replace a QHash key involving a QPointer Using QPointers (or any type that makes a QPointer part of its identity) as a key in any associative container is wrong. They get externally set to nullptr, violating the associative container's class invariants, which could lead to data corruption, even though bucket-based hash implementations are less susceptible than binary trees. To fix, write a new class that acts much like the old QPair,QByteArray>, but uses the QPointer only as a guard, not as part of its identity. To preseve identity, also saves the naked pointer originally passed and uses that for op== and qHash(). Change-Id: I4fa5a6bf86bad8fe7f5abe53d7c7f3ad3754d8d6 Reviewed-by: Thiago Macieira --- src/corelib/statemachine/qstatemachine.cpp | 7 ++++--- src/corelib/statemachine/qstatemachine_p.h | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'src/corelib/statemachine') diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 27c7b077c6..fa5e49c882 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -947,14 +947,15 @@ QList QStateMachinePrivate::restorablesToPropertyList(const QList result; QHash::const_iterator it; for (it = restorables.constBegin(); it != restorables.constEnd(); ++it) { - if (!it.key().first) { + const RestorableId &id = it.key(); + if (!id.object()) { // Property object was deleted continue; } #ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG - qDebug() << q_func() << ": restoring" << it.key().first << it.key().second << "to" << it.value(); + qDebug() << q_func() << ": restoring" << id.object() << id.proertyName() << "to" << it.value(); #endif - result.append(QPropertyAssignment(it.key().first, it.key().second, it.value(), /*explicitlySet=*/false)); + result.append(QPropertyAssignment(id.object(), id.propertyName(), it.value(), /*explicitlySet=*/false)); } return result; } diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index 465d8aff0d..51eed07548 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -189,7 +189,24 @@ public: void cancelAllDelayedEvents(); #ifndef QT_NO_PROPERTIES - typedef QPair, QByteArray> RestorableId; + class RestorableId { + QPointer guard; + QObject *obj; + QByteArray prop; + // two overloads because friends can't have default arguments + friend uint qHash(const RestorableId &key, uint seed) Q_DECL_NOEXCEPT_EXPR(noexcept(std::declval())) + { return qHash(qMakePair(key.obj, key.prop), seed); } + friend uint qHash(const RestorableId &key) Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(key, 0U))) + { return qHash(key, 0U); } + friend bool operator==(const RestorableId &lhs, const RestorableId &rhs) Q_DECL_NOTHROW + { return lhs.obj == rhs.obj && lhs.prop == rhs.prop; } + friend bool operator!=(const RestorableId &lhs, const RestorableId &rhs) Q_DECL_NOTHROW + { return !operator==(lhs, rhs); } + public: + explicit RestorableId(QObject *o, QByteArray p) Q_DECL_NOTHROW : guard(o), obj(o), prop(qMove(p)) {} + QObject *object() const Q_DECL_NOTHROW { return guard; } + QByteArray propertyName() const Q_DECL_NOTHROW { return prop; } + }; QHash > registeredRestorablesForState; bool hasRestorable(QAbstractState *state, QObject *object, const QByteArray &propertyName) const; QVariant savedValueForRestorable(const QList &exitedStates_sorted, -- cgit v1.2.3