[librsvg/librsvg-2.40: 3/10] Keep track of the number of acquired elements



commit a37f0f8f82eb8c874cb538b47ca443072cbafac4
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Feb 26 11:06:03 2020 -0600

    Keep track of the number of acquired elements

 rsvg-base.c      | 42 ++++++++++++++++++++++++++++++++++++++++++
 rsvg-private.h   |  2 ++
 rsvg-structure.c |  3 +++
 3 files changed, 47 insertions(+)
---
diff --git a/rsvg-base.c b/rsvg-base.c
index b040dad0..68e93f63 100644
--- a/rsvg-base.c
+++ b/rsvg-base.c
@@ -2166,6 +2166,41 @@ 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)
 {
@@ -2197,6 +2232,13 @@ 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;
diff --git a/rsvg-private.h b/rsvg-private.h
index 7d0188b0..734c9db4 100644
--- a/rsvg-private.h
+++ b/rsvg-private.h
@@ -370,6 +370,8 @@ 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);
diff --git a/rsvg-structure.c b/rsvg-structure.c
index 3c972e50..823a91b4 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;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]