From b389cb31e4ea0b9d6142542b990ceae1fc9b9c7c Mon Sep 17 00:00:00 2001
From: Robert Löhning <robert.loehning@qt.io>
Date: Thu, 26 Mar 2026 13:42:19 +0100
Subject: [PATCH] Test types of nodes before downcasting them

A bad cast in QSvgMarker::drawHelper lead to an endless recursion
resulting in a heap overflow. Credit to OSS-Fuzz which found this as
issue 496327371.

Amends 534d072fe9c060ca3d1b968a717513426c69c956

While fixing that, I found another, similar case and fixed it, too,
although it didn't seem to cause a crash.

Amends 29b848e9ac4e4e13c5b50116a81b1f2677196939

Change-Id: Ia57491aa329fea981307a709c5a6a750125fe2c7
Reviewed-by: Hatem ElKharashy <hatem.elkharashy@qt.io>
(cherry picked from commit e488f852fa18c2afc2842a88eff8f66ad4105a45)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 8368ba6ff143bd6b9a7fdf235918285d9b1f5d2a)
---

diff --git a/src/svg/qsvgstructure.cpp b/src/svg/qsvgstructure.cpp
index c52ad59..8ce005a 100644
--- a/src/svg/qsvgstructure.cpp
+++ b/src/svg/qsvgstructure.cpp
@@ -419,9 +419,10 @@
     const bool isPainting = (boundingRect == nullptr);
     const auto markers = markersForNode(node);
     for (auto &i : markers) {
-        QSvgMarker *markNode = static_cast<QSvgMarker*>(node->document()->namedNode(i.markerId));
-        if (!markNode)
+        QSvgNode *referencedNode = node->document()->namedNode(i.markerId);
+        if (!referencedNode || referencedNode->type() != QSvgNode::Marker)
             continue;
+        QSvgMarker *markNode = static_cast<QSvgMarker *>(referencedNode);
 
         p->save();
         p->translate(i.x, i.y);
@@ -726,8 +727,9 @@
 
     // Chrome seems to return the mask of the mask if a mask is set on the mask
     if (this->hasMask()) {
-        QSvgMask *maskNode = static_cast<QSvgMask*>(document()->namedNode(this->maskId()));
-        if (maskNode) {
+        QSvgNode *referencedNode = document()->namedNode(this->maskId());
+        if (referencedNode && referencedNode->type() == QSvgNode::Mask) {
+            QSvgMask *maskNode = static_cast<QSvgMask *>(referencedNode);
             QRectF boundsRect;
             return maskNode->createMask(p, states, localRect, &boundsRect);
         }
diff --git a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp
index 947f7cd..ed73a8c 100644
--- a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp
+++ b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp
@@ -68,6 +68,8 @@
     void duplicateStyleId();
     void oss_fuzz_23731();
     void oss_fuzz_24131();
+    void oss_fuzz_496327371();
+    void oss_fuzz_496327371_similar();
     void oss_fuzz_24738();
     void oss_fuzz_61586();
     void oss_fuzz_42532991();
@@ -1786,6 +1788,25 @@
     renderer.render(&painter);
 }
 
+void tst_QSvgRenderer::oss_fuzz_496327371()
+{
+    // Bad-cast to QSvgMarker from QSvgLine -> Heap-buffer-overflow
+    QImage image(377, 233, QImage::Format_RGB32);
+    QPainter painter(&image);
+    QSvgRenderer renderer(QByteArray("<svg><line x1=\"4\" id=\"lledr\" marker-end=\"url(#lledr)\" stroke=\"#00f\"/></svg>"));
+    renderer.render(&painter);
+}
+
+void tst_QSvgRenderer::oss_fuzz_496327371_similar()
+{
+    // modeled after 496327371 to test similar problem, needs UBSAN
+    QImage image(377, 233, QImage::Format_RGB32);
+    QPainter painter(&image);
+    QSvgRenderer renderer(QByteArray("<svg><defs><line x1=\"4\" id=\"line\"/><mask id=\"mask\" width=\"2\" height=\"2\" mask=\"url(#line)\"/>"
+                                     "</defs><rect width=\"2\" height=\"2\" mask=\"url(#mask)\"/></svg>"));
+    renderer.render(&painter);
+}
+
 void tst_QSvgRenderer::oss_fuzz_24738()
 {
     // when configured with "-sanitize undefined", this resulted in:
