PDA

View Full Version : Is this a bug?



hkhauke
23rd December 2013, 14:45
Hi everbody,

I have found a strange behavior while using QT5 which - for me -seems to be a bug. I would like to discuss this here.
I have posted the behavior description in the following QWT related thread in this forum:

http://www.qtcentre.org/threads/57102-Qwt-loaded-from-DLL-then-unloaded-and-reloaded-gt-QVariant-crashes-%28race-cond-%29

However, I found out that it is an issue related to QT5 rather than QWT: Here is the short version of the description (I use QT 5.2,
Visual Studio 2012, Windows 7):

The problem is related to loading a class declared as a metatype by using the Q_DECLARE_METATYPE macro from a dll.
When first loading the dll, the Q_DECLARE_METATYPE macro initiates a call to function QMetaType::registerNormalizedType
in file qmetatype.cpp where the type as well as the symbols creator, deleter, constructor and destructor
are registered.
When now unloading and reloading the dll, the symbols for the callbacks creator, deleter, constructor and
destructor should be updated in function QMetaType::registerNormalizedType in file qmetatype.cpp
since the addresses of creator, deleter, constructor and destructor may have changed. This does
not happen since it is detected that the type has been registered before.
If the symbols are not updated, the old symbols may be used as a target to a callback which point to invalid
memory segments which leads to a crash of the application.

Note: In an application with multiple dlls loaded at runtime, the symbols form the first time loading the dll may be identical
to the symbols for the 4 callbacks if all dlls are loaded in identical order. the described crash happen only if
the dlls are loaded in a different order compared toi the first time. Therefore, this does not happen all the time.

And here is my fix to function QMetaType::registerNormalizedType
in file qmetatype.cpp which prevents my application from crashing
(modification NEW CODE vs OLD CODE):


int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, Deleter deleter,
Creator creator,
Destructor destructor,
Constructor constructor,
int size, TypeFlags flags, const QMetaObject *metaObject)
{
QVector<QCustomTypeInfo> *ct = customTypes();
if (!ct || normalizedTypeName.isEmpty() || !deleter || !creator || !destructor || !constructor)
return -1;

int idx = qMetaTypeStaticType(normalizedTypeName.constData() ,
normalizedTypeName.size());

int previousSize = 0;
int previousFlags = 0;
if (idx == UnknownType) {
QWriteLocker locker(customTypesLock());
idx = qMetaTypeCustomType_unlocked(normalizedTypeName.co nstData(),
normalizedTypeName.size());
if (idx == UnknownType) {
QCustomTypeInfo inf;
inf.typeName = normalizedTypeName;
inf.creator = creator;
inf.deleter = deleter;
#ifndef QT_NO_DATASTREAM
inf.loadOp = 0;
inf.saveOp = 0;
#endif
inf.alias = -1;
inf.constructor = constructor;
inf.destructor = destructor;
inf.size = size;
inf.flags = flags;
inf.metaObject = metaObject;
idx = ct->size() + User;
ct->append(inf);
return idx;
}

if (idx >= User) {

/* NEW CODE */
QCustomTypeInfo ttt = ct->at(idx - User);
previousSize = ttt.size;
previousFlags = ttt.flags;
ttt.creator = creator;
ttt.deleter = deleter;
ttt.constructor = constructor;
ttt.destructor = destructor;
ct->replace(idx - User, ttt);[/B]
/* OLD CODE

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);
}

// Ignore WasDeclaredAsMetaType inconsitency, to many users were hitting the problem
previousFlags |= WasDeclaredAsMetaType;
flags |= WasDeclaredAsMetaType;

if (previousFlags != flags) {
const int maskForTypeInfo = NeedsConstruction | NeedsDestruction | MovableType;
const char *msg = "QMetaType::registerType: Binary compatibility break. "
"\nType flags for type '%s' [%i] don't match. Previously "
"registered TypeFlags(0x%x), now registering TypeFlags(0x%x). "
"This is an ODR break, which means that your application depends on a C++ undefined behavior."
"\nHint: %s";
QT_PREPEND_NAMESPACE(QByteArray) hint;
if ((previousFlags & maskForTypeInfo) != (flags & maskForTypeInfo)) {
hint += "\nIt seems that the type was registered at least twice in a different translation units, "
"but Q_DECLARE_TYPEINFO is not visible from all the translations unit or different flags were used."
"Remember that Q_DECLARE_TYPEINFO should be declared before QMetaType registration, "
"preferably it should be placed just after the type declaration and before Q_DECLARE_METATYPE";
}
qFatal(msg, normalizedTypeName.constData(), idx, previousFlags, int(flags), hint.constData());
}

return idx;
}

Best regards and Merry Christmas to all :)!

Hauke

wysota
23rd December 2013, 17:35
Hmm... what happens if you call Q_DECLARE_METATYPE more than once on the same type?

hkhauke
30th December 2013, 09:07
hi,

if you call Q_DECLARE_METATYPE for the second time on the same type, qmetatypestatictype returns a valid id (line 11 in the referenced code from my previous post). in that case, the handler only performs a rather simple test (look at the section highlighted as /* OLD_CODE */ in the code fragment in my previous post, line 51-55 + 64). in particular, the handler is not updated in terms of the callback handlers defined by the bew class declared as a metatype.

in my modification, this is what i do ( refer to section highlighted by /* NEW_CODE */, line 42-50): update the callback pointers whenever Q_DECLARE_METATYPE is called in adjacent calls.

best regards

hauke

mqt
31st July 2015, 13:09
Has it been fixed? I am still having crash in case of dll