Vulnerability: A SIGFPE is raised in the function box_blur_line of rsvg-filter.c in GNOME librsvg 2.40.17 during an attempted parse of a crafted SVG file, because of incorrect protection against division by zero.
Vulnerability ID: CVE-2017-11464
CWE: CWE-369
Severity score: 6.4
Project: https://github.com/GNOME/librsvg
Published: 2017-07-19
File: rsvg-filter.c
Commit message: bgo#783835 - Don't divide by zero in box_blur_line() for gaussian blurs

We were making the decision to use box blurs, instead of a true
Gaussian kernel, based on the size of *both* x and y dimensions.  Do
them individually instead.
Commit author: Federico Mena Quintero

Vulnerable version (before fix):
 box_blur_line (gint box_width, gint even_offset,                       kernel; it's the pixel to remove from the accumulator. */     gint  *ac;     /* Accumulator for each channel                           */      ac = g_new0 (gint, bpp);      /* The algorithm differs for even and odd-sized kernels. gaussian_blur_surface (cairo_surface_t *in,                        gdouble sx,                        gdouble sy) {     gboolean use_box_blur;     gint width, height;     cairo_format_t in_format, out_format;     gint in_stride; gaussian_blur_surface (cairo_surface_t *in,     if (sy < 0.0)         sy = 0.0;      /* For small radiuses, use a true gaussian kernel; otherwise use three box blurs with      * clever offsets.      */     if (sx < 10.0 && sy < 10.0)         use_box_blur = FALSE;     else         use_box_blur = TRUE;      /* Bail out by just copying? */     if ((sx == 0.0 && sy == 0.0)         || sx > 1000 || sy > 1000) { gaussian_blur_surface (cairo_surface_t *in,         int y;         guchar *row_buffer = NULL;         guchar *row1, *row2;          if (use_box_blur) {             box_width = compute_box_blur_width (sx); gaussian_blur_surface (cairo_surface_t *in,         guchar *col_buffer;         guchar *col1, *col2;         int x;          /* twice the size so we can have the source pixels and the blurred pixels */         col_buffer = g_new0 (guchar, height * bpp * 2);

Fixed version (after fix):
 box_blur_line (gint box_width, gint even_offset,                       kernel; it's the pixel to remove from the accumulator. */     gint  *ac;     /* Accumulator for each channel                           */      g_assert (box_width > 0);      ac = g_new0 (gint, bpp);      /* The algorithm differs for even and odd-sized kernels. gaussian_blur_surface (cairo_surface_t *in,                        gdouble sx,                        gdouble sy) {     gint width, height;     cairo_format_t in_format, out_format;     gint in_stride; gaussian_blur_surface (cairo_surface_t *in,     if (sy < 0.0)         sy = 0.0;      /* Bail out by just copying? */     if ((sx == 0.0 && sy == 0.0)         || sx > 1000 || sy > 1000) { gaussian_blur_surface (cairo_surface_t *in,         int y;         guchar *row_buffer = NULL;         guchar *row1, *row2;         gboolean use_box_blur;          /* For small radiuses, use a true gaussian kernel; otherwise use three box blurs with          * clever offsets.          */         if (sx < 10.0)             use_box_blur = FALSE;         else             use_box_blur = TRUE;          if (use_box_blur) {             box_width = compute_box_blur_width (sx); gaussian_blur_surface (cairo_surface_t *in,         guchar *col_buffer;         guchar *col1, *col2;         int x;         gboolean use_box_blur;          /* For small radiuses, use a true gaussian kernel; otherwise use three box blurs with          * clever offsets.          */         if (sy < 10.0)             use_box_blur = FALSE;         else             use_box_blur = TRUE;          /* twice the size so we can have the source pixels and the blurred pixels */         col_buffer = g_new0 (guchar, height * bpp * 2);

Patch:
patch: @@ -1417,6 +1417,8 @@ box_blur_line (gint box_width, gint even_offset,
                       kernel; it's the pixel to remove from the accumulator. */
     gint  *ac;     /* Accumulator for each channel                           */
 
+    g_assert (box_width > 0);
+
     ac = g_new0 (gint, bpp);
 
     /* The algorithm differs for even and odd-sized kernels.
@@ -1774,7 +1776,6 @@ gaussian_blur_surface (cairo_surface_t *in,
                        gdouble sx,
                        gdouble sy)
 {
-    gboolean use_box_blur;
     gint width, height;
     cairo_format_t in_format, out_format;
     gint in_stride;
@@ -1818,14 +1819,6 @@ gaussian_blur_surface (cairo_surface_t *in,
     if (sy < 0.0)
         sy = 0.0;
 
-    /* For small radiuses, use a true gaussian kernel; otherwise use three box blurs with
-     * clever offsets.
-     */
-    if (sx < 10.0 && sy < 10.0)
-        use_box_blur = FALSE;
-    else
-        use_box_blur = TRUE;
-
     /* Bail out by just copying? */
     if ((sx == 0.0 && sy == 0.0)
         || sx > 1000 || sy > 1000) {
@@ -1845,6 +1838,15 @@ gaussian_blur_surface (cairo_surface_t *in,
         int y;
         guchar *row_buffer = NULL;
         guchar *row1, *row2;
+        gboolean use_box_blur;
+
+        /* For small radiuses, use a true gaussian kernel; otherwise use three box blurs with
+         * clever offsets.
+         */
+        if (sx < 10.0)
+            use_box_blur = FALSE;
+        else
+            use_box_blur = TRUE;
 
         if (use_box_blur) {
             box_width = compute_box_blur_width (sx);
@@ -1900,6 +1902,15 @@ gaussian_blur_surface (cairo_surface_t *in,
         guchar *col_buffer;
         guchar *col1, *col2;
         int x;
+        gboolean use_box_blur;
+
+        /* For small radiuses, use a true gaussian kernel; otherwise use three box blurs with
+         * clever offsets.
+         */
+        if (sy < 10.0)
+            use_box_blur = FALSE;
+        else
+            use_box_blur = TRUE;
 
         /* twice the size so we can have the source pixels and the blurred pixels */
         col_buffer = g_new0 (guchar, height * bpp * 2);
path: rsvg-filter.c