summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2023-02-09 16:12:15 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-02-15 14:12:24 +0000
commit65b4031e7ac457da626a3d7581c835130280a656 (patch)
tree5a5d1869f0bfc6a3d369d4794616447a3bdce4da
parent86c9c914c2150936d7dc9c6e4221932d5873a12f (diff)
downloadqtbase-65b4031e7ac457da626a3d7581c835130280a656.tar.gz
QFuture: fix continuation cleanup
Not clearing the continuationData could lead to use-after-free when there is an attempt to cancel an already finished future, which belongs to an already-destroyed promise. This patch fixes it be explicitly resetting continuationData to nullptr in the clearContinuation() method, which is called from the QPromise destructor. Task-number: QTBUG-103514 Change-Id: I6418b3f5ad04f2fdc13a196ae208009eaa5de367 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io> (cherry picked from commit b34bea5e96370986ea5dfc499fc2ec6366fda627) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/corelib/thread/qfutureinterface.cpp1
-rw-r--r--tests/auto/corelib/thread/qfuture/tst_qfuture.cpp26
2 files changed, 27 insertions, 0 deletions
diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp
index eedfd7ceeb..ed46052fa7 100644
--- a/src/corelib/thread/qfutureinterface.cpp
+++ b/src/corelib/thread/qfutureinterface.cpp
@@ -847,6 +847,7 @@ void QFutureInterfaceBase::cleanContinuation()
QMutexLocker lock(&d->continuationMutex);
d->continuation = nullptr;
d->continuationState = QFutureInterfaceBasePrivate::Cleaned;
+ d->continuationData = nullptr;
}
void QFutureInterfaceBase::runContinuation() const
diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
index d6c214f534..21408095de 100644
--- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
+++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
@@ -221,6 +221,7 @@ private slots:
void whenAnyDifferentTypesWithFailed();
void continuationsDontLeak();
+ void cancelAfterFinishWithContinuations();
void unwrap();
@@ -4721,6 +4722,31 @@ void tst_QFuture::continuationsDontLeak()
QCOMPARE(InstanceCounter::count, 0);
}
+// This test checks that we do not get use-after-free
+void tst_QFuture::cancelAfterFinishWithContinuations()
+{
+ QFuture<void> future;
+ bool continuationIsRun = false;
+ bool cancelCalled = false;
+ {
+ QPromise<void> promise;
+ future = promise.future();
+
+ future.then([&continuationIsRun]() {
+ continuationIsRun = true;
+ }).onCanceled([&cancelCalled]() {
+ cancelCalled = true;
+ });
+
+ promise.start();
+ promise.finish();
+ }
+
+ QVERIFY(continuationIsRun);
+ future.cancel();
+ QVERIFY(!cancelCalled);
+}
+
void tst_QFuture::unwrap()
{
// The nested future succeeds