Fix invalid vptr during destruction of animations

Fixes UBSAN warnings about objects used after partial destruction.

Change-Id: Iceea083a77d47335ef595c0ff97b87f35f42e56f
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Allan Sandfeld Jensen 2019-05-14 12:37:40 +02:00
parent cf7d990a48
commit 48b707224e
4 changed files with 30 additions and 2 deletions

View File

@ -1067,6 +1067,8 @@ QAbstractAnimation::~QAbstractAnimation()
if (oldState == QAbstractAnimation::Running)
QAnimationTimer::unregisterAnimation(this);
}
if (d->group)
d->group->removeAnimation(this);
}
/*!

View File

@ -113,6 +113,11 @@ QAnimationGroup::QAnimationGroup(QAnimationGroupPrivate &dd, QObject *parent)
*/
QAnimationGroup::~QAnimationGroup()
{
Q_D(QAnimationGroup);
// We need to clear the animations now while we are still a valid QAnimationGroup.
// If we wait until ~QObject() the QAbstractAnimation's pointer back to us would
// point to a QObject, not a valid QAnimationGroup.
d->clear(true);
}
/*!
@ -256,7 +261,7 @@ QAbstractAnimation *QAnimationGroup::takeAnimation(int index)
void QAnimationGroup::clear()
{
Q_D(QAnimationGroup);
qDeleteAll(d->animations);
d->clear(false);
}
/*!
@ -284,6 +289,24 @@ bool QAnimationGroup::event(QEvent *event)
return QAbstractAnimation::event(event);
}
void QAnimationGroupPrivate::clear(bool onDestruction)
{
const QList<QAbstractAnimation *> animationsCopy = animations; // taking a copy
animations.clear();
// Clearing backwards so the indices doesn't change while we remove animations.
for (int i = animationsCopy.count() - 1; i >= 0; --i) {
QAbstractAnimation *animation = animationsCopy.at(i);
animation->setParent(nullptr);
QAbstractAnimationPrivate::get(animation)->group = nullptr;
// If we are in ~QAnimationGroup() it is not safe to called the virtual
// animationRemoved method, which can still be a method in a
// QAnimationGroupPrivate derived class that assumes q_ptr is still
// a valid derived class of QAnimationGroup.
if (!onDestruction)
animationRemoved(i, animation);
delete animation;
}
}
void QAnimationGroupPrivate::animationRemoved(int index, QAbstractAnimation *)
{

View File

@ -73,6 +73,8 @@ public:
virtual void animationInsertedAt(int) { }
virtual void animationRemoved(int, QAbstractAnimation *);
void clear(bool onDestruction);
void disconnectUncontrolledAnimation(QAbstractAnimation *anim)
{
//0 for the signal here because we might be called from the animation destructor

View File

@ -532,7 +532,8 @@ void QSequentialAnimationGroupPrivate::animationRemoved(int index, QAbstractAnim
Q_Q(QSequentialAnimationGroup);
QAnimationGroupPrivate::animationRemoved(index, anim);
Q_ASSERT(currentAnimation); // currentAnimation should always be set
if (!currentAnimation)
return;
if (actualDuration.size() > index)
actualDuration.removeAt(index);