From 73e173d73cffe95837490a86257cb82d9e150e3a Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 1 Jun 2022 16:07:47 +0800 Subject: [PATCH] DialogButtonBox: test item deletion order Ensure that items declared before and after the control itself do not cause heap-use-after-frees due to deletion order. Task-number: QTBUG-100396 Pick-to: 6.2 6.3 Change-Id: I3989bf1b9fc64b4ec86f241de2cb8bcd05c2f89d Reviewed-by: Ulf Hermann --- src/quicktemplates2/qquickdialogbuttonbox.cpp | 7 ++ .../controls/data/tst_dialogbuttonbox.qml | 82 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/src/quicktemplates2/qquickdialogbuttonbox.cpp b/src/quicktemplates2/qquickdialogbuttonbox.cpp index a4197692e4..0c1c1cffac 100644 --- a/src/quicktemplates2/qquickdialogbuttonbox.cpp +++ b/src/quicktemplates2/qquickdialogbuttonbox.cpp @@ -497,6 +497,13 @@ QQuickDialogButtonBox::QQuickDialogButtonBox(QQuickItem *parent) QQuickDialogButtonBox::~QQuickDialogButtonBox() { + Q_D(QQuickDialogButtonBox); + // QQuickContainerPrivate does call this, but as our type information has already been + // destroyed by that point (since this destructor has already run), it won't call our + // implementation. So, we need to make sure our implementation is called. If we don't do this, + // the listener we installed on the contentItem won't get removed, possibly resulting in + // heap-use-after-frees. + contentItemChange(nullptr, d->contentItem); } /*! diff --git a/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml b/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml index 7d4c5d9e4d..57216ed11c 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml @@ -528,4 +528,86 @@ TestCase { control.destroy() } } + + Component { + id: contentItemDeletionOrder1 + + Item { + objectName: "parentItem" + + Item { + id: item + objectName: "contentItem" + } + DialogButtonBox { + objectName: "control" + contentItem: item + } + } + } + + Component { + id: contentItemDeletionOrder2 + + Item { + objectName: "parentItem" + + DialogButtonBox { + objectName: "control" + contentItem: item + } + Item { + id: item + objectName: "contentItem" + } + } + } + + function test_contentItemDeletionOrder() { + var control1 = createTemporaryObject(contentItemDeletionOrder1, testCase) + verify(control1) + var control2 = createTemporaryObject(contentItemDeletionOrder2, testCase) + verify(control2) + } + + Component { + id: backgroundDeletionOrder1 + + Item { + objectName: "parentItem" + + Item { + id: item + objectName: "backgroundItem" + } + DialogButtonBox { + objectName: "control" + background: item + } + } + } + + Component { + id: backgroundDeletionOrder2 + + Item { + objectName: "parentItem" + + DialogButtonBox { + objectName: "control" + background: item + } + Item { + id: item + objectName: "backgroundItem" + } + } + } + + function test_backgroundDeletionOrder() { + var control1 = createTemporaryObject(backgroundDeletionOrder1, testCase) + verify(control1) + var control2 = createTemporaryObject(backgroundDeletionOrder2, testCase) + verify(control2) + } }