From 80606126a98aa91e889ddefa86137fedf7d45dd2 Mon Sep 17 00:00:00 2001 From: Hatem ElKharashy 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 (cherry picked from commit 16119ddc0f8a39a7131d95715c7d9c01e428819d) Reviewed-by: Qt Cherry-pick Bot --- diff --git a/src/svg/qsvgdocument.cpp b/src/svg/qsvgdocument.cpp index 8ec0422..cb4ccb4 100644 --- a/src/svg/qsvgdocument.cpp +++ b/src/svg/qsvgdocument.cpp @@ -30,6 +30,7 @@ , m_fps(30) , m_options(options) { + m_states.trustedSource = m_options.testFlag(QtSvg::AssumeTrustedSource); bool animationEnabled = !m_options.testFlag(QtSvg::DisableAnimations); switch (type) { case QtSvg::AnimatorType::Automatic: diff --git a/src/svg/qsvgnode.cpp b/src/svg/qsvgnode.cpp index 15d0340..e073011 100644 --- a/src/svg/qsvgnode.cpp +++ b/src/svg/qsvgnode.cpp @@ -44,6 +44,16 @@ #endif if (shouldDrawNode(p, states)) { + quint8 remainingDepth = states.trustedSource ? states.remainingNestedNodes + : states.remainingNestedNodes - 1; + QScopedValueRollback 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); applyAnimatedStyle(p, states); QSvgNode *maskNode = this->hasMask() ? document()->namedNode(this->maskId()) : nullptr; @@ -677,7 +687,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 4556ef6..078e0b7 100644 --- a/src/svg/qsvgstyle_p.h +++ b/src/svg/qsvgstyle_p.h @@ -118,6 +118,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/qtsvgglobal_p.h b/src/svg/qtsvgglobal_p.h index 77d2064..4981e47 100644 --- a/src/svg/qtsvgglobal_p.h +++ b/src/svg/qtsvgglobal_p.h @@ -43,6 +43,7 @@ ReverseAngle }; +constexpr quint8 renderingMaxNestedNodes = 32; } QT_END_NAMESPACE