From 0e72a846d379ba02ff80ecac2526640a05b872b6 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 17 Nov 2021 14:08:11 -0800 Subject: QObject: Q_ASSERT the object type before calling a PMF The old-syle signal-slot syntax had the advantage of not delivering signals to slots in derived classes after that derived class's destructor had finished running (because we called via the virtual qt_metacall). The new syntax made no checks, so a conversion from the old to the new syntax may introduce crashes or other data corruptions at runtime if the destructor had completed. This commit introduces a Q_ASSERT to print the class name that the object is not any more. Since this is in inline code, this should get enabled for users' debug modes and does not therefore depend on Qt being built in debug mode. It required some Private classes to be adapted to the new form, by exposing the public q_func() in the public: part. Pick-to: 6.2 Fixes: QTBUG-33908 Change-Id: Iccb47e5527544b6fbd75fffd16b874cdc08c1f3e Reviewed-by: Marc Mutz Reviewed-by: Qt CI Bot Reviewed-by: Giuseppe D'Angelo --- src/corelib/kernel/qobject_p.h | 8 +++++++- src/corelib/kernel/qobjectdefs_impl.h | 33 +++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 5 deletions(-) (limited to 'src/corelib/kernel') diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index da171f5c7f..ac77d898da 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -98,9 +98,9 @@ public: class Q_CORE_EXPORT QObjectPrivate : public QObjectData { +public: Q_DECLARE_PUBLIC(QObject) -public: struct ExtraData { ExtraData(QObjectPrivate *ptr) : parent(ptr) { } @@ -492,6 +492,12 @@ using FunctionStorage = typename std::conditional_t< FunctionStorageByValue >; +template inline void assertObjectType(QObjectPrivate *d) +{ + using Obj = std::remove_pointer_t()->q_func())>; + assertObjectType(d->q_ptr); +} + template class QPrivateSlotObject : public QSlotObjectBase, private FunctionStorage { diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h index a3068e1712..da9d0f6390 100644 --- a/src/corelib/kernel/qobjectdefs_impl.h +++ b/src/corelib/kernel/qobjectdefs_impl.h @@ -50,6 +50,7 @@ QT_BEGIN_NAMESPACE class QObject; +class QObjectPrivate; namespace QtPrivate { template struct RemoveRef { typedef T Type; }; @@ -139,6 +140,22 @@ namespace QtPrivate { template struct FunctionPointer { enum {ArgumentCount = -1, IsPointerToMemberFunction = false}; }; + template inline void assertObjectType(QObjectPrivate *d); + template inline void assertObjectType(QObject *o) + { + // ensure all three compile + [[maybe_unused]] auto staticcast = [](QObject *obj) { return static_cast(obj); }; + [[maybe_unused]] auto qobjcast = [](QObject *obj) { return Obj::staticMetaObject.cast(obj); }; +#ifdef __cpp_rtti + [[maybe_unused]] auto dyncast = [](QObject *obj) { return dynamic_cast(obj); }; + auto cast = dyncast; +#else + auto cast = qobjcast; +#endif + Q_ASSERT_X(cast(o), Obj::staticMetaObject.className(), + "Called object is not of the correct type (class destructor may have already run)"); + } + template struct FunctorCall; template struct FunctorCall, List, R, Function> { @@ -148,25 +165,33 @@ namespace QtPrivate { }; template struct FunctorCall, List, R, SlotRet (Obj::*)(SlotArgs...)> { - static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg) { + static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg) + { + assertObjectType(o); (o->*f)((*reinterpret_cast::Type *>(arg[II+1]))...), ApplyReturnValue(arg[0]); } }; template struct FunctorCall, List, R, SlotRet (Obj::*)(SlotArgs...) const> { - static void call(SlotRet (Obj::*f)(SlotArgs...) const, Obj *o, void **arg) { + static void call(SlotRet (Obj::*f)(SlotArgs...) const, Obj *o, void **arg) + { + assertObjectType(o); (o->*f)((*reinterpret_cast::Type *>(arg[II+1]))...), ApplyReturnValue(arg[0]); } }; template struct FunctorCall, List, R, SlotRet (Obj::*)(SlotArgs...) noexcept> { - static void call(SlotRet (Obj::*f)(SlotArgs...) noexcept, Obj *o, void **arg) { + static void call(SlotRet (Obj::*f)(SlotArgs...) noexcept, Obj *o, void **arg) + { + assertObjectType(o); (o->*f)((*reinterpret_cast::Type *>(arg[II+1]))...), ApplyReturnValue(arg[0]); } }; template struct FunctorCall, List, R, SlotRet (Obj::*)(SlotArgs...) const noexcept> { - static void call(SlotRet (Obj::*f)(SlotArgs...) const noexcept, Obj *o, void **arg) { + static void call(SlotRet (Obj::*f)(SlotArgs...) const noexcept, Obj *o, void **arg) + { + assertObjectType(o); (o->*f)((*reinterpret_cast::Type *>(arg[II+1]))...), ApplyReturnValue(arg[0]); } }; -- cgit v1.2.3