Plugins: Save some architectural requirement flags

...not just the debug flag. The information is saved outside of the CBOR
map for two reasons:

 1) removing the hack that depended on how QCborStreamWriter and
 TinyCBOR internally work, allowing for the extra parameter to be
 written directly. We wouldn't be able to use that hack anyway and would
 have needed a further, uglier hack to encode a byte whose value we
 don't know.

 2) outside the map, this information can be parsed more quickly and
 then we can discard any plugins we shouldn't actually load.

Since we're doing this for a flag, I decided to move the Qt version
there too for reason #2.

Change-Id: I61ecce6b1324410bbab4fffd153d4e5fc696d19e
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
This commit is contained in:
Thiago Macieira 2018-07-01 10:20:21 -07:00
parent d9766ddc3d
commit 7bd79b3cff
5 changed files with 75 additions and 43 deletions

View File

@ -69,11 +69,17 @@ static inline int metaDataSignatureLength()
static QJsonDocument jsonFromCborMetaData(const char *raw, qsizetype size, QString *errMsg)
{
if (Q_UNLIKELY(raw[-1] != '!')) {
*errMsg = QStringLiteral("Invalid metadata signature");
// extract the keys not stored in CBOR
int qt_metadataVersion = quint8(raw[0]);
int qt_version = qFromBigEndian<quint16>(raw + 1);
int qt_archRequirements = quint8(raw[3]);
if (Q_UNLIKELY(raw[-1] != '!' || qt_metadataVersion != 0)) {
*errMsg = QStringLiteral("Invalid metadata version");
return QJsonDocument();
}
raw += 4;
size -= 4;
QByteArray ba = QByteArray::fromRawData(raw, int(size));
QCborParserError err;
QCborValue metadata = QCborValue::fromCbor(ba, &err);
@ -88,8 +94,12 @@ static QJsonDocument jsonFromCborMetaData(const char *raw, qsizetype size, QStri
return QJsonDocument();
}
// convert the top-level map integer keys
QJsonObject o;
o.insert(QLatin1String("version"), qt_version << 8);
o.insert(QLatin1String("debug"), bool(qt_archRequirements & 1));
o.insert(QLatin1String("archreq"), qt_archRequirements);
// convert the top-level map integer keys
for (auto it : metadata.toMap()) {
QString key;
if (it.first.isInteger()) {
@ -98,6 +108,12 @@ static QJsonDocument jsonFromCborMetaData(const char *raw, qsizetype size, QStri
case int(IntKey): key = QStringLiteral(StringKey); break;
QT_PLUGIN_FOREACH_METADATA(CONVERT_TO_STRING)
#undef CONVERT_TO_STRING
case int(QtPluginMetaDataKeys::Requirements):
// special case: recreate the debug key
o.insert(QLatin1String("debug"), bool(it.second.toInteger() & 1));
key = QStringLiteral("archreq");
break;
}
} else {
key = it.first.toString();

View File

@ -55,6 +55,21 @@ QT_BEGIN_NAMESPACE
# endif
#endif
inline constexpr unsigned char qPluginArchRequirements()
{
return 0
#ifndef QT_NO_DEBUG
| 1
#endif
#ifdef __AVX2__
| 2
# ifdef __AVX512F__
| 4
# endif
#endif
;
}
typedef QObject *(*QtPluginInstanceFunction)();
typedef const char *(*QtPluginMetaDataFunction)();

View File

@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE
enum class QtPluginMetaDataKeys {
QtVersion,
Debug,
Requirements,
IID,
ClassName,
MetaData
@ -66,8 +66,6 @@ enum class QtPluginMetaDataKeys {
// F(IntKey, StringKey, Description)
// Keep this list sorted in the order moc should output.
#define QT_PLUGIN_FOREACH_METADATA(F) \
F(QtPluginMetaDataKeys::QtVersion, "version", "Qt version built against") \
F(QtPluginMetaDataKeys::Debug, "debug", "Whether it is a debug build") \
F(QtPluginMetaDataKeys::IID, "IID", "Plugin's Interface ID") \
F(QtPluginMetaDataKeys::ClassName, "className", "Plugin class name") \
F(QtPluginMetaDataKeys::MetaData, "MetaData", "Other meta data")

View File

@ -1624,8 +1624,11 @@ void Generator::generatePluginMetaData()
return;
fputs("\nQT_PLUGIN_METADATA_SECTION\n"
"static const unsigned char qt_pluginMetaData[] = {\n"
" 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',", out);
"static constexpr unsigned char qt_pluginMetaData[] = {\n"
" 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',\n"
" // metadata version, Qt version, architectural requirements\n"
" 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),", out);
CborDevice dev(out);
CborEncoder enc;
@ -1634,20 +1637,6 @@ void Generator::generatePluginMetaData()
CborEncoder map;
cbor_encoder_create_map(&enc, &map, CborIndefiniteLength);
dev.nextItem("\"version\"");
cbor_encode_int(&map, int(QtPluginMetaDataKeys::QtVersion));
cbor_encode_int(&map, QT_VERSION);
fputs("\n#ifdef QT_NO_DEBUG", out);
dev.nextItem("\"debug\" = false");
cbor_encode_int(&map, int(QtPluginMetaDataKeys::Debug));
cbor_encode_boolean(&map, false);
fputs("\n#else", out);
dev.nextItem("\"debug\" = true");
cbor_encode_int(&map, int(QtPluginMetaDataKeys::Debug));
cbor_encode_boolean(&map, true);
fputs("\n#endif", out);
dev.nextItem("\"IID\"");
cbor_encode_int(&map, int(QtPluginMetaDataKeys::IID));
cbor_encode_text_string(&map, cdef->pluginData.iid.constData(), cdef->pluginData.iid.size());

View File

@ -31,6 +31,7 @@
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <qplugin.h>
#include <QPluginLoader>
#include <private/qplugin_p.h>
@ -147,49 +148,62 @@ void tst_QPlugin::scanInvalidPlugin_data()
QTest::newRow("json-control") << (prefix + QJsonDocument(obj).toBinaryData()) << true << "";
}
QTest::newRow("json-zeroes") << prefix << false << "";
QTest::newRow("json-zeroes") << prefix << false << " ";
prefix += "qbjs";
QTest::newRow("bad-json-version0") << prefix << false << "";
QTest::newRow("bad-json-version2") << (prefix + QByteArray("\2\0\0\0", 4)) << false << "";
QTest::newRow("bad-json-version0") << prefix << false << " ";
QTest::newRow("bad-json-version2") << (prefix + QByteArray("\2\0\0\0", 4)) << false << " ";
// valid qbjs version 1
prefix += QByteArray("\1\0\0\0");
// too large for the file (100 MB)
QTest::newRow("bad-json-size-large1") << (prefix + QByteArray("\0\0\x40\x06")) << false << "";
QTest::newRow("bad-json-size-large1") << (prefix + QByteArray("\0\0\x40\x06")) << false << " ";
// too large for binary JSON (512 MB)
QTest::newRow("bad-json-size-large2") << (prefix + QByteArray("\0\0\0\x20")) << false << "";
QTest::newRow("bad-json-size-large2") << (prefix + QByteArray("\0\0\0\x20")) << false << " ";
// could overflow
QTest::newRow("bad-json-size-large3") << (prefix + "\xff\xff\xff\x7f") << false << "";
QTest::newRow("bad-json-size-large3") << (prefix + "\xff\xff\xff\x7f") << false << " ";
#endif
// CBOR metadata
QByteArray cprefix = "QTMETADATA !";
QByteArray cprefix = "QTMETADATA !1234";
cprefix[12] = 0; // current version
cprefix[13] = QT_VERSION_MAJOR;
cprefix[14] = QT_VERSION_MINOR;
cprefix[15] = qPluginArchRequirements();
{
QByteArray cborValid = [] {
QCborMap m;
m.insert(int(QtPluginMetaDataKeys::IID), QLatin1String("org.qt-project.tst_qplugin"));
m.insert(int(QtPluginMetaDataKeys::ClassName), QLatin1String("tst"));
m.insert(int(QtPluginMetaDataKeys::QtVersion), int(QT_VERSION));
#ifdef QT_NO_DEBUG
m.insert(int(QtPluginMetaDataKeys::Debug), false);
#else
m.insert(int(QtPluginMetaDataKeys::Debug), true);
#endif
m.insert(int(QtPluginMetaDataKeys::MetaData), QCborMap());
return QCborValue(m).toCbor();
}();
QTest::newRow("cbor-control") << (cprefix + cborValid) << true << "";
QTest::newRow("cbor-control") << (cprefix + QCborValue(m).toCbor()) << true << "";
}
cprefix[12] = 1;
QTest::newRow("cbor-major-too-new") << (cprefix + cborValid) << false
<< " Invalid metadata version";
cprefix[12] = 0;
cprefix[13] = QT_VERSION_MAJOR + 1;
QTest::newRow("cbor-major-too-new") << (cprefix + cborValid) << false << "";
cprefix[13] = QT_VERSION_MAJOR - 1;
QTest::newRow("cbor-major-too-old") << (cprefix + cborValid) << false << "";
cprefix[13] = QT_VERSION_MAJOR;
cprefix[14] = QT_VERSION_MINOR + 1;
QTest::newRow("cbor-minor-too-new") << (cprefix + cborValid) << false << "";
QTest::newRow("cbor-invalid") << (cprefix + "\xff") << false
<< "Metadata parsing error: Invalid CBOR stream: unexpected 'break' byte";
<< " Metadata parsing error: Invalid CBOR stream: unexpected 'break' byte";
QTest::newRow("cbor-not-map1") << (cprefix + "\x01") << false
<< "Unexpected metadata contents";
<< " Unexpected metadata contents";
QTest::newRow("cbor-not-map2") << (cprefix + "\x81\x01") << false
<< "Unexpected metadata contents";
<< " Unexpected metadata contents";
}
static const char invalidPluginSignature[] = "qplugin testfile";
@ -246,10 +260,10 @@ void tst_QPlugin::scanInvalidPlugin()
// now try to load this
QFETCH(bool, loads);
QFETCH(QString, errMsg);
if (!loads)
if (!errMsg.isEmpty())
QTest::ignoreMessage(QtWarningMsg,
"Found invalid metadata in lib " + QFile::encodeName(newName) +
": " + errMsg.toUtf8());
":" + errMsg.toUtf8());
QPluginLoader loader(newName);
QCOMPARE(loader.load(), loads);
if (loads)