From 392e44539329017b04757e664400afaa7aacf7f5 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 22 Jul 2022 22:08:24 -0700 Subject: [PATCH] QVariant: don't leak PrivateShared if the constructor throws This means we can't do a tail-call from customConstruct when PrivateShared is in use. But the case without that is still a tail-call. Change-Id: I3859764fed084846bcb0fffd17045d144181ea84 Reviewed-by: Fabian Kosmale --- src/corelib/kernel/qvariant.cpp | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index be7be78d51..b7450cbafa 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -235,6 +235,21 @@ static bool isValidMetaTypeForVariant(const QtPrivate::QMetaTypeInterface *iface return true; } +template static QVariant::PrivateShared * +customConstructShared(const QtPrivate::QMetaTypeInterface *iface, F &&construct) +{ + struct Deleter { + void operator()(QVariant::PrivateShared *p) const + { QVariant::PrivateShared::free(p); } + }; + + // this is exception-safe + std::unique_ptr ptr; + ptr.reset(QVariant::PrivateShared::create(iface)); + construct(ptr->data()); + return ptr.release(); +} + // the type of d has already been set, but other field are not set static void customConstruct(const QtPrivate::QMetaTypeInterface *iface, QVariant::Private *d, const void *copy) @@ -252,20 +267,17 @@ static void customConstruct(const QtPrivate::QMetaTypeInterface *iface, QVariant // ### Qt 7: remove nullptr_t special casing d->is_null = !copy QT6_ONLY(|| isInterfaceFor(iface)); - void *dst; if (QVariant::Private::canUseInternalSpace(iface)) { d->is_shared = false; - dst = &d->data; if (!copy && !iface->defaultCtr) return; // trivial default constructor, we've already memset + construct(iface, d->data.data, copy); } else { - d->data.shared = QVariant::PrivateShared::create(iface); + d->data.shared = customConstructShared(iface, [=](void *where) { + construct(iface, where, copy); + }); d->is_shared = true; - dst = d->data.shared->data(); } - - // now ask QMetaType to construct for us - construct(iface, dst, copy); } static void customClear(QVariant::Private *d) @@ -350,8 +362,9 @@ QVariant::Private::Private(std::piecewise_construct_t, const T &t) new (data.data) T(t); } else { static_assert(!isNothrowQVariantConstructible); // we allocate memory, even if T doesn't - data.shared = QVariant::PrivateShared::create(QtPrivate::qMetaTypeInterfaceForType()); - new (data.shared->data()) T(t); + data.shared = customConstructShared(iface, [=](void *where) { + new (where) T(t); + }); } }