QObjectPrivate: encode the version number in the constructor parameters

Instead of allowing the code to start and then possibly fail at runtime.

This isn't a 100% sure solution because it's a function call. With lazy
symbol binding on some OSes, the mistake won't be noticed until the
function call is attempted. However, most OSes now resolve *all* calls
at load time so they can mark the GOT (or equivalent) pages read-only,
meaning the loading of the library will fail.

qversiontagging_p.h is a more sure way in OSes / executable formats it
works on.

Change-Id: If7867a37256b7141001dfffd9bd299bb1bbd7c63
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
Thiago Macieira 2025-01-17 18:04:22 -08:00
parent 1b85143d21
commit 744fda9cb4
6 changed files with 19 additions and 44 deletions

View File

@ -158,11 +158,9 @@ QMetaObject *QObjectData::dynamicMetaObject() const
return metaObject->toDynamicMetaObject(q_ptr);
}
QObjectPrivate::QObjectPrivate(int version)
QObjectPrivate::QObjectPrivate(decltype(QObjectPrivateVersion))
: threadData(nullptr), currentChildBeingDeleted(nullptr)
{
checkForIncompatibleLibraryVersion(version);
// QObjectData initialization
q_ptr = nullptr;
parent = nullptr; // no parent yet. It is set by setParent()

View File

@ -39,6 +39,19 @@ QT_BEGIN_NAMESPACE
#define QT_ANONYMOUS_PRIVATE_PROPERTY(d, text) QT_ANNOTATE_CLASS2(qt_anonymous_private_property, d, text)
#endif
#define QT_CONCAT(B, M, m, u) QT_CONCAT2(B, M, m, u)
#define QT_CONCAT2(B, M, m, u) B ## M ## _ ## m ## _ ## u
#if defined(QT_BUILD_INTERNAL) && !QT_CONFIG(elf_private_full_version)
// Don't check the version parameter in internal builds.
// This allows incompatible versions to be loaded, possibly for testing.
enum QObjectPrivateVersionEnum
#else
enum QT_CONCAT(QtPrivate_, QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_PATCH)
#endif
{ QObjectPrivateVersion = QT_VERSION };
#undef QT_CONCAT
#undef QT_CONCAT2
class QVariant;
class QThreadData;
class QObjectConnectionListVector;
@ -58,8 +71,6 @@ void Q_CORE_EXPORT qt_register_signal_spy_callbacks(QSignalSpyCallbackSet *callb
extern Q_CORE_EXPORT QBasicAtomicPointer<QSignalSpyCallbackSet> qt_signal_spy_callback_set;
enum { QObjectPrivateVersion = QT_VERSION };
class Q_CORE_EXPORT QAbstractDeclarativeData
{
public:
@ -130,14 +141,12 @@ public:
linked list.
*/
QObjectPrivate(int version = QObjectPrivateVersion);
QObjectPrivate(decltype(QObjectPrivateVersion) version = QObjectPrivateVersion);
virtual ~QObjectPrivate();
void deleteChildren();
// used to clear binding storage early in ~QObject
void clearBindingStorage();
inline void checkForIncompatibleLibraryVersion(int version) const;
void setParent_helper(QObject *);
void moveToThread_helper();
void setThreadData_helper(QThreadData *currentData, QThreadData *targetData, QBindingStatus *status);
@ -216,28 +225,6 @@ public:
QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount;
};
/*
Catch mixing of incompatible library versions.
Should be called from the constructor of every non-final subclass
of QObjectPrivate, to ensure we catch incompatibilities between
the intermediate base and subclasses thereof.
*/
inline void QObjectPrivate::checkForIncompatibleLibraryVersion(int version) const
{
#if defined(QT_BUILD_INTERNAL)
// Don't check the version parameter in internal builds.
// This allows incompatible versions to be loaded, possibly for testing.
Q_UNUSED(version);
#else
if (Q_UNLIKELY(version != QObjectPrivateVersion)) {
qFatal("Cannot mix incompatible Qt library (%d.%d.%d) with this library (%d.%d.%d)",
(version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff,
(QObjectPrivateVersion >> 16) & 0xff, (QObjectPrivateVersion >> 8) & 0xff, QObjectPrivateVersion & 0xff);
}
#endif
}
inline bool QObjectPrivate::isDeclarativeSignalConnected(uint signal_index) const
{
return !isDeletingChildren && declarativeData && QAbstractDeclarativeData::isSignalConnected

View File

@ -25,7 +25,7 @@ using namespace Qt::StringLiterals;
// we use a tighter limit for the variables range.
const qreal g_offset = (sizeof(qreal) == sizeof(double)) ? QWIDGETSIZE_MAX : QWIDGETSIZE_MAX / 32;
QGraphicsAnchorPrivate::QGraphicsAnchorPrivate(int version)
QGraphicsAnchorPrivate::QGraphicsAnchorPrivate(decltype(QObjectPrivateVersion) version)
: QObjectPrivate(version), layoutPrivate(nullptr), data(nullptr),
sizePolicy(QSizePolicy::Fixed), preferredSize(0),
hasSize(true)

View File

@ -313,7 +313,7 @@ class QGraphicsAnchorPrivate : public QObjectPrivate
Q_DECLARE_PUBLIC(QGraphicsAnchor)
public:
explicit QGraphicsAnchorPrivate(int version = QObjectPrivateVersion);
explicit QGraphicsAnchorPrivate(decltype(QObjectPrivateVersion) = QObjectPrivateVersion);
~QGraphicsAnchorPrivate();
void setSpacing(qreal value);

View File

@ -103,7 +103,7 @@ static inline bool qRectIntersects(const QRect &r1, const QRect &r2)
extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); // qapplication.cpp
QWidgetPrivate::QWidgetPrivate(int version)
QWidgetPrivate::QWidgetPrivate(decltype(QObjectPrivateVersion) version)
: QObjectPrivate(version)
, focus_next(nullptr)
, focus_prev(nullptr)
@ -164,16 +164,6 @@ QWidgetPrivate::QWidgetPrivate(int version)
return;
}
#ifdef QT_BUILD_INTERNAL
// Don't check the version parameter in internal builds.
// This allows incompatible versions to be loaded, possibly for testing.
Q_UNUSED(version);
#else
if (Q_UNLIKELY(version != QObjectPrivateVersion))
qFatal("Cannot mix incompatible Qt library (version 0x%x) with this library (version 0x%x)",
version, QObjectPrivateVersion);
#endif
willBeWidget = true; // used in QObject's ctor
memset(high_attributes, 0, sizeof(high_attributes));

View File

@ -207,7 +207,7 @@ public:
Q_ENUM(Direction)
// Functions.
explicit QWidgetPrivate(int version = QObjectPrivateVersion);
explicit QWidgetPrivate(decltype(QObjectPrivateVersion) = QObjectPrivateVersion);
~QWidgetPrivate();
static QWidgetPrivate *get(QWidget *w) { return w->d_func(); }