-
-
Notifications
You must be signed in to change notification settings - Fork 14.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
qt6.qtdeclarative: invalidate qml caches created from mismatched stor…
…e paths Includes the application's store path in the verification metadata of qml cache files and prevents them from being loaded if mismatched.
- Loading branch information
Showing
2 changed files
with
115 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
112 changes: 112 additions & 0 deletions
112
.../libraries/qt-6/patches/qtdeclarative-invalidate-caches-from-mismatched-store-paths.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp | ||
index 2ab38b70d9..8634b07e41 100644 | ||
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp | ||
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp | ||
@@ -33,6 +33,8 @@ | ||
#include <QtCore/qcryptographichash.h> | ||
#include <QtCore/QScopedValueRollback> | ||
|
||
+#include <QtCore/qcoreapplication.h> | ||
+ | ||
static_assert(QV4::CompiledData::QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH); | ||
|
||
#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0 | ||
@@ -44,6 +46,36 @@ __attribute__((section(".qml_compile_hash"))) | ||
const char qml_compile_hash[QV4::CompiledData::QmlCompileHashSpace] = QML_COMPILE_HASH; | ||
static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) > QML_COMPILE_HASH_LENGTH, | ||
"Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); | ||
+ | ||
+bool nix__isNixApplication() { | ||
+ static const bool value = QCoreApplication::applicationFilePath().startsWith(QStringLiteral("/nix/store/")); | ||
+ return value; | ||
+} | ||
+ | ||
+static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) > | ||
+ /*sha1*/ 20 + /*NIX:*/ 4, | ||
+ "Nix compile hash length exceeds the reserved space in data " | ||
+ "structure. Please review the patch."); | ||
+ | ||
+const QByteArray &nix__applicationHash() { | ||
+ static const QByteArray value = [](){ | ||
+ QCryptographicHash applicationHash(QCryptographicHash::Sha1); | ||
+ applicationHash.addData(QByteArrayView(qml_compile_hash, QML_COMPILE_HASH_LENGTH)); | ||
+ | ||
+ // We only care about the package, not the specific file path. | ||
+ auto view = QCoreApplication::applicationFilePath().sliced(11); // len("/nix/store/") | ||
+ auto pkgEndIdx = view.indexOf(QStringLiteral("/")); | ||
+ if (pkgEndIdx != -1) view = view.sliced(0, pkgEndIdx); | ||
+ | ||
+ QCryptographicHash::hashLength(QCryptographicHash::Sha1); | ||
+ applicationHash.addData(view.toUtf8()); | ||
+ | ||
+ return QByteArray("NIX:") + applicationHash.result(); | ||
+ }(); | ||
+ | ||
+ return value; | ||
+} | ||
+ | ||
#else | ||
# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" | ||
#endif | ||
@@ -909,6 +941,28 @@ bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorSt | ||
return false; | ||
} | ||
|
||
+#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0 | ||
+ if (nix__isNixApplication() && unitUrl.scheme() == QStringLiteral("qrc")) { | ||
+ // If the application is running from the nix store, we can safely save | ||
+ // bytecode for its embedded QML files as long as we hash the | ||
+ // application path into the version. | ||
+ const auto &applicationHash = nix__applicationHash(); | ||
+ | ||
+ memcpy(const_cast<char *>(unitData()->libraryVersionHash), | ||
+ applicationHash.constData(), applicationHash.length()); | ||
+ } else if (unitUrl.path().startsWith(QStringLiteral("/nix/store/"))) { | ||
+ // We don't store bytecode for bare QML files in the nix store as the | ||
+ // paths will change every time the application updates, filling caches | ||
+ // endlessly with junk. | ||
+ *errorString = QStringLiteral("Refusing to save bytecode for bare /nix/store/ path."); | ||
+ return false; | ||
+ } else { | ||
+ // If the QML file is loaded from a normal file path it doesn't matter | ||
+ // if the application itself is running from a nix path, so we fall back | ||
+ // to the default Qt behavior. | ||
+ } | ||
+#endif | ||
+ | ||
return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>( | ||
[&unitUrl, errorString](const char *data, quint32 size) { | ||
const QString cachePath = localCacheFilePath(unitUrl); | ||
@@ -1038,13 +1092,29 @@ bool ExecutableCompilationUnit::verifyHeader( | ||
} | ||
|
||
#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0 | ||
- if (qstrncmp(qml_compile_hash, unit->libraryVersionHash, QML_COMPILE_HASH_LENGTH) != 0) { | ||
+ const bool nixUnit = qstrncmp("NIX:", unit->libraryVersionHash, 4) == 0; | ||
+ | ||
+ if (nixUnit && !nix__isNixApplication()) { | ||
+ *errorString = QStringLiteral("QML compile hash is for a nix store application."); | ||
+ return false; | ||
+ } | ||
+ | ||
+ const char *targetHash = qml_compile_hash; | ||
+ size_t targetHashLength = QML_COMPILE_HASH_LENGTH; | ||
+ | ||
+ if (nixUnit) { | ||
+ const auto &applicationHash = nix__applicationHash(); | ||
+ targetHash = applicationHash.constData(); | ||
+ targetHashLength = applicationHash.length(); | ||
+ } | ||
+ | ||
+ if (qstrncmp(targetHash, unit->libraryVersionHash, targetHashLength) != 0) { | ||
*errorString = QStringLiteral("QML compile hashes don't match. Found %1 expected %2") | ||
.arg(QString::fromLatin1( | ||
- QByteArray(unit->libraryVersionHash, QML_COMPILE_HASH_LENGTH) | ||
+ QByteArray(unit->libraryVersionHash, targetHashLength) | ||
.toPercentEncoding()), | ||
QString::fromLatin1( | ||
- QByteArray(qml_compile_hash, QML_COMPILE_HASH_LENGTH) | ||
+ QByteArray(targetHash, targetHashLength) | ||
.toPercentEncoding())); | ||
return false; | ||
} |