| From a51919f7e1ca9c535390a746fbf6e28c8402dc61 Mon Sep 17 00:00:00 2001 |
| From: Benjamin Otte <otte@redhat.com> |
| 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 <akuster@mvista.com> |
| |
| --- |
| 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, |