Hello, I have patched librsvg to fix the security issues in librsvg in Debian wheezy. As attached.
I note that security-tracker for CVE-2015-7558 says the patch a51919f7e1ca9c535390a746fbf6e28c8402dc61 is "Too intrusive to backport", which is the same patch that fixes CVE-2016-4347. As in in fixes the problems with the given exploit. (note I haven't found an exploit for CVE-2015-7558 yet so haven't been able to positively prove that the patch does fix this just yet; I have a suspicion that CVE-2015-7558 and CVE-2016-4347 might be the same issue however) commit a51919f7e1ca9c535390a746fbf6e28c8402dc61 Author: Benjamin Otte <[email protected]> Date: Wed Oct 7 08:45:37 2015 +0200 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. I had no problems backporting it. I did have to apply another patch first: commit 6cfaab12c70cd4a34c4730837f1ecdf792593c90 Author: Benjamin Otte <[email protected]> Date: Wed Oct 7 07:57:39 2015 +0200 state: Look up clip path lazily As these patches applied cleanly (with a bit of fuzz) to librsvg in Wheezy, I imagine they will apply equally as cleanly to librsvg in Jessie. Hence the reason for CCing the security team. Wheezy packages for testing are available here: https://people.debian.org/~bam/debian/pool/main/libr/librsvg/ The patch is attached. Any comments?? Thanks -- Brian May <[email protected]>
diff -Nru librsvg-2.36.1/debian/changelog librsvg-2.36.1/debian/changelog --- librsvg-2.36.1/debian/changelog 2016-03-27 09:46:35.000000000 +1100 +++ librsvg-2.36.1/debian/changelog 2016-05-12 09:31:01.000000000 +1000 @@ -1,3 +1,12 @@ +librsvg (2.36.1-2+deb7u2) wheezy-security; urgency=high + + * Upload by the Debian-LTS team. + * Fix CVE-2015-7558: Stack exhaustion. + * Fix CVE-2016-4347: stack exhaustion. + * Fix CVE-2016-4348: stack exhaustion. + + -- Brian May <[email protected]> Thu, 12 May 2016 09:16:32 +1000 + librsvg (2.36.1-2+deb7u1) wheezy; urgency=medium * Non-maintainer upload. diff -Nru librsvg-2.36.1/debian/patches/CVE-2016-4347.patch librsvg-2.36.1/debian/patches/CVE-2016-4347.patch --- librsvg-2.36.1/debian/patches/CVE-2016-4347.patch 1970-01-01 10:00:00.000000000 +1000 +++ librsvg-2.36.1/debian/patches/CVE-2016-4347.patch 2016-05-12 09:15:47.000000000 +1000 @@ -0,0 +1,189 @@ +--- a/rsvg-base.c ++++ b/rsvg-base.c +@@ -1187,6 +1187,8 @@ + 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); +@@ -1968,6 +1970,59 @@ + 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) + { +--- a/rsvg-cairo-draw.c ++++ b/rsvg-cairo-draw.c +@@ -723,7 +723,7 @@ + + 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; + +@@ -741,6 +741,8 @@ + } + + } ++ ++ rsvg_release_node (ctx, node); + } + + if (state->opacity == 0xFF +@@ -800,10 +802,12 @@ + + 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 +@@ -833,17 +837,20 @@ + 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 +--- a/rsvg-cairo-render.c ++++ b/rsvg-cairo-render.c +@@ -155,6 +155,7 @@ + draw->pango_context = NULL; + draw->drawsub_stack = NULL; + draw->ptrs = NULL; ++ draw->acquired_nodes = NULL; + + rsvg_state_push (draw); + state = rsvg_current_state (draw); +--- a/rsvg-filter.c ++++ b/rsvg-filter.c +@@ -3514,6 +3514,7 @@ + RsvgDrawingCtx *ctx; + RsvgFilterPrimitiveImage *upself; + RsvgNode *drawable; ++ cairo_surface_t *result; + + ctx = context->ctx; + +@@ -3522,13 +3523,17 @@ + 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 * +--- a/rsvg-private.h ++++ b/rsvg-private.h +@@ -200,6 +200,7 @@ + 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 @@ + 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, diff -Nru librsvg-2.36.1/debian/patches/CVE-2016-4348.patch librsvg-2.36.1/debian/patches/CVE-2016-4348.patch --- librsvg-2.36.1/debian/patches/CVE-2016-4348.patch 1970-01-01 10:00:00.000000000 +1000 +++ librsvg-2.36.1/debian/patches/CVE-2016-4348.patch 2016-05-12 09:15:01.000000000 +1000 @@ -0,0 +1,105 @@ +--- a/rsvg-cairo-draw.c ++++ b/rsvg-cairo-draw.c +@@ -827,7 +827,11 @@ + cairo_set_operator (render->cr, state->comp_op); + + if (state->mask) { +- rsvg_cairo_generate_mask (render->cr, state->mask, ctx, &render->bbox); ++ RsvgNode *mask; ++ ++ mask = rsvg_defs_lookup (ctx->defs, state->mask); ++ if (mask && RSVG_NODE_TYPE (mask) == RSVG_NODE_TYPE_MASK) ++ rsvg_cairo_generate_mask (render->cr, (RsvgMask *) mask, ctx, &render->bbox); + } else if (state->opacity != 0xFF) + cairo_paint_with_alpha (render->cr, (double) state->opacity / 255.0); + else +--- a/rsvg-mask.c ++++ b/rsvg-mask.c +@@ -103,23 +103,6 @@ + } + + RsvgNode * +-rsvg_mask_parse (const RsvgDefs * defs, const char *str) +-{ +- char *name; +- +- name = rsvg_get_url_string (str); +- if (name) { +- RsvgNode *val; +- val = rsvg_defs_lookup (defs, name); +- g_free (name); +- +- if (val && RSVG_NODE_TYPE (val) == RSVG_NODE_TYPE_MASK) +- return val; +- } +- return NULL; +-} +- +-RsvgNode * + rsvg_clip_path_parse (const RsvgDefs * defs, const char *str) + { + char *name; +--- a/rsvg-mask.h ++++ b/rsvg-mask.h +@@ -48,8 +48,6 @@ + + G_GNUC_INTERNAL + RsvgNode *rsvg_new_mask (void); +-G_GNUC_INTERNAL +-RsvgNode *rsvg_mask_parse (const RsvgDefs * defs, const char *str); + + typedef struct _RsvgClipPath RsvgClipPath; + +--- a/rsvg-styles.c ++++ b/rsvg-styles.c +@@ -221,6 +221,7 @@ + + *dst = *src; + dst->parent = parent; ++ dst->mask = g_strdup (src->mask); + dst->font_family = g_strdup (src->font_family); + dst->lang = g_strdup (src->lang); + rsvg_paint_server_ref (dst->fill); +@@ -356,7 +357,8 @@ + + if (inherituninheritables) { + dst->clip_path_ref = src->clip_path_ref; +- dst->mask = src->mask; ++ g_free (dst->mask); ++ dst->mask = g_strdup (src->mask); + dst->enable_background = src->enable_background; + dst->adobe_blend = src->adobe_blend; + dst->opacity = src->opacity; +@@ -444,6 +446,7 @@ + void + rsvg_state_finalize (RsvgState * state) + { ++ g_free (state->mask); + g_free (state->font_family); + g_free (state->lang); + rsvg_paint_server_unref (state->fill); +@@ -517,9 +520,10 @@ + state->adobe_blend = 11; + else + state->adobe_blend = 0; +- } else if (g_str_equal (name, "mask")) +- state->mask = rsvg_mask_parse (ctx->priv->defs, value); +- else if (g_str_equal (name, "clip-path")) { ++ } else if (g_str_equal (name, "mask")) { ++ g_free (state->mask); ++ state->mask = rsvg_get_url_string (value); ++ } else if (g_str_equal (name, "clip-path")) { + state->clip_path_ref = rsvg_clip_path_parse (ctx->priv->defs, value); + } else if (g_str_equal (name, "overflow")) { + if (!g_str_equal (value, "inherit")) { +--- a/rsvg-styles.h ++++ b/rsvg-styles.h +@@ -80,7 +80,7 @@ + cairo_matrix_t personal_affine; + + RsvgFilter *filter; +- void *mask; ++ char *mask; + void *clip_path_ref; + guint8 adobe_blend; /* 0..11 */ + guint8 opacity; /* 0..255 */ diff -Nru librsvg-2.36.1/debian/patches/look_up_clip_path_lazily.patch librsvg-2.36.1/debian/patches/look_up_clip_path_lazily.patch --- librsvg-2.36.1/debian/patches/look_up_clip_path_lazily.patch 1970-01-01 10:00:00.000000000 +1000 +++ librsvg-2.36.1/debian/patches/look_up_clip_path_lazily.patch 2016-05-12 09:15:28.000000000 +1000 @@ -0,0 +1,199 @@ +--- a/rsvg-cairo-draw.c ++++ b/rsvg-cairo-draw.c +@@ -463,7 +463,7 @@ + return; + + need_tmpbuf = ((state->fill != NULL) && (state->stroke != NULL) && state->opacity != 0xff) +- || state->clip_path_ref || state->mask || state->filter ++ || state->clip_path || state->mask || state->filter + || (state->comp_op != CAIRO_OPERATOR_OVER); + + if (need_tmpbuf) +@@ -710,18 +710,6 @@ + } + + static void +-rsvg_cairo_push_early_clips (RsvgDrawingCtx * ctx) +-{ +- RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render); +- +- cairo_save (render->cr); +- if (rsvg_current_state (ctx)->clip_path_ref) +- if (((RsvgClipPath *) rsvg_current_state (ctx)->clip_path_ref)->units == userSpaceOnUse) +- rsvg_cairo_clip (ctx, rsvg_current_state (ctx)->clip_path_ref, NULL); +- +-} +- +-static void + rsvg_cairo_push_render_stack (RsvgDrawingCtx * ctx) + { + /* XXX: Untested, probably needs help wrt filters */ +@@ -733,9 +721,27 @@ + RsvgState *state = rsvg_current_state (ctx); + gboolean lateclip = FALSE; + +- if (rsvg_current_state (ctx)->clip_path_ref) +- if (((RsvgClipPath *) rsvg_current_state (ctx)->clip_path_ref)->units == objectBoundingBox) +- lateclip = TRUE; ++ if (rsvg_current_state (ctx)->clip_path) { ++ RsvgNode *node; ++ node = rsvg_defs_lookup (ctx->defs, rsvg_current_state (ctx)->clip_path); ++ if (node && RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_CLIP_PATH) { ++ RsvgClipPath *clip_path = (RsvgClipPath *) node; ++ ++ switch (clip_path->units) { ++ case userSpaceOnUse: ++ rsvg_cairo_clip (ctx, clip_path, NULL); ++ break; ++ case objectBoundingBox: ++ lateclip = TRUE; ++ break; ++ ++ default: ++ g_assert_not_reached (); ++ break; ++ } ++ ++ } ++ } + + if (state->opacity == 0xFF + && !state->filter && !state->mask && !lateclip && (state->comp_op == CAIRO_OPERATOR_OVER) +@@ -776,7 +782,9 @@ + void + rsvg_cairo_push_discrete_layer (RsvgDrawingCtx * ctx) + { +- rsvg_cairo_push_early_clips (ctx); ++ RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render); ++ ++ cairo_save (render->cr); + rsvg_cairo_push_render_stack (ctx); + } + +@@ -785,14 +793,18 @@ + { + RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render); + cairo_t *child_cr = render->cr; +- gboolean lateclip = FALSE; ++ RsvgClipPath *lateclip = NULL; + cairo_surface_t *surface = NULL; + RsvgState *state = rsvg_current_state (ctx); + gboolean nest; + +- if (rsvg_current_state (ctx)->clip_path_ref) +- if (((RsvgClipPath *) rsvg_current_state (ctx)->clip_path_ref)->units == objectBoundingBox) +- lateclip = TRUE; ++ if (rsvg_current_state (ctx)->clip_path) { ++ RsvgNode *node; ++ node = rsvg_defs_lookup (ctx->defs, rsvg_current_state (ctx)->clip_path); ++ if (node && RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_CLIP_PATH ++ && ((RsvgClipPath *) node)->units == objectBoundingBox) ++ lateclip = (RsvgClipPath *) node; ++ } + + if (state->opacity == 0xFF + && !state->filter && !state->mask && !lateclip && (state->comp_op == CAIRO_OPERATOR_OVER) +@@ -822,7 +834,7 @@ + nest ? 0 : render->offset_y); + + if (lateclip) +- rsvg_cairo_clip (ctx, rsvg_current_state (ctx)->clip_path_ref, &render->bbox); ++ rsvg_cairo_clip (ctx, lateclip, &render->bbox); + + cairo_set_operator (render->cr, state->comp_op); + +--- a/rsvg-mask.c ++++ b/rsvg-mask.c +@@ -102,23 +102,6 @@ + return NULL; + } + +-RsvgNode * +-rsvg_clip_path_parse (const RsvgDefs * defs, const char *str) +-{ +- char *name; +- +- name = rsvg_get_url_string (str); +- if (name) { +- RsvgNode *val; +- val = rsvg_defs_lookup (defs, name); +- g_free (name); +- +- if (val && RSVG_NODE_TYPE (val) == RSVG_NODE_TYPE_CLIP_PATH) +- return val; +- } +- return NULL; +-} +- + static void + rsvg_clip_path_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts) + { +--- a/rsvg-mask.h ++++ b/rsvg-mask.h +@@ -58,8 +58,6 @@ + + G_GNUC_INTERNAL + RsvgNode *rsvg_new_clip_path (void); +-G_GNUC_INTERNAL +-RsvgNode *rsvg_clip_path_parse (const RsvgDefs * defs, const char *str); + + G_END_DECLS + #endif +--- a/rsvg-styles.c ++++ b/rsvg-styles.c +@@ -149,7 +149,7 @@ + state->visible = TRUE; + state->cond_true = TRUE; + state->filter = NULL; +- state->clip_path_ref = NULL; ++ state->clip_path = NULL; + state->startMarker = NULL; + state->middleMarker = NULL; + state->endMarker = NULL; +@@ -222,6 +222,7 @@ + *dst = *src; + dst->parent = parent; + dst->mask = g_strdup (src->mask); ++ dst->clip_path = g_strdup (src->clip_path); + dst->font_family = g_strdup (src->font_family); + dst->lang = g_strdup (src->lang); + rsvg_paint_server_ref (dst->fill); +@@ -356,7 +357,8 @@ + } + + if (inherituninheritables) { +- dst->clip_path_ref = src->clip_path_ref; ++ g_free (dst->clip_path); ++ dst->clip_path = g_strdup (src->clip_path); + g_free (dst->mask); + dst->mask = g_strdup (src->mask); + dst->enable_background = src->enable_background; +@@ -447,6 +449,7 @@ + rsvg_state_finalize (RsvgState * state) + { + g_free (state->mask); ++ g_free (state->clip_path); + g_free (state->font_family); + g_free (state->lang); + rsvg_paint_server_unref (state->fill); +@@ -524,7 +527,8 @@ + g_free (state->mask); + state->mask = rsvg_get_url_string (value); + } else if (g_str_equal (name, "clip-path")) { +- state->clip_path_ref = rsvg_clip_path_parse (ctx->priv->defs, value); ++ g_free (state->clip_path); ++ state->clip_path = rsvg_get_url_string (value); + } else if (g_str_equal (name, "overflow")) { + if (!g_str_equal (value, "inherit")) { + state->overflow = rsvg_css_parse_overflow (value, &state->has_overflow); +--- a/rsvg-styles.h ++++ b/rsvg-styles.h +@@ -81,7 +81,7 @@ + + RsvgFilter *filter; + char *mask; +- void *clip_path_ref; ++ char *clip_path; + guint8 adobe_blend; /* 0..11 */ + guint8 opacity; /* 0..255 */ + diff -Nru librsvg-2.36.1/debian/patches/series librsvg-2.36.1/debian/patches/series --- librsvg-2.36.1/debian/patches/series 2016-03-27 09:46:35.000000000 +1100 +++ librsvg-2.36.1/debian/patches/series 2016-05-12 09:15:34.000000000 +1000 @@ -4,3 +4,6 @@ 20_rsvg_compat.patch 99_ltmain_as-needed.patch CVE-2015-7557.patch +CVE-2016-4348.patch +look_up_clip_path_lazily.patch +CVE-2016-4347.patch
