diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 3b39e51e195..d4f9235538f 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -210,7 +210,7 @@ static QLibraryScanResult qt_find_pattern(const char *s, qsizetype s_len, QStrin information could not be read. Returns true if version information is present and successfully read. */ -static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) +static QLibraryScanResult findPatternUnloaded(const QString &library, QLibraryPrivate *lib) { QFile file(library); if (!file.open(QIODevice::ReadOnly)) { @@ -218,7 +218,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) lib->errorString = file.errorString(); qCWarning(qt_lcDebugPlugins, "%ls: cannot open: %ls", qUtf16Printable(library), qUtf16Printable(file.errorString())); - return false; + return {}; } // Files can be bigger than the virtual memory size on 32-bit systems, so @@ -235,7 +235,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) // This can't be used as a plugin. qCWarning(qt_lcDebugPlugins, "%ls: failed to map to memory: %ls", qUtf16Printable(library), qUtf16Printable(file.errorString())); - return false; + return {}; } #else QByteArray data; @@ -252,6 +252,10 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) QString errMsg = library; QLibraryScanResult r = qt_find_pattern(filedata, fdlen, &errMsg); if (r.length) { +#if defined(Q_OF_MACH_O) + if (r.isEncrypted) + return r; +#endif if (!lib->metaData.parse(QByteArrayView(filedata + r.pos, r.length))) { errMsg = lib->metaData.errorString(); qCWarning(qt_lcDebugPlugins, "Found invalid metadata in lib %ls: %ls", @@ -260,7 +264,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) qCDebug(qt_lcDebugPlugins, "Found metadata in lib %ls, metadata=\n%s\n", qUtf16Printable(library), QJsonDocument(lib->metaData.toJson()).toJson().constData()); - return true; + return r; } } else { qCDebug(qt_lcDebugPlugins, "Failed to find metadata in lib %ls: %ls", @@ -269,7 +273,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) lib->errorString = QLibrary::tr("Failed to extract plugin meta data from '%1': %2") .arg(library, errMsg); - return false; + return {}; } static void installCoverageTool(QLibraryPrivate *libPrivate) @@ -724,7 +728,22 @@ void QLibraryPrivate::updatePluginState() if (!pHnd.loadRelaxed()) { // scan for the plugin metadata without loading - success = findPatternUnloaded(fileName, this); + QLibraryScanResult result = findPatternUnloaded(fileName, this); +#if defined(Q_OF_MACH_O) + if (result.length && result.isEncrypted) { + // We found the .qtmetadata section, but since the library is encrypted + // we need to dlopen() it before we can parse the metadata for further + // validation. + qCDebug(qt_lcDebugPlugins, "Library is encrypted. Doing prospective load before parsing metadata"); + locker.unlock(); + load(); + locker.relock(); + success = qt_get_metadata(this, &errorString); + } else +#endif + { + success = result.length != 0; + } } else { // library is already loaded (probably via QLibrary) // simply get the target function and call it. diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h index 8c722a3283e..e3bbbe104bf 100644 --- a/src/corelib/plugin/qlibrary_p.h +++ b/src/corelib/plugin/qlibrary_p.h @@ -38,6 +38,9 @@ struct QLibraryScanResult { qsizetype pos; qsizetype length; +#if defined(Q_OF_MACH_O) + bool isEncrypted = false; +#endif }; class QLibraryStore; diff --git a/src/corelib/plugin/qmachparser.cpp b/src/corelib/plugin/qmachparser.cpp index 979ce2c7de1..7a82b84cb36 100644 --- a/src/corelib/plugin/qmachparser.cpp +++ b/src/corelib/plugin/qmachparser.cpp @@ -56,6 +56,23 @@ static QLibraryScanResult notfound(const QString &reason, QString *errorString) return {}; } +static bool isEncrypted(const my_mach_header *header) +{ + auto commandCursor = uintptr_t(header) + sizeof(my_mach_header); + for (uint32_t i = 0; i < header->ncmds; ++i) { + load_command *loadCommand = reinterpret_cast(commandCursor); + if (loadCommand->cmd == LC_ENCRYPTION_INFO || loadCommand->cmd == LC_ENCRYPTION_INFO_64) { + // The layout of encryption_info_command and encryption_info_command_64 is the same + // up until and including cryptid, so we can treat it as encryption_info_command. + auto encryptionInfoCommand = reinterpret_cast(loadCommand); + return encryptionInfoCommand->cryptid != 0; + } + commandCursor += loadCommand->cmdsize; + } + + return false; +} + QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *errorString) { // The minimum size of a Mach-O binary we're interested in. @@ -166,8 +183,12 @@ QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *e if (sect[j].size < sizeof(QPluginMetaData::MagicHeader)) return notfound(QLibrary::tr(".qtmetadata section is too small"), errorString); + const bool binaryIsEncrypted = isEncrypted(header); qsizetype pos = reinterpret_cast(header) - m_s + sect[j].offset; - if (IncludeValidityChecks) { + + // We can not read the section data of encrypted libraries until they + // have been dlopened(), so skip validity check if that's the case. + if (IncludeValidityChecks && !binaryIsEncrypted) { QByteArrayView expectedMagic = QByteArrayView::fromArray(QPluginMetaData::MagicString); QByteArrayView actualMagic = QByteArrayView(m_s + pos, expectedMagic.size()); if (expectedMagic != actualMagic) @@ -175,7 +196,7 @@ QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *e } pos += sizeof(QPluginMetaData::MagicString); - return { pos, qsizetype(sect[j].size - sizeof(QPluginMetaData::MagicString)) }; + return { pos, qsizetype(sect[j].size - sizeof(QPluginMetaData::MagicString)), binaryIsEncrypted }; } }