From 62f2be9de24bd3760f507e3ceda5b5a1a7f3dbec Mon Sep 17 00:00:00 2001 From: Arno Rehn Date: Mon, 10 Aug 2020 21:52:14 +0200 Subject: Fix infinite recursion when wrapping a self-contained object twice The previous unit test for self-contained objects only wrapped the test object once. After wrapping, a different code path is taken which still exhibited infinite recursion. This patch addresses both the unit test and the infinite recursion. To fix the problem, a boolean in the ObjectInfo struct is toggled to indicate whether the object in question is currently being wrapped. If that is the case, the recursing code path is skipped. [ChangeLog][General] Fixed infinite recursion when dealing with self contained objects. Fixes: QTBUG-84007 Change-Id: Ie0898fb5f28cec91587897835ff937672d60f2a1 Reviewed-by: Kai Koehne (adapted from commit 0451ef836415c93a6beb68a315a25c6ab27f44fa) Reviewed-by: Paul Wicking --- src/webchannel/qmetaobjectpublisher.cpp | 21 ++++++++++++++------- src/webchannel/qmetaobjectpublisher_p.h | 4 +++- tests/auto/webchannel/tst_webchannel.cpp | 3 +++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp index 677f79c..536eb5c 100644 --- a/src/webchannel/qmetaobjectpublisher.cpp +++ b/src/webchannel/qmetaobjectpublisher.cpp @@ -754,14 +754,21 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result, QWebChannelA wrappedObjects.insert(id, oi); initializePropertyUpdates(object, classInfo); - } else if (wrappedObjects.contains(id)) { - Q_ASSERT(object == wrappedObjects.value(id).object); - // check if this transport is already assigned to the object - if (transport && !wrappedObjects.value(id).transports.contains(transport)) { - wrappedObjects[id].transports.append(transport); - transportedWrappedObjects.insert(transport, id); + } else { + auto oi = wrappedObjects.find(id); + if (oi != wrappedObjects.end() && !oi->isBeingWrapped) { + Q_ASSERT(object == oi->object); + // check if this transport is already assigned to the object + if (transport && !oi->transports.contains(transport)) { + oi->transports.append(transport); + transportedWrappedObjects.insert(transport, id); + } + // QTBUG-84007: Block infinite recursion for self-contained objects + // which have already been wrapped + oi->isBeingWrapped = true; + classInfo = classInfoForObject(object, transport); + oi->isBeingWrapped = false; } - classInfo = classInfoForObject(object, transport); } QJsonObject objectInfo; diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h index 6030de2..bbd9875 100644 --- a/src/webchannel/qmetaobjectpublisher_p.h +++ b/src/webchannel/qmetaobjectpublisher_p.h @@ -292,13 +292,15 @@ private: QHash registeredObjectIds; // Groups individually wrapped objects with their class information and the transports that have access to it. + // Also tags objects that are in the process of being wrapped to prevent infinite recursion. struct ObjectInfo { ObjectInfo(QObject *o = nullptr) - : object(o) + : object(o), isBeingWrapped(false) {} QObject *object; QVector transports; + bool isBeingWrapped; }; // Map of objects wrapped from invocation returns diff --git a/tests/auto/webchannel/tst_webchannel.cpp b/tests/auto/webchannel/tst_webchannel.cpp index a1e824b..181da9e 100644 --- a/tests/auto/webchannel/tst_webchannel.cpp +++ b/tests/auto/webchannel/tst_webchannel.cpp @@ -936,6 +936,9 @@ void TestWebChannel::testInfiniteRecursion() channel.d_func()->publisher->initializeClient(m_dummyTransport); QJsonObject objectInfo = channel.d_func()->publisher->wrapResult(QVariant::fromValue(&obj), m_dummyTransport).toObject(); + + // Wrap the result twice to test for QTBUG-84007. A single result wrap will not trigger all recursion paths. + objectInfo = channel.d_func()->publisher->wrapResult(QVariant::fromValue(&obj), m_dummyTransport).toObject(); } void TestWebChannel::testAsyncObject() -- cgit v1.2.3