Detect incompatibilities in repeated type registration

QMetaType used to register a typeName and factory functions for
creation/destruction of objects. While it would be possible for a single
type name to be registered matching different actual types and memory
layouts, there was little that could be done about it.

Now that QMetaType is tracking type information with a direct impact on
data layout and ABI (size and type flags) it is important that we check
and detect binary incompatibilities as early as possible.

[Such incompatibilities could arise from type name re-use (technically,
ODR violations) or, more commonly, as version mismatch between different
shared libraries or plugins.]

Only type size and flags are checked as function pointers to inline and
template or otherwise non-exported functions could trivially differ
across translation units and shared libraries.

When registering typedef types, a check is made to ensure the same name
doesn't get registered as different types.

Change-Id: I8211c3de75d4854ce8fafdb620d3a931c206e0c3
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@nokia.com>
Reviewed-by: Kent Hansen <kent.hansen@nokia.com>
bb10
João Abecasis 2012-01-30 14:50:04 +01:00 committed by Qt by Nokia
parent b8cf1d6bdc
commit 5a92dd6124
2 changed files with 88 additions and 17 deletions

View File

@ -465,6 +465,8 @@ int QMetaType::registerType(const char *typeName, Deleter deleter,
int idx = qMetaTypeStaticType(normalizedTypeName.constData(),
normalizedTypeName.size());
int previousSize = 0;
int previousFlags = 0;
if (!idx) {
QWriteLocker locker(customTypesLock());
idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
@ -485,8 +487,33 @@ int QMetaType::registerType(const char *typeName, Deleter deleter,
inf.flags = flags;
idx = ct->size() + User;
ct->append(inf);
return idx;
}
if (idx >= User) {
previousSize = ct->at(idx - User).size;
previousFlags = ct->at(idx - User).flags;
}
}
if (idx < User) {
previousSize = QMetaType::sizeOf(idx);
previousFlags = QMetaType::typeFlags(idx);
}
if (previousSize != size) {
qFatal("QMetaType::registerType: Binary compatibility break "
"-- Size mismatch for type '%s' [%i]. Previously registered "
"size %i, now registering size %i.",
normalizedTypeName.constData(), idx, previousSize, size);
}
if (previousFlags != flags) {
qFatal("QMetaType::registerType: Binary compatibility break "
"-- Type flags for type '%s' [%i] don't match. Previously "
"registered TypeFlags(0x%x), now registering TypeFlags(0x%x).",
normalizedTypeName.constData(), idx, previousFlags, int(flags));
}
return idx;
}
@ -510,25 +537,30 @@ int QMetaType::registerTypedef(const char* typeName, int aliasId)
int idx = qMetaTypeStaticType(normalizedTypeName.constData(),
normalizedTypeName.size());
if (idx) {
Q_ASSERT(idx == aliasId);
return idx;
if (!idx) {
QWriteLocker locker(customTypesLock());
idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
normalizedTypeName.size());
if (!idx) {
QCustomTypeInfo inf;
inf.typeName = normalizedTypeName;
inf.alias = aliasId;
inf.creator = 0;
inf.deleter = 0;
ct->append(inf);
return aliasId;
}
}
QWriteLocker locker(customTypesLock());
idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
normalizedTypeName.size());
if (idx)
return idx;
QCustomTypeInfo inf;
inf.typeName = normalizedTypeName;
inf.alias = aliasId;
inf.creator = 0;
inf.deleter = 0;
ct->append(inf);
return aliasId;
if (idx != aliasId) {
qFatal("QMetaType::registerTypedef: Binary compatibility break "
"-- Type name '%s' previously registered as typedef of '%s' [%i], "
"now registering as typedef of '%s' [%i].",
normalizedTypeName.constData(), QMetaType::typeName(idx), idx,
QMetaType::typeName(aliasId), aliasId);
}
return idx;
}
/*!

View File

@ -85,6 +85,7 @@ private slots:
void constructCopy_data();
void constructCopy();
void typedefs();
void registerType();
void isRegistered_data();
void isRegistered();
void registerStreamBuiltin();
@ -828,6 +829,44 @@ void tst_QMetaType::typedefs()
QCOMPARE(QMetaType::type("WhityDouble"), ::qMetaTypeId<WhityDouble>());
}
void tst_QMetaType::registerType()
{
// Built-in
QCOMPARE(qRegisterMetaType<QString>("QString"), int(QMetaType::QString));
QCOMPARE(qRegisterMetaType<QString>("QString"), int(QMetaType::QString));
// Custom
int fooId = qRegisterMetaType<TestSpace::Foo>("TestSpace::Foo");
QVERIFY(fooId >= int(QMetaType::User));
QCOMPARE(qRegisterMetaType<TestSpace::Foo>("TestSpace::Foo"), fooId);
int movableId = qRegisterMetaType<CustomMovable>("CustomMovable");
QVERIFY(movableId >= int(QMetaType::User));
QCOMPARE(qRegisterMetaType<CustomMovable>("CustomMovable"), movableId);
// Alias to built-in
typedef QString MyString;
QCOMPARE(qRegisterMetaType<MyString>("MyString"), int(QMetaType::QString));
QCOMPARE(qRegisterMetaType<MyString>("MyString"), int(QMetaType::QString));
QCOMPARE(QMetaType::type("MyString"), int(QMetaType::QString));
// Alias to custom type
typedef CustomMovable MyMovable;
typedef TestSpace::Foo MyFoo;
QCOMPARE(qRegisterMetaType<MyMovable>("MyMovable"), movableId);
QCOMPARE(qRegisterMetaType<MyMovable>("MyMovable"), movableId);
QCOMPARE(QMetaType::type("MyMovable"), movableId);
QCOMPARE(qRegisterMetaType<MyFoo>("MyFoo"), fooId);
QCOMPARE(qRegisterMetaType<MyFoo>("MyFoo"), fooId);
QCOMPARE(QMetaType::type("MyFoo"), fooId);
}
class IsRegisteredDummyType { };
void tst_QMetaType::isRegistered_data()