From 04ac252ecbea01fb1388afb0cf8887b5b417155a Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 8 Nov 2022 08:20:04 +0100 Subject: QML: Check for stack overflows when creating objects Fixes: QTBUG-106875 Change-Id: I3b0abda6948b79a9e3cf263f27885037fff1804c Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel (cherry picked from commit edc01fbfa430d6f0ce66f1871ab28e0f691ee252) --- src/qml/jsruntime/qv4engine_p.h | 10 ++++++++-- src/qml/jsruntime/qv4jsonobject.cpp | 2 +- src/qml/qml/qqmlincubator.cpp | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 194d5a9c95..83bf9dc102 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -778,6 +778,9 @@ public: int argc, void **args, QMetaType *types); private: + template + friend struct ExecutionEngineCallDepthRecorder; + QV4::ReturnedValue fromData( const QMetaType &type, const void *ptr, const QVariant *variant = nullptr); @@ -813,12 +816,15 @@ private: #define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); +template struct ExecutionEngineCallDepthRecorder { ExecutionEngine *ee; - ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ++ee->callDepth; } - ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; } + ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ee->callDepth += Frames; } + ~ExecutionEngineCallDepthRecorder() { ee->callDepth -= Frames; } + + bool hasOverflow() const { return ee->callDepth >= ExecutionEngine::maxCallDepth; } }; inline bool ExecutionEngine::checkStackLimits() diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 1b5c4244fe..f95c24c157 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -665,7 +665,7 @@ public: bool foundProblem() const { return m_callDepthRecorder.ee->hasException; } private: - ExecutionEngineCallDepthRecorder m_callDepthRecorder; + ExecutionEngineCallDepthRecorder<1> m_callDepthRecorder; }; static QString quote(const QString &str) diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index 0f70ac0efc..6fc2a80b4b 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -269,6 +269,7 @@ void QQmlIncubatorPrivate::forceCompletion(QQmlInstantiationInterrupt &i) } } + void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) { if (!compilationUnit) @@ -280,6 +281,20 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) // get a copy of the engine pointer as it might get reset; QQmlEnginePrivate *enginePriv = this->enginePriv; + // Incubating objects takes quite a bit more stack space than our usual V4 function + enum { EstimatedSizeInV4Frames = 2 }; + QV4::ExecutionEngineCallDepthRecorder callDepthRecorder( + compilationUnit->engine); + if (callDepthRecorder.hasOverflow()) { + QQmlError error; + error.setMessageType(QtCriticalMsg); + error.setUrl(compilationUnit->url()); + error.setDescription(QQmlComponent::tr("Maximum call stack size exceeded.")); + errors << error; + progress = QQmlIncubatorPrivate::Completed; + goto finishIncubate; + } + if (!vmeGuard.isOK()) { QQmlError error; error.setMessageType(QtInfoMsg); -- cgit v1.2.3