From ea44b50c6e61104cadd6b7c8ede92a4108634232 Mon Sep 17 00:00:00 2001 From: Robert Löhning Date: Wed, 18 Jun 2025 16:02:32 +0200 Subject: [PATCH] Replace check for endless recursion when loading The old check parsed the tree of SvgNodes again and again which lead to quadratic complexity. Instead, set and check a bool where the recursion may actually happen which is faster and only has linear complexity. Partially reverts 0332df304f013ded362537c1f61556098b875352 I chose to have the check in QSvgPattern::renderPattern() because: - It not only appears in the recursive backtrace of the stack-overflow which was fixed using the qudratic check, but also in the backtrace of another, still unfixed stack overflow. That way, both can be fixed by the same patch. Credit to OSS-Fuzz for finding them. - The function already had some error checking and returns a default value when it cannot render the content. In the same way, I can return a QImage of the right size but without any content when the endless recursion is about to happen. [ChangeLog] Speed up loading by replacing check for cyclic elements [ChangeLog] Fix stack overflow when an element references its child element using url() Fixes: QTBUG-137553 Pick-to: 6.8 Change-Id: If011c15fde50dcefeb653d1d5995ff1347e7b5ac Reviewed-by: Hatem ElKharashy (cherry picked from commit 9e5bed9584ab65d56cd5fbac0471e06e37a54412) Reviewed-by: Qt Cherry-pick Bot (cherry picked from commit 90a5331640bb760b0114a7ea4e08b9e42b03e082) --- diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp index 48f9f0b..c871a20 100644 --- a/src/svg/qsvghandler.cpp +++ b/src/svg/qsvghandler.cpp @@ -4647,8 +4647,7 @@ // this point is to do what everyone else seems to do and // ignore the reported namespaceUri completely. if (remainingUnfinishedElements - && startElement(xml->name().toString(), xml->attributes()) - && !detectCyclesAndWarn(m_doc)) { + && startElement(xml->name().toString(), xml->attributes())) { --remainingUnfinishedElements; } else { delete m_doc; diff --git a/src/svg/qsvgstructure.cpp b/src/svg/qsvgstructure.cpp index e08f1bc..d073b1c 100644 --- a/src/svg/qsvgstructure.cpp +++ b/src/svg/qsvgstructure.cpp @@ -800,6 +800,7 @@ m_rect(bounds), m_viewBox(viewBox), m_contentUnits(contentUnits), + m_isRendering(false), m_transform(transform) { @@ -885,6 +886,13 @@ } pattern.fill(Qt::transparent); + if (m_isRendering) { + qCWarning(lcSvgDraw) << "The pattern is trying to render itself recursively. " + "Returning a transparent QImage of the right size."; + return pattern; + } + QScopedValueRollback guard(m_isRendering, true); + // Draw the pattern using our QPainter. QPainter patternPainter(&pattern); QSvgExtraStates patternStates; diff --git a/src/svg/qsvgstructure_p.h b/src/svg/qsvgstructure_p.h index 63a8396..f95508f 100644 --- a/src/svg/qsvgstructure_p.h +++ b/src/svg/qsvgstructure_p.h @@ -241,6 +241,7 @@ QSvgRectF m_rect; QRectF m_viewBox; QtSvg::UnitTypes m_contentUnits; + mutable bool m_isRendering; QTransform m_transform; }; diff --git a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp index b212ad3..56f6664 100644 --- a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +++ b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp @@ -1809,6 +1809,9 @@ // resulted in stack overflow QTest::newRow("cyclic-reference") // id=42532991 << R"-()-"_ba; + // resulted in stack overflow + QTest::newRow("cyclic-reference-from-parent") // id=390467765 + << R"-()-"_ba; } void tst_QSvgRenderer::ossFuzzLoad()