From 57960ab075b9e7a471c42ddea6a2abbc2994ec83 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 12 Sep 2021 20:07:40 -0700 Subject: Q{Elf,Mach}Parser: simplify the return codes The multi-state return code was a legacy of how Arvid wrote the ELF parser code back in the day, the fact that it scanned for two different types of plugins in Qt 4 and that the metadata could exist in different places. None of that matters nowadays: who cares if the file is a corrupt binary, not a valid binary, does not have the right architecture, or has no suitable section? It's not a plugin, period. The Qt 4 plugin mechanism was removed for Qt 5.0 in commit 7443895857fdaee132c8efc643e471f02b3d0fa4 ("Remove support for Qt 4 style plugins"). Change-Id: I42eb903a916645db9900fffd16a442d800399b98 Reviewed-by: Lars Knoll Reviewed-by: Oswald Buddenhagen --- src/corelib/plugin/qelfparser_p.cpp | 36 +++++++++++------------- src/corelib/plugin/qelfparser_p.h | 5 ++-- src/corelib/plugin/qlibrary.cpp | 56 ++++++++++++++----------------------- src/corelib/plugin/qlibrary_p.h | 6 ++++ src/corelib/plugin/qmachparser.cpp | 20 ++++++------- src/corelib/plugin/qmachparser_p.h | 7 ++--- 6 files changed, 57 insertions(+), 73 deletions(-) (limited to 'src') diff --git a/src/corelib/plugin/qelfparser_p.cpp b/src/corelib/plugin/qelfparser_p.cpp index bbcfe2f865..c5fdf79525 100644 --- a/src/corelib/plugin/qelfparser_p.cpp +++ b/src/corelib/plugin/qelfparser_p.cpp @@ -63,8 +63,8 @@ const char *QElfParser::parseSectionHeader(const char *data, ElfSectionHeader *s return data; } -auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &library, - QLibraryPrivate *lib, qsizetype *pos, qsizetype *sectionlen) -> ScanResult +QLibraryScanResult QElfParser::parse(const char *dataStart, ulong fdlen, const QString &library, + QLibraryPrivate *lib) { #if defined(QELFPARSER_DEBUG) qDebug() << "QElfParser::parse " << library; @@ -73,19 +73,19 @@ auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &librar if (fdlen < 64) { if (lib) lib->errorString = QLibrary::tr("'%1' is not an ELF object (%2)").arg(library, QLibrary::tr("file too small")); - return NotElf; + return {}; } const char *data = dataStart; if (qstrncmp(data, "\177ELF", 4) != 0) { if (lib) lib->errorString = QLibrary::tr("'%1' is not an ELF object").arg(library); - return NotElf; + return {}; } // 32 or 64 bit if (data[4] != 1 && data[4] != 2) { if (lib) lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, QLibrary::tr("odd cpu architecture")); - return Corrupt; + return {}; } /* If you remove this check, to read ELF objects of a different arch, please make sure you modify the typedefs @@ -95,7 +95,7 @@ auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &librar if (data[4] != ExpectedClass) { if (lib) lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, QLibrary::tr("wrong cpu architecture")); - return Corrupt; + return {}; } // endian @@ -103,7 +103,7 @@ auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &librar if (data[5] != ExpectedEndianness) { if (lib) lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, QLibrary::tr("odd endianness")); - return Corrupt; + return {}; } data += 16 // e_ident @@ -122,7 +122,7 @@ auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &librar if (e_shsize > fdlen) { if (lib) lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, QLibrary::tr("unexpected e_shsize")); - return Corrupt; + return {}; } data += sizeof(qelfhalf_t) // e_ehsize @@ -134,7 +134,7 @@ auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &librar if (e_shentsize % 4) { if (lib) lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, QLibrary::tr("unexpected e_shentsize")); - return Corrupt; + return {}; } data += sizeof(qelfhalf_t); // e_shentsize qelfhalf_t e_shnum = qFromUnaligned (data); @@ -149,7 +149,7 @@ auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &librar nullptr, int(e_shnum)).arg(e_shentsize); lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, message); } - return Corrupt; + return {}; } #if defined(QELFPARSER_DEBUG) @@ -164,7 +164,7 @@ auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &librar lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") .arg(library, QLibrary::tr("shstrtab section header seems to be at %1") .arg(QString::number(soff, 16))); - return Corrupt; + return {}; } parseSectionHeader(dataStart + soff, &strtab); @@ -175,7 +175,7 @@ auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &librar lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") .arg(library, QLibrary::tr("string table seems to be at %1") .arg(QString::number(strtab.offset, 16))); - return Corrupt; + return {}; } #if defined(QELFPARSER_DEBUG) @@ -197,7 +197,7 @@ auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &librar lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") .arg(library, QLibrary::tr("section name %1 of %2 behind end of file") .arg(i).arg(e_shnum)); - return Corrupt; + return {}; } #if defined(QELFPARSER_DEBUG) @@ -210,7 +210,7 @@ auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &librar if (lib) lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") .arg(library, QLibrary::tr("empty .rodata. not a library.")); - return Corrupt; + return {}; } #if defined(QELFPARSER_DEBUG) qDebug()<<"section is not program data. skipped."; @@ -223,15 +223,13 @@ auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &librar if (lib) lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)") .arg(library, QLibrary::tr("missing section data. This is not a library.")); - return Corrupt; + return {}; } - *pos = sh.offset; - *sectionlen = sh.size; - return QtMetaDataSection; + return { qsizetype(sh.offset), qsizetype(sh.size) }; } s += e_shentsize; } - return NoQtSection; + return {}; } QT_END_NAMESPACE diff --git a/src/corelib/plugin/qelfparser_p.h b/src/corelib/plugin/qelfparser_p.h index bd967e53ae..316d3bd0b6 100644 --- a/src/corelib/plugin/qelfparser_p.h +++ b/src/corelib/plugin/qelfparser_p.h @@ -52,7 +52,7 @@ // #include -#include +#include "qlibrary_p.h" QT_REQUIRE_CONFIG(library); @@ -71,7 +71,6 @@ typedef quintptr qelfaddr_t; class QElfParser { public: - enum ScanResult { QtMetaDataSection, NoQtSection, NotElf, Corrupt }; enum { ElfLittleEndian = 0, ElfBigEndian = 1 }; struct ElfSectionHeader @@ -85,7 +84,7 @@ public: qelfoff_t m_stringTableFileOffset; const char *parseSectionHeader(const char* s, ElfSectionHeader *sh); - ScanResult parse(const char *m_s, ulong fdlen, const QString &library, QLibraryPrivate *lib, qsizetype *pos, qsizetype *sectionlen); + QLibraryScanResult parse(const char *m_s, ulong fdlen, const QString &library, QLibraryPrivate *lib); }; QT_END_NAMESPACE diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 2c5940f8f2..cfc7479d37 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -254,8 +254,10 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) constexpr qint64 MaxMemoryMapSize = Q_INT64_C(1) << (sizeof(qsizetype) > 4 ? 40 : 29); - qsizetype fdlen = qMin(file.size(), MaxMemoryMapSize); - const char *filedata = reinterpret_cast(file.map(0, fdlen)); + QLibraryScanResult r; + r.pos = 0; + r.length = qMin(file.size(), MaxMemoryMapSize); + const char *filedata = reinterpret_cast(file.map(0, r.length)); #ifdef Q_OS_UNIX if (filedata == nullptr) { @@ -274,65 +276,49 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) // the side of doing a regular read into memory (up to 64 MB). data = file.read(64 * 1024 * 1024); filedata = data.constData(); - fdlen = data.size(); + r.length = data.size(); } #endif /* - ELF and Mach-O binaries with GCC have .qplugin sections. + ELF and Mach-O binaries with GCC have .qtmetadata sections. Find them. */ bool hasMetaData = false; - qsizetype pos = 0; char pattern[] = "qTMETADATA "; pattern[0] = 'Q'; // Ensure the pattern "QTMETADATA" is not found in this library should QPluginLoader ever encounter it. const ulong plen = ulong(qstrlen(pattern)); #if defined (Q_OF_ELF) - QElfParser::ScanResult r = QElfParser().parse(filedata, fdlen, library, lib, &pos, &fdlen); - if (r == QElfParser::Corrupt || r == QElfParser::NotElf) { - if (lib && qt_debug_component()) { - qWarning("QElfParser: %ls", qUtf16Printable(lib->errorString)); - } - return false; - } else if (r == QElfParser::QtMetaDataSection) { - qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen); - if (rel < 0) - pos = -1; - else - pos += rel; - hasMetaData = true; + r = QElfParser().parse(filedata, r.length, library, lib); + if (r.length == 0) { + if (lib && qt_debug_component()) + qWarning("QElfParser: %ls", qUtf16Printable(lib->errorString)); + return false; } #elif defined(Q_OF_MACH_O) { QString errorString; - int r = QMachOParser::parse(filedata, fdlen, library, &errorString, &pos, &fdlen); - if (r == QMachOParser::NotSuitable) { + r = QMachOParser::parse(filedata, r.length, library, &errorString); + if (r.length == 0) { if (qt_debug_component()) qWarning("QMachOParser: %ls", qUtf16Printable(errorString)); if (lib) lib->errorString = errorString; return false; } - // even if the metadata section was not found, the Mach-O parser will - // at least return the boundaries of the right architecture - qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen); - if (rel < 0) - pos = -1; - else - pos += rel; - hasMetaData = true; } -#else - pos = qt_find_pattern(filedata, fdlen, pattern, plen); - if (pos > 0) - hasMetaData = true; #endif // defined(Q_OF_ELF) && defined(Q_CC_GNU) + if (qsizetype rel = qt_find_pattern(filedata + r.pos, r.length, pattern, plen); + rel >= 0) { + r.pos += rel; + hasMetaData = true; + } bool ret = false; - if (pos >= 0 && hasMetaData) { - const char *data = filedata + pos; + if (r.pos >= 0 && hasMetaData) { + const char *data = filedata + r.pos; QString errMsg; - QJsonDocument doc = qJsonFromRawLibraryMetaData(data, fdlen, &errMsg); + QJsonDocument doc = qJsonFromRawLibraryMetaData(data, r.length, &errMsg); if (doc.isNull()) { qWarning("Found invalid metadata in lib %ls: %ls", qUtf16Printable(library), qUtf16Printable(errMsg)); diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h index 3ca544b2de..121ecae34e 100644 --- a/src/corelib/plugin/qlibrary_p.h +++ b/src/corelib/plugin/qlibrary_p.h @@ -68,6 +68,12 @@ QT_BEGIN_NAMESPACE bool qt_debug_component(); +struct QLibraryScanResult +{ + qsizetype pos; + qsizetype length; +}; + class QLibraryStore; class QLibraryPrivate { diff --git a/src/corelib/plugin/qmachparser.cpp b/src/corelib/plugin/qmachparser.cpp index 9d0b4de4f5..db4453dffe 100644 --- a/src/corelib/plugin/qmachparser.cpp +++ b/src/corelib/plugin/qmachparser.cpp @@ -42,7 +42,6 @@ #if defined(Q_OF_MACH_O) #include -#include "qlibrary_p.h" #include #include @@ -81,15 +80,16 @@ typedef section my_section; static const uint32_t my_magic = MH_MAGIC; #endif -static int ns(const QString &reason, const QString &library, QString *errorString) +Q_DECL_COLD_FUNCTION +static QLibraryScanResult ns(const QString &reason, const QString &library, QString *errorString) { if (errorString) *errorString = QLibrary::tr("'%1' is not a valid Mach-O binary (%2)") .arg(library, reason.isEmpty() ? QLibrary::tr("file is corrupt") : reason); - return QMachOParser::NotSuitable; + return {}; } -int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QString *errorString, qsizetype *pos, qsizetype *sectionlen) +QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QString *errorString) { // The minimum size of a Mach-O binary we're interested in. // It must have a full Mach header, at least one segment and at least one @@ -146,9 +146,7 @@ int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QS library, errorString); } - // from this point on, fdlen is specific to this architecture // from this point on, everything is in host byte order - *pos = reinterpret_cast(header) - m_s; // (re-)check the CPU type // ### should we check the CPU subtype? Maybe on ARM? @@ -197,9 +195,8 @@ int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QS || Q_UNLIKELY(fdlen < sect[j].offset + sect[j].size)) return ns(QString(), library, errorString); - *pos += sect[j].offset; - *sectionlen = sect[j].size; - return QtMetaDataSection; + qsizetype pos = reinterpret_cast(header) - m_s + sect[j].offset; + return { pos, qsizetype(sect[j].size) }; } } @@ -207,11 +204,10 @@ int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QS seg = reinterpret_cast(reinterpret_cast(seg) + seg->cmdsize); } -// // No Qt section was found, but at least we know that where the proper architecture's boundaries are -// return NoQtSection; + // No .qtmetadata section was found if (errorString) *errorString = QLibrary::tr("'%1' is not a Qt plugin").arg(library); - return NotSuitable; + return {}; } QT_END_NAMESPACE diff --git a/src/corelib/plugin/qmachparser_p.h b/src/corelib/plugin/qmachparser_p.h index 290b68876f..499c63aa94 100644 --- a/src/corelib/plugin/qmachparser_p.h +++ b/src/corelib/plugin/qmachparser_p.h @@ -51,8 +51,7 @@ // We mean it. // -#include -#include +#include "qlibrary_p.h" QT_REQUIRE_CONFIG(library); @@ -66,8 +65,8 @@ class QLibraryPrivate; class Q_AUTOTEST_EXPORT QMachOParser { public: - enum { QtMetaDataSection, NoQtSection, NotSuitable }; - static int parse(const char *m_s, ulong fdlen, const QString &library, QString *errorString, qsizetype *pos, qsizetype *sectionlen); + static QLibraryScanResult parse(const char *m_s, ulong fdlen, const QString &library, + QString *errorString); }; QT_END_NAMESPACE -- cgit v1.2.3