diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp index eedfd7ceeb9..ed46052fa7b 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 9234204bdcb..deba56d3b17 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 future; + bool continuationIsRun = false; + bool cancelCalled = false; + { + QPromise 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