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>
This commit is contained in:
Ivan Solovev 2023-02-09 16:12:15 +01:00 committed by Qt Cherry-pick Bot
parent 390ce392c8
commit dba4d78441
2 changed files with 27 additions and 0 deletions

View File

@ -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

View File

@ -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