diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2022-07-15 12:10:20 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2022-07-21 07:32:06 +0000 |
commit | 65c5636ad8a03fb4abd71160cbbfab49f8d8d08e (patch) | |
tree | 6a2b10e8402e448ab98ea0777aa59f35f30b638b /src/quick | |
parent | 577fb29b42d76b55290bf2c38ed07d2eaa24c9df (diff) |
Avoid use-after-free in QQuickPathView
When releasing an item we always need to unregister the change listener,
even if the model is already gone. The model is a public property and a
QPointer. Anything can happen to it behind our back.
Furthermore, we apparently don't own items and itemCache. Therefore, we
have to listen to their "destroy" events and remove them from the list
when those are triggered. Otherwise we operate on dangling pointers
when we later releaseItem() from the dtor.
Change-Id: I662b0d70b8b06d15b504c6fe1fc33384b1ae12d9
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 1b8109fc1476e255d1fdb571da884215d9791cec)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'src/quick')
-rw-r--r-- | src/quick/items/qquickpathview.cpp | 10 | ||||
-rw-r--r-- | src/quick/items/qquickpathview_p_p.h | 6 |
2 files changed, 13 insertions, 3 deletions
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 4052051904..9be141827c 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -146,7 +146,8 @@ QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool async) item->setParentItem(q); requestedIndex = -1; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry); + itemPrivate->addItemChangeListener( + this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); } inRequest = false; return item; @@ -199,11 +200,14 @@ void QQuickPathView::initItem(int index, QObject *object) void QQuickPathViewPrivate::releaseItem(QQuickItem *item) { - if (!item || !model) + if (!item) return; qCDebug(lcItemViewDelegateLifecycle) << "release" << item; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry); + itemPrivate->removeItemChangeListener( + this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + if (!model) + return; QQmlInstanceModel::ReleaseFlags flags = model->release(item); if (!flags) { // item was not destroyed, and we no longer reference it. diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h index 6c964bcb83..b245c6b5ee 100644 --- a/src/quick/items/qquickpathview_p_p.h +++ b/src/quick/items/qquickpathview_p_p.h @@ -88,6 +88,12 @@ public: } } + void itemDestroyed(QQuickItem *item) override + { + if (!items.removeOne(item)) + itemCache.removeOne(item); + } + void scheduleLayout() { Q_Q(QQuickPathView); if (!layoutScheduled) { |