diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2016-07-21 16:30:57 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2016-07-23 05:18:59 +0000 |
commit | 4c1a51006e5936dc69e3373539787120092f6719 (patch) | |
tree | a8979a9e61604958bce581ffd38361b0265510c3 /src | |
parent | 8a8b826cad197cf39e50f88521d0c40dc9c9344d (diff) |
Improve robustness of qml disk caching
Perform various basic checks before proceeding to load an existing cache file,
including the qt version, architecture, data structure version and others.
Change-Id: Ie822b056e944ac120643aad260e97f62616688bf
Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 55 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 15 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 10 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler_p.h | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 2 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth.cpp | 4 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth_p.h | 9 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_p.cpp | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_p.h | 6 | ||||
-rw-r--r-- | src/qml/jit/qv4isel_masm.cpp | 4 | ||||
-rw-r--r-- | src/qml/jit/qv4isel_masm_p.h | 11 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 4 |
12 files changed, 96 insertions, 30 deletions
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 1bcd6892d2..965924262d 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -331,20 +331,56 @@ bool CompilationUnit::saveToDisk(QString *errorString) return true; } -bool CompilationUnit::loadFromDisk(const QUrl &url, QString *errorString) +bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) { if (!url.isLocalFile()) { *errorString = QStringLiteral("File has to be a local file."); return false; } - QScopedPointer<QFile> cacheFile(new QFile(url.toLocalFile() + QLatin1Char('c'))); + const QString sourcePath = url.toLocalFile(); + QScopedPointer<QFile> cacheFile(new QFile(sourcePath + QLatin1Char('c'))); if (!cacheFile->open(QIODevice::ReadOnly)) { *errorString = cacheFile->errorString(); return false; } + { + CompiledData::Unit header; + qint64 bytesRead = cacheFile->read(reinterpret_cast<char *>(&header), sizeof(header)); + + if (bytesRead != sizeof(header)) { + *errorString = QStringLiteral("File too small for the header fields"); + return false; + } + + if (strncmp(header.magic, CompiledData::magic_str, sizeof(header.magic))) { + *errorString = QStringLiteral("Magic bytes in the header do not match"); + return false; + } + + if (header.version != quint32(QV4_DATA_STRUCTURE_VERSION)) { + *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header.version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); + return false; + } + + if (header.qtVersion != quint32(QT_VERSION)) { + *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header.qtVersion, 0, 16).arg(QT_VERSION, 0, 16); + return false; + } + + { + QFileInfo sourceCode(sourcePath); + if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header.sourceTimeStamp) { + *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); + return false; + } + } + + } + // Data structure and qt version matched, so now we can access the rest of the file safely. + uchar *cacheData = cacheFile->map(/*offset*/0, cacheFile->size()); if (!cacheData) { *errorString = cacheFile->errorString(); @@ -354,13 +390,22 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, QString *errorString) QScopedValueRollback<const Unit *> dataPtrChange(data, reinterpret_cast<const Unit *>(cacheData)); { - QFileInfo sourceCode(url.toLocalFile()); - if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != data->sourceTimeStamp) { - *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); + const QString foundArchitecture = stringAt(data->architectureIndex); + const QString expectedArchitecture = QSysInfo::buildAbi(); + if (foundArchitecture != expectedArchitecture) { + *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture); return false; } } + { + const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); + const QString expectedCodeGenerator = iselFactory->codeGeneratorName; + if (foundCodeGenerator != expectedCodeGenerator) { + *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator); + return false; + } + } if (!memoryMapCode(errorString)) return false; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 20b68026e9..4153259760 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -70,6 +70,9 @@ QT_BEGIN_NAMESPACE +// Bump this whenever the compiler data structures change in an incompatible way. +#define QV4_DATA_STRUCTURE_VERSION 0x01 + class QIODevice; class QQmlPropertyCache; class QQmlPropertyData; @@ -88,6 +91,7 @@ struct Function; } struct Function; +class EvalISelFactory; namespace CompiledData { @@ -597,11 +601,16 @@ static const char magic_str[] = "qv4cdata"; struct Unit { + // DO NOT CHANGE THESE FIELDS EVER char magic[8]; - LEInt16 architecture; - LEInt16 version; + LEUInt32 version; + LEUInt32 qtVersion; LEInt64 sourceTimeStamp; LEUInt32 unitSize; // Size of the Unit and any depending data. + // END DO NOT CHANGE THESE FIELDS EVER + + LEUInt32 architectureIndex; // string index to QSysInfo::buildAbi() + LEUInt32 codeGeneratorIndex; enum : unsigned int { IsJavascript = 0x1, @@ -880,7 +889,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount void destroy() Q_DECL_OVERRIDE; bool saveToDisk(QString *errorString); - bool loadFromDisk(const QUrl &url, QString *errorString); + bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString); protected: virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 5d13734247..768a4ffcd3 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -352,17 +352,19 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i } } -QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset) const +QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset) { CompiledData::Unit unit; memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic)); - unit.architecture = 0; // ### unit.flags = QV4::CompiledData::Unit::IsJavascript; - unit.version = 1; - unit.functionTableSize = irModule->functions.size(); + unit.version = QV4_DATA_STRUCTURE_VERSION; + unit.qtVersion = QT_VERSION; + unit.architectureIndex = registerString(QSysInfo::buildAbi()); + unit.codeGeneratorIndex = registerString(codeGeneratorName); quint32 nextOffset = sizeof(CompiledData::Unit); + unit.functionTableSize = irModule->functions.size(); unit.offsetToFunctionTable = nextOffset; nextOffset += unit.functionTableSize * sizeof(uint); diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 4b67fe8600..49b8664513 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -118,8 +118,9 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { void writeFunction(char *f, IR::Function *irFunction) const; StringTableGenerator stringTable; + QString codeGeneratorName; private: - CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset) const; + CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset); IR::Module *irModule; diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index e95a7f7046..112003ebb8 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -174,6 +174,8 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace Moth { + // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h + struct Param { // Params are looked up as follows: // Constant: 0 diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 283948727b..dc803f1ee2 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -313,8 +313,8 @@ protected: }; } // anonymous namespace -InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - : EvalInstructionSelection(execAllocator, module, jsGenerator) +InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) + : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) , qmlEngine(qmlEngine) , _block(0) , _codeStart(0) diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index eaca299c94..ea323497ef 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -80,7 +80,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); ~InstructionSelection(); virtual void run(int functionIndex); @@ -205,10 +205,11 @@ private: class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: + ISelFactory() : EvalISelFactory(QStringLiteral("moth")) {} virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } - virtual bool jitCompileRegexps() const + EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL + { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); } + bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL { return false; } QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE; diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 0ae08160ab..d97eec5e1d 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -52,7 +52,7 @@ using namespace QV4; using namespace QV4::IR; -EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) +EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) : useFastLookups(true) , useTypeInference(true) , executableAllocator(execAllocator) @@ -67,6 +67,7 @@ EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *exe Q_ASSERT(execAllocator); #endif Q_ASSERT(module); + jsGenerator->codeGeneratorName = iselFactory->codeGeneratorName; } EvalInstructionSelection::~EvalInstructionSelection() diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index d93c0893ae..a3fa80b6f0 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -65,13 +65,14 @@ class QQmlEnginePrivate; namespace QV4 { +class EvalISelFactory; class ExecutableAllocator; struct Function; class Q_QML_PRIVATE_EXPORT EvalInstructionSelection { public: - EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); virtual ~EvalInstructionSelection() = 0; QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(bool generateUnitData = true); @@ -104,10 +105,13 @@ protected: class Q_QML_PRIVATE_EXPORT EvalISelFactory { public: + EvalISelFactory(const QString &codeGeneratorName) : codeGeneratorName(codeGeneratorName) {} virtual ~EvalISelFactory() = 0; virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0; virtual bool jitCompileRegexps() const = 0; virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() = 0; + + const QString codeGeneratorName; }; namespace IR { diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index bde2c59526..4066ab213c 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -258,8 +258,8 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) return codeRef; } -InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator) - : EvalInstructionSelection(execAllocator, module, jsGenerator) +InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) + : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) , _block(0) , _as(0) , compilationUnit(new CompilationUnit) diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 7616ba147f..4b35a72e01 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -76,7 +76,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory); ~InstructionSelection(); virtual void run(int functionIndex); @@ -285,12 +285,13 @@ private: class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: + ISelFactory() : EvalISelFactory(QStringLiteral("jit")) {} virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } - virtual bool jitCompileRegexps() const + EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL + { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); } + bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL { return true; } - QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE; + QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE Q_DECL_FINAL; }; } // end of namespace JIT diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 52fed14ecb..851a18fa5f 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2057,7 +2057,7 @@ bool QQmlTypeData::tryLoadFromDiskCache() QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading(); { QString error; - if (!unit->loadFromDisk(url(), &error)) { + if (!unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) { qDebug() << "Error loading" << url().toString() << "from disk cache:" << error; return false; } @@ -2840,7 +2840,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) if (diskCache() && !forceDiskCacheRefresh()) { QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading(); QString error; - if (unit->loadFromDisk(url(), &error)) { + if (unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) { initializeFromCompilationUnit(unit); return; } else { |