7a198f
From faec595a1721a2496e9c258917facbb564f85854 Mon Sep 17 00:00:00 2001
7a198f
From: rpm-build <rpm-build>
7a198f
Date: Wed, 13 May 2020 17:53:13 -0500
7a198f
Subject: [PATCH] CVE-2019-20446.patch
7a198f
7a198f
---
7a198f
 librsvg/rsvg-base.c                           |  90 +++++++++---
7a198f
 librsvg/rsvg-private.h                        |   5 +-
7a198f
 rsvg_internals/src/drawing_ctx.rs             |  23 ++--
7a198f
 rsvg_internals/src/structure.rs               |  21 ++-
7a198f
 tests/errors.c                                |  52 ++++++-
7a198f
 .../errors/308-doubly-recursive-use.svg       |  13 ++
7a198f
 tests/fixtures/errors/308-recursive-use.svg   |   9 ++
7a198f
 tests/fixtures/errors/308-use-self-ref.svg    |   7 +
7a198f
 .../errors/515-pattern-billion-laughs.svg     | 130 ++++++++++++++++++
7a198f
 .../errors/515-too-many-elements.svgz         | Bin 0 -> 40811 bytes
7a198f
 10 files changed, 310 insertions(+), 40 deletions(-)
7a198f
 create mode 100644 tests/fixtures/errors/308-doubly-recursive-use.svg
7a198f
 create mode 100644 tests/fixtures/errors/308-recursive-use.svg
7a198f
 create mode 100644 tests/fixtures/errors/308-use-self-ref.svg
7a198f
 create mode 100644 tests/fixtures/errors/515-pattern-billion-laughs.svg
7a198f
 create mode 100644 tests/fixtures/errors/515-too-many-elements.svgz
7a198f
7a198f
diff --git a/librsvg/rsvg-base.c b/librsvg/rsvg-base.c
7a198f
index dbad819..af3d43c 100644
7a198f
--- a/librsvg/rsvg-base.c
7a198f
+++ b/librsvg/rsvg-base.c
7a198f
@@ -431,12 +431,29 @@ node_set_atts (RsvgNode * node, RsvgHandle *handle, const NodeCreator *creator,
7a198f
     }
7a198f
 }
7a198f
 
7a198f
+static gboolean
7a198f
+loading_limits_exceeded (RsvgHandle *handle)
7a198f
+{
7a198f
+    /* This is a mitigation for SVG files which create millions of elements
7a198f
+     * in an attempt to exhaust memory.  We don't allow loading more than
7a198f
+     * this number of elements during the initial streaming load process.
7a198f
+     */
7a198f
+    return handle->priv->num_loaded_elements > 200000;
7a198f
+}
7a198f
+
7a198f
 static void
7a198f
 rsvg_standard_element_start (RsvgHandle *handle, const char *name, RsvgPropertyBag * atts)
7a198f
 {
7a198f
     const NodeCreator *creator;
7a198f
     RsvgNode *newnode = NULL;
7a198f
 
7a198f
+    if (loading_limits_exceeded (handle)) {
7a198f
+        g_set_error (handle->priv->error, RSVG_ERROR, 0, "instancing limit");
7a198f
+
7a198f
+        xmlStopParser (handle->priv->ctxt);
7a198f
+        return;
7a198f
+    }
7a198f
+
7a198f
     creator = get_node_creator_for_element_name (name);
7a198f
     g_assert (creator != NULL && creator->create_fn != NULL);
7a198f
 
7a198f
@@ -456,6 +473,7 @@ rsvg_standard_element_start (RsvgHandle *handle, const char *name, RsvgPropertyB
7a198f
         handle->priv->treebase = rsvg_node_ref (newnode);
7a198f
     }
7a198f
 
7a198f
+    handle->priv->num_loaded_elements += 1;
7a198f
     handle->priv->currentnode = rsvg_node_ref (newnode);
7a198f
 
7a198f
     node_set_atts (newnode, handle, creator, atts);
7a198f
@@ -1641,6 +1659,52 @@ rsvg_push_discrete_layer (RsvgDrawingCtx * ctx)
7a198f
     ctx->render->push_discrete_layer (ctx);
7a198f
 }
7a198f
 
7a198f
+void
7a198f
+rsvg_drawing_ctx_increase_num_elements_acquired (RsvgDrawingCtx *draw_ctx)
7a198f
+{
7a198f
+    draw_ctx->num_elements_acquired++;
7a198f
+}
7a198f
+
7a198f
+/* This is a mitigation for the security-related bugs:
7a198f
+ * https://gitlab.gnome.org/GNOME/librsvg/issues/323
7a198f
+ * https://gitlab.gnome.org/GNOME/librsvg/issues/515
7a198f
+ *
7a198f
+ * Imagine the XML [billion laughs attack], but done in SVG's terms:
7a198f
+ *
7a198f
+ * - #323 above creates deeply nested groups of `<use>` elements.
7a198f
+ * The first one references the second one ten times, the second one
7a198f
+ * references the third one ten times, and so on.  In the file given,
7a198f
+ * this causes 10^17 objects to be rendered.  While this does not
7a198f
+ * exhaust memory, it would take a really long time.
7a198f
+ *
7a198f
+ * - #515 has deeply nested references of `<pattern>` elements.  Each
7a198f
+ * object inside each pattern has an attribute
7a198f
+ * fill="url(#next_pattern)", so the number of final rendered objects
7a198f
+ * grows exponentially.
7a198f
+ *
7a198f
+ * We deal with both cases by placing a limit on how many references
7a198f
+ * will be resolved during the SVG rendering process, that is,
7a198f
+ * how many `url(#foo)` will be resolved.
7a198f
+ *
7a198f
+ * [billion laughs attack]: https://bitbucket.org/tiran/defusedxml
7a198f
+ */
7a198f
+static gboolean
7a198f
+limits_exceeded (RsvgDrawingCtx *draw_ctx)
7a198f
+{
7a198f
+    return draw_ctx->num_elements_acquired > 500000;
7a198f
+}
7a198f
+
7a198f
+RsvgNode *
7a198f
+rsvg_drawing_ctx_acquire_node_ref (RsvgDrawingCtx * ctx, RsvgNode *node)
7a198f
+{
7a198f
+  if (g_slist_find (ctx->acquired_nodes, node))
7a198f
+    return NULL;
7a198f
+
7a198f
+  ctx->acquired_nodes = g_slist_prepend (ctx->acquired_nodes, node);
7a198f
+
7a198f
+  return node;
7a198f
+}
7a198f
+
7a198f
 /*
7a198f
  * rsvg_drawing_ctx_acquire_node:
7a198f
  * @ctx: The drawing context in use
7a198f
@@ -1668,16 +1732,15 @@ rsvg_drawing_ctx_acquire_node (RsvgDrawingCtx * ctx, const char *url)
7a198f
   if (url == NULL)
7a198f
       return NULL;
7a198f
 
7a198f
+  rsvg_drawing_ctx_increase_num_elements_acquired (ctx);
7a198f
+  if (limits_exceeded (ctx))
7a198f
+      return NULL;
7a198f
+
7a198f
   node = rsvg_defs_lookup (ctx->defs, url);
7a198f
   if (node == NULL)
7a198f
     return NULL;
7a198f
 
7a198f
-  if (g_slist_find (ctx->acquired_nodes, node))
7a198f
-    return NULL;
7a198f
-
7a198f
-  ctx->acquired_nodes = g_slist_prepend (ctx->acquired_nodes, node);
7a198f
-
7a198f
-  return node;
7a198f
+  return rsvg_drawing_ctx_acquire_node_ref (ctx, node);
7a198f
 }
7a198f
 
7a198f
 /**
7a198f
@@ -1734,18 +1797,9 @@ rsvg_drawing_ctx_release_node (RsvgDrawingCtx * ctx, RsvgNode *node)
7a198f
   if (node == NULL)
7a198f
     return;
7a198f
 
7a198f
-  g_return_if_fail (ctx->acquired_nodes != NULL);
7a198f
-  g_return_if_fail (ctx->acquired_nodes->data == node);
7a198f
-
7a198f
   ctx->acquired_nodes = g_slist_remove (ctx->acquired_nodes, node);
7a198f
 }
7a198f
 
7a198f
-void
7a198f
-rsvg_drawing_ctx_increase_num_elements_rendered_through_use (RsvgDrawingCtx *draw_ctx)
7a198f
-{
7a198f
-    draw_ctx->num_elements_rendered_through_use++;
7a198f
-}
7a198f
-
7a198f
 void
7a198f
 rsvg_drawing_ctx_add_node_and_ancestors_to_stack (RsvgDrawingCtx *draw_ctx, RsvgNode *node)
7a198f
 {
7a198f
@@ -1759,12 +1813,6 @@ rsvg_drawing_ctx_add_node_and_ancestors_to_stack (RsvgDrawingCtx *draw_ctx, Rsvg
7a198f
     }
7a198f
 }
7a198f
 
7a198f
-static gboolean
7a198f
-limits_exceeded (RsvgDrawingCtx *draw_ctx)
7a198f
-{
7a198f
-    return draw_ctx->num_elements_rendered_through_use > 500000;
7a198f
-}
7a198f
-
7a198f
 gboolean
7a198f
 rsvg_drawing_ctx_draw_node_from_stack (RsvgDrawingCtx *ctx, RsvgNode *node, int dominate)
7a198f
 {
7a198f
diff --git a/librsvg/rsvg-private.h b/librsvg/rsvg-private.h
7a198f
index aeec8d5..06f4c2b 100644
7a198f
--- a/librsvg/rsvg-private.h
7a198f
+++ b/librsvg/rsvg-private.h
7a198f
@@ -164,6 +164,7 @@ struct RsvgHandlePrivate {
7a198f
      */
7a198f
     RsvgSaxHandler *handler;
7a198f
     int handler_nest;
7a198f
+    gsize num_loaded_elements;
7a198f
 
7a198f
     GHashTable *entities;       /* g_malloc'd string -> xmlEntityPtr */
7a198f
 
7a198f
@@ -200,7 +201,7 @@ struct RsvgDrawingCtx {
7a198f
     RsvgState *state;
7a198f
     GError **error;
7a198f
     RsvgDefs *defs;
7a198f
-    gsize num_elements_rendered_through_use;
7a198f
+    gsize num_elements_acquired;
7a198f
     PangoContext *pango_context;
7a198f
     double dpi_x, dpi_y;
7a198f
     RsvgViewBox vb;
7a198f
@@ -502,6 +503,8 @@ RsvgNode *rsvg_drawing_ctx_acquire_node         (RsvgDrawingCtx * ctx, const cha
7a198f
 G_GNUC_INTERNAL
7a198f
 RsvgNode *rsvg_drawing_ctx_acquire_node_of_type (RsvgDrawingCtx * ctx, const char *url, RsvgNodeType type);
7a198f
 G_GNUC_INTERNAL
7a198f
+RsvgNode *rsvg_drawing_ctx_acquire_node_ref     (RsvgDrawingCtx * ctx, RsvgNode *node);
7a198f
+G_GNUC_INTERNAL
7a198f
 void rsvg_drawing_ctx_release_node              (RsvgDrawingCtx * ctx, RsvgNode *node);
7a198f
 
7a198f
 G_GNUC_INTERNAL
7a198f
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
7a198f
index 79f0c9f..631b073 100644
7a198f
--- a/rsvg_internals/src/drawing_ctx.rs
7a198f
+++ b/rsvg_internals/src/drawing_ctx.rs
7a198f
@@ -32,6 +32,11 @@ extern "C" {
7a198f
 
7a198f
     fn rsvg_drawing_ctx_pop_view_box(draw_ctx: *const RsvgDrawingCtx);
7a198f
 
7a198f
+    fn rsvg_drawing_ctx_acquire_node_ref(
7a198f
+        draw_ctx: *const RsvgDrawingCtx,
7a198f
+        node: *const RsvgNode,
7a198f
+    ) -> *mut RsvgNode;
7a198f
+
7a198f
     fn rsvg_drawing_ctx_acquire_node(
7a198f
         draw_ctx: *const RsvgDrawingCtx,
7a198f
         url: *const libc::c_char,
7a198f
@@ -45,8 +50,6 @@ extern "C" {
7a198f
 
7a198f
     fn rsvg_drawing_ctx_release_node(draw_ctx: *const RsvgDrawingCtx, node: *mut RsvgNode);
7a198f
 
7a198f
-    fn rsvg_drawing_ctx_increase_num_elements_rendered_through_use(draw_ctx: *const RsvgDrawingCtx);
7a198f
-
7a198f
     fn rsvg_drawing_ctx_get_current_state_affine(draw_ctx: *const RsvgDrawingCtx) -> cairo::Matrix;
7a198f
 
7a198f
     fn rsvg_drawing_ctx_set_current_state_affine(
7a198f
@@ -149,6 +152,16 @@ pub fn pop_view_box(draw_ctx: *const RsvgDrawingCtx) {
7a198f
     }
7a198f
 }
7a198f
 
7a198f
+pub fn acquire_node_ref(draw_ctx: *const RsvgDrawingCtx, node: *const RsvgNode) -> Option<AcquiredNode> {
7a198f
+    let raw_node = unsafe { rsvg_drawing_ctx_acquire_node_ref(draw_ctx, node) };
7a198f
+
7a198f
+    if raw_node.is_null() {
7a198f
+        None
7a198f
+    } else {
7a198f
+        Some(AcquiredNode(draw_ctx, raw_node))
7a198f
+    }
7a198f
+}
7a198f
+
7a198f
 pub fn get_acquired_node(draw_ctx: *const RsvgDrawingCtx, url: &str) -> Option<AcquiredNode> {
7a198f
     let raw_node = unsafe { rsvg_drawing_ctx_acquire_node(draw_ctx, str::to_glib_none(url).0) };
7a198f
 
7a198f
@@ -290,12 +303,6 @@ pub fn state_pop(draw_ctx: *const RsvgDrawingCtx) {
7a198f
     }
7a198f
 }
7a198f
 
7a198f
-pub fn increase_num_elements_rendered_through_use(draw_ctx: *const RsvgDrawingCtx) {
7a198f
-    unsafe {
7a198f
-        rsvg_drawing_ctx_increase_num_elements_rendered_through_use(draw_ctx);
7a198f
-    }
7a198f
-}
7a198f
-
7a198f
 pub struct AcquiredNode(*const RsvgDrawingCtx, *mut RsvgNode);
7a198f
 
7a198f
 impl Drop for AcquiredNode {
7a198f
diff --git a/rsvg_internals/src/structure.rs b/rsvg_internals/src/structure.rs
7a198f
index 71c9ff0..e4234ae 100644
7a198f
--- a/rsvg_internals/src/structure.rs
7a198f
+++ b/rsvg_internals/src/structure.rs
7a198f
@@ -278,6 +278,20 @@ impl NodeTrait for NodeUse {
7a198f
             return;
7a198f
         }
7a198f
 
7a198f
+        // <use> is an element that is used directly, unlike
7a198f
+        // <pattern>, which is used through a fill="url(#...)"
7a198f
+        // reference.  However, <use> will always reference another
7a198f
+        // element, potentially itself or an ancestor of itself (or
7a198f
+        // another <use> which references the first one, etc.).  So,
7a198f
+        // we acquire the <use> element itself so that circular
7a198f
+        // references can be caught.
7a198f
+        let self_box = box_node(node.clone());
7a198f
+        let self_acquired = drawing_ctx::acquire_node_ref(draw_ctx, self_box);
7a198f
+        rsvg_node_unref(self_box);
7a198f
+        if self_acquired.is_none() {
7a198f
+            return;
7a198f
+        }
7a198f
+
7a198f
         let child = if let Some(acquired) =
7a198f
             drawing_ctx::get_acquired_node(draw_ctx, link.as_ref().unwrap())
7a198f
         {
7a198f
@@ -286,13 +300,6 @@ impl NodeTrait for NodeUse {
7a198f
             return;
7a198f
         };
7a198f
 
7a198f
-        if Node::is_ancestor(node.clone(), child.clone()) {
7a198f
-            // or, if we're <use>'ing ourselves
7a198f
-            return;
7a198f
-        }
7a198f
-
7a198f
-        drawing_ctx::increase_num_elements_rendered_through_use(draw_ctx);
7a198f
-
7a198f
         let nx = self.x.get().normalize(draw_ctx);
7a198f
         let ny = self.y.get().normalize(draw_ctx);
7a198f
 
7a198f
diff --git a/tests/errors.c b/tests/errors.c
7a198f
index f370d60..ab5898a 100644
7a198f
--- a/tests/errors.c
7a198f
+++ b/tests/errors.c
7a198f
@@ -22,10 +22,29 @@ get_test_filename (const char *basename) {
7a198f
                              basename,
7a198f
                              NULL);
7a198f
 }
7a198f
+
7a198f
+static void
7a198f
+test_loading_error (gconstpointer data)
7a198f
+{
7a198f
+    const char *basename = data;
7a198f
+    char *filename = get_test_filename (basename);
7a198f
+    RsvgHandle *handle;
7a198f
+    GError *error = NULL;
7a198f
+
7a198f
+    handle = rsvg_handle_new_from_file (filename, &error);
7a198f
+    g_free (filename);
7a198f
+
7a198f
+    g_assert (handle == NULL);
7a198f
+    g_assert (g_error_matches (error, RSVG_ERROR, RSVG_ERROR_FAILED));
7a198f
+
7a198f
+    g_error_free (error);
7a198f
+}
7a198f
+
7a198f
 static void
7a198f
-test_instancing_limit (void)
7a198f
+test_instancing_limit (gconstpointer data)
7a198f
 {
7a198f
-    char *filename = get_test_filename ("323-nested-use.svg");
7a198f
+    const char *basename = data;
7a198f
+    char *filename = get_test_filename (basename);
7a198f
     RsvgHandle *handle;
7a198f
     GError *error = NULL;
7a198f
     cairo_surface_t *surf;
7a198f
@@ -49,7 +68,34 @@ main (int argc, char **argv)
7a198f
 {
7a198f
     g_test_init (&argc, &argv, NULL);
7a198f
 
7a198f
-    g_test_add_func ("/errors/instancing_limit", test_instancing_limit);
7a198f
+    g_test_add_data_func_full ("/errors/instancing_limit/323-nested-use.svg",
7a198f
+                               "323-nested-use.svg",
7a198f
+                               test_instancing_limit,
7a198f
+                               NULL);
7a198f
+
7a198f
+    g_test_add_data_func_full ("/errors/instancing_limit/515-pattern-billion-laughs.svg",
7a198f
+                               "515-pattern-billion-laughs.svg",
7a198f
+                               test_instancing_limit,
7a198f
+                               NULL);
7a198f
+
7a198f
+    g_test_add_data_func_full ("/errors/instancing_limit/308-use-self-ref.svg",
7a198f
+                               "308-use-self-ref.svg",
7a198f
+                               test_instancing_limit,
7a198f
+                               NULL);
7a198f
+    g_test_add_data_func_full ("/errors/instancing_limit/308-recursive-use.svg",
7a198f
+                               "308-recursive-use.svg",
7a198f
+                               test_instancing_limit,
7a198f
+                               NULL);
7a198f
+    g_test_add_data_func_full ("/errors/instancing_limit/308-doubly-recursive-use.svg",
7a198f
+                               "308-doubly-recursive-use.svg",
7a198f
+                               test_instancing_limit,
7a198f
+                               NULL);
7a198f
+
7a198f
+    g_test_add_data_func_full ("/errors/515-too-many-elements.svgz",
7a198f
+                               "515-too-many-elements.svgz",
7a198f
+                               test_loading_error,
7a198f
+                               NULL);
7a198f
+
7a198f
 
7a198f
     return g_test_run ();
7a198f
 }
7a198f
diff --git a/tests/fixtures/errors/308-doubly-recursive-use.svg b/tests/fixtures/errors/308-doubly-recursive-use.svg
7a198f
new file mode 100644
7a198f
index 0000000..9b248a6
7a198f
--- /dev/null
7a198f
+++ b/tests/fixtures/errors/308-doubly-recursive-use.svg
7a198f
@@ -0,0 +1,13 @@
7a198f
+<svg>
7a198f
+  <defs>
7a198f
+    <g id="one">
7a198f
+      <use xlink:href="#two"/>
7a198f
+    </g>
7a198f
+
7a198f
+    <g id="two">
7a198f
+      <use xlink:href="#one"/>
7a198f
+    </g>
7a198f
+  </defs>
7a198f
+
7a198f
+  <use xlink:href="#one"/>
7a198f
+</svg>
7a198f
diff --git a/tests/fixtures/errors/308-recursive-use.svg b/tests/fixtures/errors/308-recursive-use.svg
7a198f
new file mode 100644
7a198f
index 0000000..f5d00bf
7a198f
--- /dev/null
7a198f
+++ b/tests/fixtures/errors/308-recursive-use.svg
7a198f
@@ -0,0 +1,9 @@
7a198f
+<svg>
7a198f
+  <defs>
7a198f
+    <g id="one">
7a198f
+      <use xlink:href="#one"/>
7a198f
+    </g>
7a198f
+  </defs>
7a198f
+
7a198f
+  <use xlink:href="#one"/>
7a198f
+</svg>
7a198f
diff --git a/tests/fixtures/errors/308-use-self-ref.svg b/tests/fixtures/errors/308-use-self-ref.svg
7a198f
new file mode 100644
7a198f
index 0000000..dbf14c5
7a198f
--- /dev/null
7a198f
+++ b/tests/fixtures/errors/308-use-self-ref.svg
7a198f
@@ -0,0 +1,7 @@
7a198f
+<svg>
7a198f
+  <defs>
7a198f
+    <use id="one" xlink:href="#one"/>
7a198f
+  </defs>
7a198f
+
7a198f
+  <use xlink:href="#one"/>
7a198f
+</svg>
7a198f
diff --git a/tests/fixtures/errors/515-pattern-billion-laughs.svg b/tests/fixtures/errors/515-pattern-billion-laughs.svg
7a198f
new file mode 100644
7a198f
index 0000000..a306960
7a198f
--- /dev/null
7a198f
+++ b/tests/fixtures/errors/515-pattern-billion-laughs.svg
7a198f
@@ -0,0 +1,130 @@
7a198f
+
7a198f
+
7a198f
+  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
7a198f
+
7a198f
+     xmlns="http://www.w3.org/2000/svg">
7a198f
+  <defs>
7a198f
+    <pattern id="z" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternTransform="scale(10,10)">
7a198f
+      <rect x="0" y="0" width="20" height="20" fill="url(#i)" stroke="yellow"/>
7a198f
+    </pattern>
7a198f
+
7a198f
+	<pattern id="i" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternTransform="scale(0.5,0.5)">
7a198f
+      <rect x="0" y="0" width="20" height="20" fill="url(#h)" stroke="green" />
7a198f
+	  <rect x="1" y="1" width="20" height="20" fill="url(#h)" stroke="brown" />
7a198f
+	  <rect x="2" y="2" width="20" height="20" fill="url(#h)" stroke="pink" />
7a198f
+	  <rect x="3" y="3" width="20" height="20" fill="url(#h)" stroke="grey" />
7a198f
+	  <rect x="4" y="3" width="20" height="20" fill="url(#h)" stroke="cyan" />
7a198f
+	  <rect x="5" y="3" width="20" height="20" fill="url(#h)" stroke="green" />
7a198f
+	  <rect x="6" y="3" width="20" height="20" fill="url(#h)" stroke="brown" />
7a198f
+	  <rect x="7" y="3" width="20" height="20" fill="url(#h)" stroke="pink" />
7a198f
+	  <rect x="8" y="3" width="20" height="20" fill="url(#h)" stroke="grey" />
7a198f
+	  <rect x="9" y="3" width="20" height="20" fill="url(#h)" stroke="cyan" />
7a198f
+    </pattern>
7a198f
+
7a198f
+	<pattern id="h" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternTransform="scale(0.5,0.5)">
7a198f
+      <rect x="0" y="0" width="20" height="20" fill="url(#g)" stroke="green" />
7a198f
+	  <rect x="1" y="1" width="20" height="20" fill="url(#g)" stroke="brown" />
7a198f
+	  <rect x="2" y="2" width="20" height="20" fill="url(#g)" stroke="pink" />
7a198f
+	  <rect x="3" y="3" width="20" height="20" fill="url(#g)" stroke="grey" />
7a198f
+	  <rect x="4" y="3" width="20" height="20" fill="url(#g)" stroke="cyan" />
7a198f
+	  <rect x="5" y="3" width="20" height="20" fill="url(#g)" stroke="green" />
7a198f
+	  <rect x="6" y="3" width="20" height="20" fill="url(#g)" stroke="brown" />
7a198f
+	  <rect x="7" y="3" width="20" height="20" fill="url(#g)" stroke="pink" />
7a198f
+	  <rect x="8" y="3" width="20" height="20" fill="url(#g)" stroke="grey" />
7a198f
+	  <rect x="9" y="3" width="20" height="20" fill="url(#g)" stroke="cyan" />
7a198f
+    </pattern>
7a198f
+
7a198f
+	<pattern id="g" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternTransform="scale(0.5,0.5)">
7a198f
+      <rect x="0" y="0" width="20" height="20" fill="url(#f)" stroke="green" />
7a198f
+	  <rect x="1" y="1" width="20" height="20" fill="url(#f)" stroke="brown" />
7a198f
+	  <rect x="2" y="2" width="20" height="20" fill="url(#f)" stroke="pink" />
7a198f
+	  <rect x="3" y="3" width="20" height="20" fill="url(#f)" stroke="grey" />
7a198f
+	  <rect x="4" y="3" width="20" height="20" fill="url(#f)" stroke="cyan" />
7a198f
+	  <rect x="5" y="3" width="20" height="20" fill="url(#f)" stroke="green" />
7a198f
+	  <rect x="6" y="3" width="20" height="20" fill="url(#f)" stroke="brown" />
7a198f
+	  <rect x="7" y="3" width="20" height="20" fill="url(#f)" stroke="pink" />
7a198f
+	  <rect x="8" y="3" width="20" height="20" fill="url(#f)" stroke="grey" />
7a198f
+	  <rect x="9" y="3" width="20" height="20" fill="url(#f)" stroke="cyan" />
7a198f
+    </pattern>
7a198f
+
7a198f
+	<pattern id="f" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternTransform="scale(0.5,0.5)">
7a198f
+      <rect x="0" y="0" width="20" height="20" fill="url(#e)" stroke="green" />
7a198f
+	  <rect x="1" y="1" width="20" height="20" fill="url(#e)" stroke="brown" />
7a198f
+	  <rect x="2" y="2" width="20" height="20" fill="url(#e)" stroke="pink" />
7a198f
+	  <rect x="3" y="3" width="20" height="20" fill="url(#e)" stroke="grey" />
7a198f
+	  <rect x="4" y="3" width="20" height="20" fill="url(#e)" stroke="cyan" />
7a198f
+	  <rect x="5" y="3" width="20" height="20" fill="url(#e)" stroke="green" />
7a198f
+	  <rect x="6" y="3" width="20" height="20" fill="url(#e)" stroke="brown" />
7a198f
+	  <rect x="7" y="3" width="20" height="20" fill="url(#e)" stroke="pink" />
7a198f
+	  <rect x="8" y="3" width="20" height="20" fill="url(#e)" stroke="grey" />
7a198f
+	  <rect x="9" y="3" width="20" height="20" fill="url(#e)" stroke="cyan" />
7a198f
+    </pattern>
7a198f
+
7a198f
+	<pattern id="e" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternTransform="scale(0.5,0.5)">
7a198f
+      <rect x="0" y="0" width="20" height="20" fill="url(#d)" stroke="green" />
7a198f
+	  <rect x="1" y="1" width="20" height="20" fill="url(#d)" stroke="brown" />
7a198f
+	  <rect x="2" y="2" width="20" height="20" fill="url(#d)" stroke="pink" />
7a198f
+	  <rect x="3" y="3" width="20" height="20" fill="url(#d)" stroke="grey" />
7a198f
+	  <rect x="4" y="3" width="20" height="20" fill="url(#d)" stroke="cyan" />
7a198f
+	  <rect x="5" y="3" width="20" height="20" fill="url(#d)" stroke="green" />
7a198f
+	  <rect x="6" y="3" width="20" height="20" fill="url(#d)" stroke="brown" />
7a198f
+	  <rect x="7" y="3" width="20" height="20" fill="url(#d)" stroke="pink" />
7a198f
+	  <rect x="8" y="3" width="20" height="20" fill="url(#d)" stroke="grey" />
7a198f
+	  <rect x="9" y="3" width="20" height="20" fill="url(#d)" stroke="cyan" />
7a198f
+    </pattern>
7a198f
+
7a198f
+    <pattern id="d" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternTransform="scale(0.5,0.5)">
7a198f
+      <rect x="0" y="0" width="20" height="20" fill="url(#c)" stroke="green" />
7a198f
+	  <rect x="1" y="1" width="20" height="20" fill="url(#c)" stroke="brown" />
7a198f
+	  <rect x="2" y="2" width="20" height="20" fill="url(#c)" stroke="pink" />
7a198f
+	  <rect x="3" y="3" width="20" height="20" fill="url(#c)" stroke="grey" />
7a198f
+	  <rect x="4" y="3" width="20" height="20" fill="url(#c)" stroke="cyan" />
7a198f
+	  <rect x="5" y="3" width="20" height="20" fill="url(#c)" stroke="green" />
7a198f
+	  <rect x="6" y="3" width="20" height="20" fill="url(#c)" stroke="brown" />
7a198f
+	  <rect x="7" y="3" width="20" height="20" fill="url(#c)" stroke="pink" />
7a198f
+	  <rect x="8" y="3" width="20" height="20" fill="url(#c)" stroke="grey" />
7a198f
+	  <rect x="9" y="3" width="20" height="20" fill="url(#c)" stroke="cyan" />
7a198f
+    </pattern>
7a198f
+    <pattern id="c" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternTransform="scale(0.5,0.5)">
7a198f
+      <rect x="0" y="0" width="20" height="20" fill="url(#b)" stroke="green" />
7a198f
+	  <rect x="1" y="1" width="20" height="20" fill="url(#b)" stroke="brown" />
7a198f
+	  <rect x="2" y="2" width="20" height="20" fill="url(#b)" stroke="pink" />
7a198f
+	  <rect x="3" y="3" width="20" height="20" fill="url(#b)" stroke="grey" />
7a198f
+	  <rect x="4" y="3" width="20" height="20" fill="url(#b)" stroke="cyan" />
7a198f
+	  <rect x="5" y="3" width="20" height="20" fill="url(#b)" stroke="green" />
7a198f
+	  <rect x="6" y="3" width="20" height="20" fill="url(#b)" stroke="brown" />
7a198f
+	  <rect x="7" y="3" width="20" height="20" fill="url(#b)" stroke="pink" />
7a198f
+	  <rect x="8" y="3" width="20" height="20" fill="url(#b)" stroke="grey" />
7a198f
+	  <rect x="9" y="3" width="20" height="20" fill="url(#b)" stroke="cyan" />
7a198f
+    </pattern>
7a198f
+    <pattern id="b" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternTransform="scale(0.5,0.5)">
7a198f
+	  <rect x="0" y="0" width="20" height="20" fill="url(#a)" stroke="green" />
7a198f
+	  <rect x="1" y="1" width="20" height="20" fill="url(#a)" stroke="brown" />
7a198f
+	  <rect x="2" y="2" width="20" height="20" fill="url(#a)" stroke="pink" />
7a198f
+	  <rect x="3" y="3" width="20" height="20" fill="url(#a)" stroke="grey" />
7a198f
+	  <rect x="4" y="3" width="20" height="20" fill="url(#a)" stroke="cyan" />
7a198f
+	  <rect x="5" y="3" width="20" height="20" fill="url(#a)" stroke="green" />
7a198f
+	  <rect x="6" y="3" width="20" height="20" fill="url(#a)" stroke="brown" />
7a198f
+	  <rect x="7" y="3" width="20" height="20" fill="url(#a)" stroke="pink" />
7a198f
+	  <rect x="8" y="3" width="20" height="20" fill="url(#a)" stroke="grey" />
7a198f
+	  <rect x="9" y="3" width="20" height="20" fill="url(#a)" stroke="cyan" />
7a198f
+
7a198f
+    </pattern>
7a198f
+    <pattern id="a" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternTransform="scale(0.5,0.5)">
7a198f
+      <rect x="0" y="0" width="20" height="20" fill="none" stroke="green" />
7a198f
+	  <rect x="1" y="1" width="20" height="20" fill="none" stroke="brown" />
7a198f
+	  <rect x="2" y="2" width="20" height="20" fill="none" stroke="pink" />
7a198f
+	  <rect x="3" y="3" width="20" height="20" fill="none" stroke="grey" />
7a198f
+	  <rect x="4" y="3" width="20" height="20" fill="none" stroke="cyan" />
7a198f
+	  <rect x="5" y="3" width="20" height="20" fill="none" stroke="green" />
7a198f
+	  <rect x="6" y="3" width="20" height="20" fill="none" stroke="brown" />
7a198f
+	  <rect x="7" y="3" width="20" height="20" fill="none" stroke="pink" />
7a198f
+	  <rect x="8" y="3" width="20" height="20" fill="none" stroke="grey" />
7a198f
+	  <rect x="9" y="3" width="20" height="20" fill="none" stroke="cyan" />
7a198f
+    </pattern>
7a198f
+  </defs>
7a198f
+
7a198f
+  
7a198f
+           cx="400" cy="200" rx="350" ry="150" />
7a198f
+
7a198f
+</svg>
7a198f
\ No newline at end of file
7a198f
diff --git a/tests/fixtures/errors/515-too-many-elements.svgz b/tests/fixtures/errors/515-too-many-elements.svgz
7a198f
new file mode 100644
7a198f
index 0000000000000000000000000000000000000000..a7f7cf678ca2f29af6df61078d1c6a86c73c2d1a
7a198f
GIT binary patch
7a198f
literal 40811
7a198f
zcmeIuO)I1U007{3c1mhf$VD-7q(+J1MDM}L%|UFTsL8?1JBN{yP&im}QQ{&|QhvZj
7a198f
zljI=9MY%ail5kSH<)g@tkhY%ZCp
7a198f
zerz~+JUc3x2`j>*LibxH_PE;`Ph^fuo4Xpr-qW6!wVUeOr_1r`p~-)LTcZB@uIs(k
7a198f
zp^3xx{A%Lt>ENbsBzwPKxl_B*S@%4>{ZPBL{_?u~dmaM@3>YwAz<>b*1`HT5V8DO@
7a198f
z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*Vq
7a198f
ziwEvbqN?)X)ARdf*>V8`1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*
7a198f
z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA;JXJdqN
7a198f
zYVLd>)r0{91`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>f$?1`2;_+E#M0g
7a198f
I9YGlT0Agk%tpET3
7a198f
7a198f
literal 0
7a198f
HcmV?d00001
7a198f
7a198f
-- 
7a198f
2.26.2
7a198f