Fix memory corruption via lambdas that capture local variables by reference.

The lambdas in ScriptEngine::fetchModuleSource() were referencing local stack variables by reference.
This could lead to un-expected results including memory corruption.
To workaround this issue the QTimer and QEventLoop variables are allocated on the heap and held onto by a shared_ptr.
This shared_ptr is passed to the lambda.

This will not result in cycles and should result in the QTimer and QEventLoop being destroyed when the BatchLoader
object they are connected to is deleted.
This commit is contained in:
Anthony J. Thibault 2018-06-27 16:57:57 -07:00
parent 7fb2e535ff
commit ac6fcf092c

View file

@ -1639,22 +1639,24 @@ QVariantMap ScriptEngine::fetchModuleSource(const QString& modulePath, const boo
loader->start(MAX_RETRIES);
if (!loader->isFinished()) {
QTimer monitor;
QEventLoop loop;
QObject::connect(loader, &BatchLoader::finished, this, [&monitor, &loop]{
monitor.stop();
loop.quit();
// This lambda can get called AFTER this local scope has completed.
// This is why we pass smart ptrs to the lambda instead of references to local variables.
auto monitor = std::make_shared<QTimer>();
auto loop = std::make_shared<QEventLoop>();
QObject::connect(loader, &BatchLoader::finished, this, [monitor, loop] {
monitor->stop();
loop->quit();
});
// this helps detect the case where stop() is invoked during the download
// but not seen in time to abort processing in onload()...
connect(&monitor, &QTimer::timeout, this, [this, &loop]{
connect(monitor.get(), &QTimer::timeout, this, [this, loop] {
if (isStopping()) {
loop.exit(-1);
loop->exit(-1);
}
});
monitor.start(500);
loop.exec();
monitor->start(500);
loop->exec();
}
loader->deleteLater();
return req;