From a51919f7e1ca9c535390a746fbf6e28c8402dc61 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 7 Oct 2015 08:45:37 +0200 Subject: [PATCH] rsvg: Add rsvg_acquire_node() This function does proper recursion checks when looking up resources from URLs and thereby helps avoiding infinite loops when cyclic references span multiple types of elements. Upstream-status: Backport https://git.gnome.org/browse/librsvg/commit/rsvg-styles.c?id=a51919f7e1ca9c535390a746fbf6e28c8402dc61 CVE: CVE-2015-7558 Signed-off-by: Armin Kuster --- rsvg-base.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++ rsvg-cairo-draw.c | 15 +++++++++++---- rsvg-cairo-render.c | 1 + rsvg-filter.c | 9 +++++++-- rsvg-private.h | 5 +++++ 5 files changed, 79 insertions(+), 6 deletions(-) Index: librsvg-2.40.10/rsvg-base.c =================================================================== --- librsvg-2.40.10.orig/rsvg-base.c +++ librsvg-2.40.10/rsvg-base.c @@ -1236,6 +1236,8 @@ rsvg_drawing_ctx_free (RsvgDrawingCtx * g_slist_free (handle->drawsub_stack); g_slist_free (handle->ptrs); + g_warn_if_fail (handle->acquired_nodes == NULL); + g_slist_free (handle->acquired_nodes); if (handle->base_uri) g_free (handle->base_uri); @@ -2018,6 +2020,59 @@ rsvg_push_discrete_layer (RsvgDrawingCtx ctx->render->push_discrete_layer (ctx); } +/* + * rsvg_acquire_node: + * @ctx: The drawing context in use + * @url: The IRI to lookup + * + * Use this function when looking up urls to other nodes. This + * function does proper recursion checking and thereby avoids + * infinite loops. + * + * Nodes acquired by this function must be released using + * rsvg_release_node() in reverse acquiring order. + * + * Returns: The node referenced by @url or %NULL if the @url + * does not reference a node. + */ +RsvgNode * +rsvg_acquire_node (RsvgDrawingCtx * ctx, const char *url) +{ + RsvgNode *node; + + 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; +} + +/* + * rsvg_release_node: + * @ctx: The drawing context the node was acquired from + * @node: Node to release + * + * Releases a node previously acquired via rsvg_acquire_node(). + * + * if @node is %NULL, this function does nothing. + */ +void +rsvg_release_node (RsvgDrawingCtx * ctx, RsvgNode *node) +{ + if (node == NULL) + return; + + g_return_if_fail (ctx->acquired_nodes != NULL); + g_return_if_fail (ctx->acquired_nodes->data == node); + + ctx->acquired_nodes = g_slist_remove (ctx->acquired_nodes, node); +} + void rsvg_render_path (RsvgDrawingCtx * ctx, const cairo_path_t *path) { Index: librsvg-2.40.10/rsvg-cairo-draw.c =================================================================== --- librsvg-2.40.10.orig/rsvg-cairo-draw.c +++ librsvg-2.40.10/rsvg-cairo-draw.c @@ -721,7 +721,7 @@ rsvg_cairo_push_render_stack (RsvgDrawin if (rsvg_current_state (ctx)->clip_path) { RsvgNode *node; - node = rsvg_defs_lookup (ctx->defs, rsvg_current_state (ctx)->clip_path); + node = rsvg_acquire_node (ctx, rsvg_current_state (ctx)->clip_path); if (node && RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_CLIP_PATH) { RsvgClipPath *clip_path = (RsvgClipPath *) node; @@ -739,6 +739,8 @@ rsvg_cairo_push_render_stack (RsvgDrawin } } + + rsvg_release_node (ctx, node); } if (state->opacity == 0xFF @@ -798,10 +800,12 @@ rsvg_cairo_pop_render_stack (RsvgDrawing if (rsvg_current_state (ctx)->clip_path) { RsvgNode *node; - node = rsvg_defs_lookup (ctx->defs, rsvg_current_state (ctx)->clip_path); + node = rsvg_acquire_node (ctx, rsvg_current_state (ctx)->clip_path); if (node && RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_CLIP_PATH && ((RsvgClipPath *) node)->units == objectBoundingBox) lateclip = (RsvgClipPath *) node; + else + rsvg_release_node (ctx, node); } if (state->opacity == 0xFF @@ -831,17 +835,20 @@ rsvg_cairo_pop_render_stack (RsvgDrawing nest ? 0 : render->offset_x, nest ? 0 : render->offset_y); - if (lateclip) + if (lateclip) { rsvg_cairo_clip (ctx, lateclip, &render->bbox); + rsvg_release_node (ctx, (RsvgNode *) lateclip); + } cairo_set_operator (render->cr, state->comp_op); if (state->mask) { RsvgNode *mask; - mask = rsvg_defs_lookup (ctx->defs, state->mask); + mask = rsvg_acquire_node (ctx, state->mask); if (mask && RSVG_NODE_TYPE (mask) == RSVG_NODE_TYPE_MASK) rsvg_cairo_generate_mask (render->cr, (RsvgMask *) mask, ctx, &render->bbox); + rsvg_release_node (ctx, mask); } else if (state->opacity != 0xFF) cairo_paint_with_alpha (render->cr, (double) state->opacity / 255.0); else Index: librsvg-2.40.10/rsvg-cairo-render.c =================================================================== --- librsvg-2.40.10.orig/rsvg-cairo-render.c +++ librsvg-2.40.10/rsvg-cairo-render.c @@ -155,6 +155,7 @@ rsvg_cairo_new_drawing_ctx (cairo_t * cr draw->pango_context = NULL; draw->drawsub_stack = NULL; draw->ptrs = NULL; + draw->acquired_nodes = NULL; rsvg_state_push (draw); state = rsvg_current_state (draw); Index: librsvg-2.40.10/rsvg-filter.c =================================================================== --- librsvg-2.40.10.orig/rsvg-filter.c +++ librsvg-2.40.10/rsvg-filter.c @@ -3921,6 +3921,7 @@ rsvg_filter_primitive_image_render_in (R RsvgDrawingCtx *ctx; RsvgFilterPrimitiveImage *upself; RsvgNode *drawable; + cairo_surface_t *result; ctx = context->ctx; @@ -3929,13 +3930,17 @@ rsvg_filter_primitive_image_render_in (R if (!upself->href) return NULL; - drawable = rsvg_defs_lookup (ctx->defs, upself->href->str); + drawable = rsvg_acquire_node (ctx, upself->href->str); if (!drawable) return NULL; rsvg_current_state (ctx)->affine = context->paffine; - return rsvg_get_surface_of_node (ctx, drawable, context->width, context->height); + result = rsvg_get_surface_of_node (ctx, drawable, context->width, context->height); + + rsvg_release_node (ctx, drawable); + + return result; } static cairo_surface_t * Index: librsvg-2.40.10/rsvg-private.h =================================================================== --- librsvg-2.40.10.orig/rsvg-private.h +++ librsvg-2.40.10/rsvg-private.h @@ -200,6 +200,7 @@ struct RsvgDrawingCtx { GSList *vb_stack; GSList *drawsub_stack; GSList *ptrs; + GSList *acquired_nodes; }; /*Abstract base class for context for our backends (one as yet)*/ @@ -360,6 +361,10 @@ void rsvg_pop_discrete_layer (RsvgDra G_GNUC_INTERNAL void rsvg_push_discrete_layer (RsvgDrawingCtx * ctx); G_GNUC_INTERNAL +RsvgNode *rsvg_acquire_node (RsvgDrawingCtx * ctx, const char *url); +G_GNUC_INTERNAL +void rsvg_release_node (RsvgDrawingCtx * ctx, RsvgNode *node); +G_GNUC_INTERNAL void rsvg_render_path (RsvgDrawingCtx * ctx, const cairo_path_t *path); G_GNUC_INTERNAL void rsvg_render_surface (RsvgDrawingCtx * ctx, cairo_surface_t *surface,