From dc53698ba4476194d0f8b2971eeb132169563962 Mon Sep 17 00:00:00 2001
From: Hatem ElKharashy <hatem.elkharashy@qt.io>
Date: Wed, 22 Apr 2026 13:46:59 +0300
Subject: [PATCH] Fix rendering causing stack overflow

With a deeply nested svg document, the process will run out of stack
if the stack is limited. To fix this, a limit to the number of nested
element is used to stop rendering if the current nested level is
exceeding the max limit.

Task-number: QTBUG-145916
Change-Id: I2047aa3d64332b47a7c2819bcc3219eabbc00891
Reviewed-by: Robert Löhning <robert.loehning@qt.io>
(cherry picked from commit 16119ddc0f8a39a7131d95715c7d9c01e428819d)
(cherry picked from commit b898d5b01c2b1576e6cbe577e2215f59992ef57a)
---

diff --git a/src/svg/qsvgnode.cpp b/src/svg/qsvgnode.cpp
index 37a22df..1df78c4 100644
--- a/src/svg/qsvgnode.cpp
+++ b/src/svg/qsvgnode.cpp
@@ -42,6 +42,16 @@
 #endif
 
     if (shouldDrawNode(p, states)) {
+        quint8 remainingDepth = states.trustedSource ? states.remainingNestedNodes
+                                                     : states.remainingNestedNodes - 1;
+        QScopedValueRollback<quint8> nestedNodesGuard(states.remainingNestedNodes, remainingDepth);
+        if (states.remainingNestedNodes == 0) {
+            qCWarning(lcSvgDraw) << "Too many nested nodes at" << qPrintable(typeName())
+                                 << "exceeding max nested limit of" << QtSvg::renderingMaxNestedNodes << "."
+                                 << "Enable AssumeTrustedSource in QSvgHandler or set QT_SVG_DEFAULT_OPTIONS=2 to disable this check.";
+            return;
+        }
+
         applyStyle(p, states);
         QSvgNode *maskNode = this->hasMask() ? document()->namedNode(this->maskId()) : nullptr;
         QSvgFilterContainer *filterNode = this->hasFilter() ? static_cast<QSvgFilterContainer*>(document()->namedNode(this->filterId()))
@@ -673,7 +683,7 @@
     if (m_displayMode == DisplayMode::NoneMode)
         return false;
 
-    if (document() && document()->options().testFlag(QtSvg::AssumeTrustedSource))
+    if (document() && states.trustedSource)
         return true;
 
     QRectF brect = internalFastBounds(p, states);
diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h
index e2c56da..4afaa06 100644
--- a/src/svg/qsvgstyle_p.h
+++ b/src/svg/qsvgstyle_p.h
@@ -117,6 +117,8 @@
     qreal strokeDashOffset;
     int nestedUseLevel = 0;
     int nestedUseCount = 0;
+    bool trustedSource = false;
+    quint8 remainingNestedNodes = QtSvg::renderingMaxNestedNodes;
     bool vectorEffect; // true if pen is cosmetic
     qint8 imageRendering; // QSvgQualityStyle::ImageRendering
     bool inUse = false; // true if currently in QSvgUseNode
diff --git a/src/svg/qsvgtinydocument.cpp b/src/svg/qsvgtinydocument.cpp
index c63e7e1..29bf184 100644
--- a/src/svg/qsvgtinydocument.cpp
+++ b/src/svg/qsvgtinydocument.cpp
@@ -33,6 +33,7 @@
     , m_fps(30)
     , m_options(options)
 {
+    m_states.trustedSource = m_options.testFlag(QtSvg::AssumeTrustedSource);
 }
 
 QSvgTinyDocument::~QSvgTinyDocument()
diff --git a/src/svg/qtsvgglobal_p.h b/src/svg/qtsvgglobal_p.h
index 28c3b3f..30cd723 100644
--- a/src/svg/qtsvgglobal_p.h
+++ b/src/svg/qtsvgglobal_p.h
@@ -28,6 +28,7 @@
     userSpaceOnUse
 };
 
+constexpr quint8 renderingMaxNestedNodes = 32;
 }
 
 QT_END_NAMESPACE
