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:
parent
390ce392c8
commit
dba4d78441
@ -847,6 +847,7 @@ void QFutureInterfaceBase::cleanContinuation()
|
|||||||
QMutexLocker lock(&d->continuationMutex);
|
QMutexLocker lock(&d->continuationMutex);
|
||||||
d->continuation = nullptr;
|
d->continuation = nullptr;
|
||||||
d->continuationState = QFutureInterfaceBasePrivate::Cleaned;
|
d->continuationState = QFutureInterfaceBasePrivate::Cleaned;
|
||||||
|
d->continuationData = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QFutureInterfaceBase::runContinuation() const
|
void QFutureInterfaceBase::runContinuation() const
|
||||||
|
@ -221,6 +221,7 @@ private slots:
|
|||||||
void whenAnyDifferentTypesWithFailed();
|
void whenAnyDifferentTypesWithFailed();
|
||||||
|
|
||||||
void continuationsDontLeak();
|
void continuationsDontLeak();
|
||||||
|
void cancelAfterFinishWithContinuations();
|
||||||
|
|
||||||
void unwrap();
|
void unwrap();
|
||||||
|
|
||||||
@ -4721,6 +4722,31 @@ void tst_QFuture::continuationsDontLeak()
|
|||||||
QCOMPARE(InstanceCounter::count, 0);
|
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()
|
void tst_QFuture::unwrap()
|
||||||
{
|
{
|
||||||
// The nested future succeeds
|
// The nested future succeeds
|
||||||
|
Loading…
x
Reference in New Issue
Block a user