Skip to content

Commit

Permalink
Merge pull request #1 from g4jc/master
Browse files Browse the repository at this point in the history
Bump to 2.40.21
  • Loading branch information
oaken-source authored Sep 17, 2020
2 parents 0654e1c + 8a20488 commit 586e75f
Show file tree
Hide file tree
Showing 14 changed files with 606 additions and 40 deletions.
2 changes: 1 addition & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ librsvg-og
This is librsvg-og (read: librsvg "old gen") - A small library to render
Scalable Vector Graphics (SVG). It has been created as a fork of the original
librsvg of version 2.40.20, which was the last stable release untainted by rust
additions. librsvg-ng performs the same task as the original librsvg, to render
additions. librsvg-og performs the same task as the original librsvg, to render
SVG files to Cairo surfaces, and should be a drop-in replacement for librsvg,
assuming no API-breaking changes by upstream.

Expand Down
4 changes: 2 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
m4_define([rsvg_major_version],[2])
m4_define([rsvg_minor_version],[40])
m4_define([rsvg_micro_version],[20])
m4_define([rsvg_micro_version],[21])
m4_define([rsvg_extra_version],[])
m4_define([rsvg_version],[rsvg_major_version.rsvg_minor_version.rsvg_micro_version()rsvg_extra_version])
m4_define([rsvg_lt_version_info],m4_eval(rsvg_major_version + rsvg_minor_version):rsvg_micro_version:rsvg_minor_version)

AC_INIT([RSVG],[rsvg_version],[https://bugzilla.gnome.org/enter_bug.cgi?product=librsvg],[librsvg])
AC_INIT([RSVG],[rsvg_version],[https://github.com/oaken-source/librsvg-og/issues],[librsvg])

AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([rsvg.h])
Expand Down
107 changes: 96 additions & 11 deletions rsvg-base.c
Original file line number Diff line number Diff line change
Expand Up @@ -705,12 +705,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) {
Expand Down Expand Up @@ -1387,6 +1411,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;

Expand Down Expand Up @@ -1446,6 +1471,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);
Expand All @@ -1463,6 +1494,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;
Expand All @@ -1472,7 +1505,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;
}

/**
Expand All @@ -1498,7 +1531,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);
Expand Down Expand Up @@ -1544,6 +1577,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);
Expand All @@ -1560,15 +1599,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;
}

/**
Expand Down Expand Up @@ -2166,6 +2203,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
Expand All @@ -2186,16 +2269,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);
}

/*
Expand Down
10 changes: 9 additions & 1 deletion rsvg-cairo-render.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,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;
Expand Down Expand Up @@ -219,6 +220,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);

Expand Down Expand Up @@ -247,11 +249,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;
}

/**
Expand Down
6 changes: 6 additions & 0 deletions rsvg-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ struct RsvgHandlePrivate {
*/
RsvgSaxHandler *handler;
int handler_nest;
gsize num_loaded_elements;

GHashTable *entities; /* g_malloc'd string -> xmlEntityPtr */

Expand Down Expand Up @@ -203,6 +204,7 @@ struct RsvgDrawingCtx {
RsvgState *state;
GError **error;
RsvgDefs *defs;
gsize num_elements_acquired;
PangoContext *pango_context;
double dpi_x, dpi_y;
RsvgViewBox vb;
Expand Down Expand Up @@ -369,6 +371,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);
Expand Down
53 changes: 29 additions & 24 deletions rsvg-structure.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion tests/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
include $(top_srcdir)/glib-tap.mk

# Keep "errors" at the end; they are the slowest to run
test_programs = \
loading \
rsvg-test \
crash \
styles \
render-crash \
dimensions
dimensions \
errors

test_utils_common_sources = \
test-utils.c \
Expand All @@ -16,6 +18,10 @@ rsvg_test_SOURCES = \
rsvg-test.c \
$(test_utils_common_sources)

errors_SOURCES = \
errors.c \
$(test_utils_common_sources)

crash_SOURCES = \
crash.c \
$(test_utils_common_sources)
Expand Down Expand Up @@ -51,6 +57,7 @@ dist_installed_test_data = \
$(wildcard $(srcdir)/resources/*) \
$(wildcard $(srcdir)/fixtures/crash/*.svg) \
$(wildcard $(srcdir)/fixtures/crash/*.png) \
$(wildcard $(srcdir)/fixtures/errors/*) \
$(wildcard $(srcdir)/fixtures/loading/*) \
$(wildcard $(srcdir)/fixtures/reftests/*.svg) \
$(wildcard $(srcdir)/fixtures/reftests/*.png) \
Expand Down
Loading

0 comments on commit 586e75f

Please sign in to comment.