From 80592dcf0df3bce6177e8e051e88af7b9e6b3f6e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Mar 2018 16:33:01 +0100 Subject: Tighten QML cache version checking Don't just include the "compile hash" of QtQml in the dependencies hash of QML files but use a dedicated field in the data structure, that we will also fill in when generating cache files ahead of time. This ensures that AOT generated cache files are considered invalid even when switching between different sha1s of declarative. Task-number: QTBUG-66986 Change-Id: I3d8ee103fd1a33a5b4c4576b3a2703fcd09712dd Reviewed-by: Erik Verbruggen --- src/qml/compiler/compiler.pri | 2 -- src/qml/compiler/qv4compileddata.cpp | 45 +++++++++--------------------------- src/qml/compiler/qv4compileddata_p.h | 6 +++-- src/qml/compiler/qv4compiler.cpp | 4 ++++ 4 files changed, 19 insertions(+), 38 deletions(-) (limited to 'src/qml/compiler') diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 2ca0c39acc..95096db51d 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -40,8 +40,6 @@ SOURCES += \ unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp - -qtConfig(private_tests):qtConfig(dlopen): QMAKE_USE_PRIVATE += libdl } gcc { diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6220c1bc11..8dcc068a06 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -70,18 +70,14 @@ #include -#if defined(QT_BUILD_INTERNAL) -#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) -#include -#endif -#endif - QT_BEGIN_NAMESPACE namespace QV4 { namespace CompiledData { +static_assert(sizeof(Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); + #if !defined(V4_BOOTSTRAP) static QString cacheFilePath(const QUrl &url) { @@ -686,32 +682,6 @@ void ResolvedTypeReference::doDynamicTypeCheck() isFullyDynamicType = qtTypeInherits(mo); } -static QByteArray ownLibraryChecksum() -{ - static QByteArray libraryChecksum; - static bool checksumInitialized = false; - if (checksumInitialized) - return libraryChecksum; - checksumInitialized = true; -#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_DYNAMIC_CAST) && QT_CONFIG(dlopen) - // This is a bit of a hack to make development easier. When hacking on the code generator - // the cache files may end up being re-used. To avoid that we also add the checksum of - // the QtQml library. - Dl_info libInfo; - if (dladdr(reinterpret_cast(&ownLibraryChecksum), &libInfo) != 0) { - QFile library(QFile::decodeName(libInfo.dli_fname)); - if (library.open(QIODevice::ReadOnly)) { - QCryptographicHash hash(QCryptographicHash::Md5); - hash.addData(&library); - libraryChecksum = hash.result(); - } - } -#else - libraryChecksum = QByteArray(QML_COMPILE_HASH); -#endif - return libraryChecksum; -} - bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const { for (auto it = constBegin(), end = constEnd(); it != end; ++it) { @@ -719,8 +689,6 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *e return false; } - hash->addData(ownLibraryChecksum()); - return true; } @@ -785,6 +753,15 @@ bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) } } +#if defined(QML_COMPILE_HASH) + if (qstrcmp(QML_COMPILE_HASH, libraryVersionHash) != 0) { + *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match"); + return false; + } +#else +#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" +#endif + return true; #else Q_UNUSED(expectedSourceTimeStamp) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 49360bc3f1..1df9d6794f 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -72,7 +72,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x18 +#define QV4_DATA_STRUCTURE_VERSION 0x19 class QIODevice; class QQmlPropertyCache; @@ -688,6 +688,8 @@ struct Unit quint32_le unitSize; // Size of the Unit and any depending data. // END DO NOT CHANGE THESE FIELDS EVER + char libraryVersionHash[48]; + char md5Checksum[16]; // checksum of all bytes following this field. void generateChecksum(); @@ -793,7 +795,7 @@ struct Unit } }; -static_assert(sizeof(Unit) == 144, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Unit) == 192, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct TypeReference { diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index ccc909c199..c9e535c93f 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -48,6 +48,9 @@ #include #include +// generated by qmake: +#include "qml_compile_hash_p.h" + QV4::Compiler::StringTableGenerator::StringTableGenerator() { clear(); @@ -396,6 +399,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.flags |= module->unitFlags; unit.version = QV4_DATA_STRUCTURE_VERSION; unit.qtVersion = QT_VERSION; + qstrcpy(unit.libraryVersionHash, QML_COMPILE_HASH); memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); -- cgit v1.2.3