commit 3efcd83823d855a2e532b978b6e09117eed3ccf9
Author: Federico Mena Quintero <federico@gnome.org>
Date:   Thu Mar 5 19:52:39 2020 -0600

    Update to 2.40.21

diff --git a/rsvg-base.c b/rsvg-base.c
index b34b143..5d867e3 100644
--- a/rsvg-base.c
+++ b/rsvg-base.c
@@ -683,12 +683,36 @@ rsvg_start_xinclude (RsvgHandle * ctx, RsvgPropertyBag * atts)
 
 /* end xinclude */
 
+static gboolean
+loading_limits_exceeded (RsvgHandle *handle)
+{
+    /* This is a mitigation for SVG files which create millions of elements
+     * in an attempt to exhaust memory.  We don't allow loading more than
+     * this number of elements during the initial streaming load process.
+     */
+    return handle->priv->num_loaded_elements > 200000;
+}
+
 static void
 rsvg_start_element (void *data, const xmlChar * name, const xmlChar ** atts)
 {
     RsvgPropertyBag *bag;
     RsvgHandle *ctx = (RsvgHandle *) data;
 
+    /* In a different way from librsvg 2.42, we do the following check here, not
+     * in rsvg_standard_element_start() as it is done there.  This is because
+     * librsvg 2.40 still creates nodes for <title> and <metadata> elements, and
+     * we'd like to prevent unbounded memory consuption for those elements, too.
+     */
+    if (loading_limits_exceeded (ctx)) {
+        g_set_error (ctx->priv->error, RSVG_ERROR, 0, "instancing limit");
+
+        xmlStopParser (ctx->priv->ctxt);
+        return;
+    }
+
+    ctx->priv->num_loaded_elements += 1;
+
     bag = rsvg_property_bag_new ((const char **) atts);
 
     if (ctx->priv->handler) {
@@ -1377,6 +1401,7 @@ rsvg_handle_get_dimensions_sub (RsvgHandle * handle, RsvgDimensionData * dimensi
     RsvgNodeSvg *root = NULL;
     RsvgNode *sself = NULL;
     RsvgBbox bbox;
+    gboolean retval = FALSE;
 
     gboolean handle_subelement = TRUE;
 
@@ -1436,6 +1461,12 @@ rsvg_handle_get_dimensions_sub (RsvgHandle * handle, RsvgDimensionData * dimensi
         rsvg_node_draw (handle->priv->treebase, draw, 0);
         bbox = RSVG_CAIRO_RENDER (draw->render)->bbox;
 
+        if (rsvg_drawing_ctx_limits_exceeded (draw)) {
+            retval = FALSE;
+        } else {
+            retval = TRUE;
+        }
+
         cairo_restore (cr);
         rsvg_state_pop (draw);
         rsvg_drawing_ctx_free (draw);
@@ -1453,6 +1484,8 @@ rsvg_handle_get_dimensions_sub (RsvgHandle * handle, RsvgDimensionData * dimensi
         dimension_data->height = (int) (_rsvg_css_hand_normalize_length (&root->h, handle->priv->dpi_y,
                                          bbox.rect.height + bbox.rect.y * 2,
                                          12) + 0.5);
+
+        retval = TRUE;
     }
     
     dimension_data->em = dimension_data->width;
@@ -1462,7 +1495,7 @@ rsvg_handle_get_dimensions_sub (RsvgHandle * handle, RsvgDimensionData * dimensi
         (*handle->priv->size_func) (&dimension_data->width, &dimension_data->height,
                                     handle->priv->user_data);
 
-    return TRUE;
+    return retval;
 }
 
 /**
@@ -1488,7 +1521,7 @@ rsvg_handle_get_position_sub (RsvgHandle * handle, RsvgPositionData * position_d
     RsvgDimensionData    dimension_data;
     cairo_surface_t		*target = NULL;
     cairo_t				*cr = NULL;
-    gboolean			 ret = FALSE;
+    gboolean			 retval = FALSE;
 
     g_return_val_if_fail (handle, FALSE);
     g_return_val_if_fail (position_data, FALSE);
@@ -1534,6 +1567,12 @@ rsvg_handle_get_position_sub (RsvgHandle * handle, RsvgPositionData * position_d
     rsvg_node_draw (handle->priv->treebase, draw, 0);
     bbox = RSVG_CAIRO_RENDER (draw->render)->bbox;
 
+    if (rsvg_drawing_ctx_limits_exceeded (draw)) {
+        retval = FALSE;
+    } else {
+        retval = TRUE;
+    }
+
     cairo_restore (cr);
     rsvg_state_pop (draw);
     rsvg_drawing_ctx_free (draw);
@@ -1550,15 +1589,13 @@ rsvg_handle_get_position_sub (RsvgHandle * handle, RsvgPositionData * position_d
         (*handle->priv->size_func) (&dimension_data.width, &dimension_data.height,
                                     handle->priv->user_data);
 
-    ret = TRUE;
-
 bail:
     if (cr)
         cairo_destroy (cr);
     if (target)
         cairo_surface_destroy (target);
 
-    return ret;
+    return retval;
 }
 
 /** 
@@ -2240,6 +2277,52 @@ rsvg_push_discrete_layer (RsvgDrawingCtx * ctx)
     ctx->render->push_discrete_layer (ctx);
 }
 
+void
+rsvg_drawing_ctx_increase_num_elements_acquired (RsvgDrawingCtx *draw_ctx)
+{
+    draw_ctx->num_elements_acquired++;
+}
+
+/* This is a mitigation for the security-related bugs:
+ * https://gitlab.gnome.org/GNOME/librsvg/issues/323
+ * https://gitlab.gnome.org/GNOME/librsvg/issues/515
+ * 
+ * Imagine the XML [billion laughs attack], but done in SVG's terms:
+ * 
+ * - #323 above creates deeply nested groups of `<use>` elements.
+ * The first one references the second one ten times, the second one
+ * references the third one ten times, and so on.  In the file given,
+ * this causes 10^17 objects to be rendered.  While this does not
+ * exhaust memory, it would take a really long time.
+ * 
+ * - #515 has deeply nested references of `<pattern>` elements.  Each
+ * object inside each pattern has an attribute
+ * fill="url(#next_pattern)", so the number of final rendered objects
+ * grows exponentially.
+ * 
+ * We deal with both cases by placing a limit on how many references
+ * will be resolved during the SVG rendering process, that is,
+ * how many `url(#foo)` will be resolved.
+ * 
+ * [billion laughs attack]: https://bitbucket.org/tiran/defusedxml
+ */
+gboolean
+rsvg_drawing_ctx_limits_exceeded (RsvgDrawingCtx *draw_ctx)
+{
+    return draw_ctx->num_elements_acquired > 500000;
+}
+
+RsvgNode *
+rsvg_drawing_ctx_acquire_node_ref (RsvgDrawingCtx * ctx, RsvgNode *node)
+{
+  if (g_slist_find (ctx->acquired_nodes, node))
+    return NULL;
+
+  ctx->acquired_nodes = g_slist_prepend (ctx->acquired_nodes, node);
+
+  return node;
+}
+
 /*
  * rsvg_acquire_node:
  * @ctx: The drawing context in use
@@ -2260,16 +2343,18 @@ rsvg_acquire_node (RsvgDrawingCtx * ctx, const char *url)
 {
   RsvgNode *node;
 
+  if (url == NULL)
+      return NULL;
+
+  rsvg_drawing_ctx_increase_num_elements_acquired (ctx);
+  if (rsvg_drawing_ctx_limits_exceeded (ctx))
+      return NULL;
+
   node = rsvg_defs_lookup (ctx->defs, url);
   if (node == NULL)
     return NULL;
 
-  if (g_slist_find (ctx->acquired_nodes, node))
-    return NULL;
-
-  ctx->acquired_nodes = g_slist_prepend (ctx->acquired_nodes, node);
-
-  return node;
+  return rsvg_drawing_ctx_acquire_node_ref (ctx, node);
 }
 
 /*
diff --git a/rsvg-cairo-render.c b/rsvg-cairo-render.c
index ca93338..37e0ba7 100644
--- a/rsvg-cairo-render.c
+++ b/rsvg-cairo-render.c
@@ -155,6 +155,7 @@ rsvg_cairo_new_drawing_ctx (cairo_t * cr, RsvgHandle * handle)
     draw->dpi_y = handle->priv->dpi_y;
     draw->vb.rect.width = data.em;
     draw->vb.rect.height = data.ex;
+    draw->num_elements_acquired = 0;
     draw->pango_context = NULL;
     draw->drawsub_stack = NULL;
     draw->acquired_nodes = NULL;
@@ -199,6 +200,7 @@ rsvg_handle_render_cairo_sub (RsvgHandle * handle, cairo_t * cr, const char *id)
 {
     RsvgDrawingCtx *draw;
     RsvgNode *drawsub = NULL;
+    gboolean retval = FALSE;
 
     g_return_val_if_fail (handle != NULL, FALSE);
 
@@ -227,11 +229,17 @@ rsvg_handle_render_cairo_sub (RsvgHandle * handle, cairo_t * cr, const char *id)
 
     rsvg_node_draw ((RsvgNode *) handle->priv->treebase, draw, 0);
 
+    if (rsvg_drawing_ctx_limits_exceeded (draw)) {
+        retval = FALSE;
+    } else {
+        retval = TRUE;
+    }
+
     cairo_restore (cr);
     rsvg_state_pop (draw);
     rsvg_drawing_ctx_free (draw);
 
-    return TRUE;
+    return retval;
 }
 
 /**
diff --git a/rsvg-private.h b/rsvg-private.h
index 03d02ad..e17b057 100644
--- a/rsvg-private.h
+++ b/rsvg-private.h
@@ -171,6 +171,7 @@ struct RsvgHandlePrivate {
      */
     RsvgSaxHandler *handler;
     int handler_nest;
+    gsize num_loaded_elements;
 
     GHashTable *entities;       /* g_malloc'd string -> xmlEntityPtr */
 
@@ -209,6 +210,7 @@ struct RsvgDrawingCtx {
     RsvgState *state;
     GError **error;
     RsvgDefs *defs;
+    gsize num_elements_acquired;
     PangoContext *pango_context;
     double dpi_x, dpi_y;
     RsvgViewBox vb;
@@ -374,6 +376,10 @@ void rsvg_pop_discrete_layer    (RsvgDrawingCtx * ctx);
 G_GNUC_INTERNAL
 void rsvg_push_discrete_layer   (RsvgDrawingCtx * ctx);
 G_GNUC_INTERNAL
+gboolean rsvg_drawing_ctx_limits_exceeded (RsvgDrawingCtx *draw_ctx);
+G_GNUC_INTERNAL
+RsvgNode *rsvg_drawing_ctx_acquire_node_ref     (RsvgDrawingCtx * ctx, RsvgNode *node);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_acquire_node     (RsvgDrawingCtx * ctx, const char *url);
 G_GNUC_INTERNAL
 void rsvg_release_node          (RsvgDrawingCtx * ctx, RsvgNode *node);
diff --git a/rsvg-structure.c b/rsvg-structure.c
index 3c972e5..88b7ce2 100644
--- a/rsvg-structure.c
+++ b/rsvg-structure.c
@@ -40,6 +40,9 @@ rsvg_node_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
     RsvgState *state;
     GSList *stacksave;
 
+    if (rsvg_drawing_ctx_limits_exceeded (ctx))
+        return;
+
     state = self->state;
 
     stacksave = ctx->drawsub_stack;
@@ -159,27 +162,11 @@ rsvg_node_group_pack (RsvgNode * self, RsvgNode * child)
     child->parent = self;
 }
 
-static gboolean
-rsvg_node_is_ancestor (RsvgNode * potential_ancestor, RsvgNode * potential_descendant)
-{
-    /* work our way up the family tree */
-    while (TRUE) {
-        if (potential_ancestor == potential_descendant)
-            return TRUE;
-        else if (potential_descendant->parent == NULL)
-            return FALSE;
-        else
-            potential_descendant = potential_descendant->parent;
-    }
-
-    g_assert_not_reached ();
-    return FALSE;
-}
-
 static void
 rsvg_node_use_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 {
     RsvgNodeUse *use = (RsvgNodeUse *) self;
+    RsvgNode *self_acquired = NULL;
     RsvgNode *child;
     RsvgState *state;
     cairo_matrix_t affine;
@@ -191,14 +178,26 @@ rsvg_node_use_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 
     rsvg_state_reinherit_top (ctx, self->state, dominate);
 
-    if (use->link == NULL)
-      return;
+    /* <use> is an element that is used directly, unlike
+     * <pattern>, which is used through a fill="url(#...)"
+     * reference.  However, <use> will always reference another
+     * element, potentially itself or an ancestor of itself (or
+     * another <use> which references the first one, etc.).  So,
+     * we acquire the <use> element itself so that circular
+     * references can be caught.
+     */
+    self_acquired = rsvg_drawing_ctx_acquire_node_ref (ctx, self);
+    if (!self_acquired) {
+        goto out;
+    }
+
+    if (use->link == NULL) {
+        goto out;
+    }
+
     child = rsvg_acquire_node (ctx, use->link);
-    if (!child)
-        return;
-    else if (rsvg_node_is_ancestor (child, self)) {     /* or, if we're <use>'ing ourself */
-        rsvg_release_node (ctx, child);
-        return;
+    if (!child) {
+        goto out;
     }
 
     state = rsvg_current_state (ctx);
@@ -251,6 +250,12 @@ rsvg_node_use_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 
         rsvg_release_node (ctx, child);
     }
+
+out:
+
+    if (self_acquired) {
+        rsvg_release_node (ctx, self_acquired);
+    }
 }
 
 static void
