[librsvg] all: Add option to parse huge SVG XML files



commit aa1f447e2741d7b5c399ec1f8bf9cfdce81d5506
Author: Christian Persch <chpe gnome org>
Date:   Wed Jun 18 20:08:34 2014 +0200

    all: Add option to parse huge SVG XML files
    
    For security reasons, newer libxml versions applies limits that huge
    SVG files exceed and thus fail parsing. Add an RSVG_HANDLE_FLAG_UNLIMITED
    flag that uses XML_PARSE_HUGE to allow parsing these files again.
    
    Note: for security reasons, this flag should ONLY be used on trusted input!
    
    https://bugzilla.gnome.org/show_bug.cgi?id=710310

 Makefile.am    |    5 +--
 configure.in   |    2 +
 rsvg-base.c    |   23 ++++++++++++++--
 rsvg-convert.c |   76 ++++++++++++++++++++------------------------------------
 rsvg-css.h     |   12 +++++++-
 rsvg.h         |    6 +++-
 test-display.c |   10 ++++++-
 7 files changed, 75 insertions(+), 59 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index e881100..5c3eab6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -119,15 +119,14 @@ rsvg_convert_CPPFLAGS = \
        $(AM_CPPFLAGS)
 
 rsvg_convert_CFLAGS =\
-       $(LIBRSVG_CFLAGS)       \
+       $(RSVG_CONVERT_CFLAGS) \
        $(AM_CFLAGS)
 
 rsvg_convert_LDFLAGS = $(AM_LDFLAGS)
 
 rsvg_convert_LDADD = \
        $(top_builddir)/librsvg- RSVG_API_MAJOR_VERSION@.la     \
-       $(LIBRSVG_LIBS)         \
-       $(GTHREAD_LIBS)         \
+       $(RSVG_CONVERT_LIBS) \
        $(LIBM)
 
 rsvg_view_3_SOURCES = \
diff --git a/configure.in b/configure.in
index 69c2a4c..ac76716 100644
--- a/configure.in
+++ b/configure.in
@@ -111,6 +111,8 @@ PKG_CHECK_MODULES([GTHREAD],[gthread-2.0 >= $GLIB_REQUIRED])
 
 PKG_CHECK_MODULES([GMODULE],[gmodule-2.0])
 
+PKG_CHECK_MODULES([RSVG_CONVERT],[gio-2.0 gio-unix-2.0 gdk-pixbuf-2.0 cairo pangocairo])
+
 dnl ===========================================================================
 
 AC_CHECK_FUNCS(strtok_r)
diff --git a/rsvg-base.c b/rsvg-base.c
index b1cc71a..683d607 100644
--- a/rsvg-base.c
+++ b/rsvg-base.c
@@ -523,6 +523,23 @@ rsvg_xinclude_handler_end (RsvgSaxHandler * self, const char *name)
     }
 }
 
+static void
+_rsvg_set_xml_parse_options(xmlParserCtxtPtr xml_parser,
+                            RsvgHandle *ctx)
+{
+    xml_parser->options |= XML_PARSE_NONET;
+
+    if (ctx->priv->flags & RSVG_HANDLE_FLAG_UNLIMITED) {
+#if LIBXML_VERSION > 20632
+        xml_parser->options |= XML_PARSE_HUGE;
+#endif
+    }
+
+#if LIBXML_VERSION > 20800
+    xml_parser->options |= XML_PARSE_BIG_LINES;
+#endif
+}
+
 /* http://www.w3.org/TR/xinclude/ */
 static void
 rsvg_start_xinclude (RsvgHandle * ctx, RsvgPropertyBag * atts)
@@ -575,7 +592,7 @@ rsvg_start_xinclude (RsvgHandle * ctx, RsvgPropertyBag * atts)
             goto fallback;
 
         xml_parser = xmlCreatePushParserCtxt (&rsvgSAXHandlerStruct, ctx, NULL, 0, NULL);
-        xml_parser->options |= XML_PARSE_NONET;
+        _rsvg_set_xml_parse_options(xml_parser, ctx);
 
         buffer = _rsvg_xml_input_buffer_new_from_stream (stream, NULL /* cancellable */, 
XML_CHAR_ENCODING_NONE, &err);
         g_object_unref (stream);
@@ -1115,7 +1132,7 @@ rsvg_handle_write_impl (RsvgHandle * handle, const guchar * buf, gsize count, GE
     if (handle->priv->ctxt == NULL) {
         handle->priv->ctxt = xmlCreatePushParserCtxt (&rsvgSAXHandlerStruct, handle, NULL, 0,
                                                       rsvg_handle_get_base_uri (handle));
-        handle->priv->ctxt->options |= XML_PARSE_NONET;
+        _rsvg_set_xml_parse_options(handle->priv->ctxt, handle);
 
         /* if false, external entities work, but internal ones don't. if true, internal entities
            work, but external ones don't. favor internal entities, in order to not cause a
@@ -1773,7 +1790,7 @@ rsvg_handle_read_stream_sync (RsvgHandle   *handle,
     if (priv->ctxt == NULL) {
         priv->ctxt = xmlCreatePushParserCtxt (&rsvgSAXHandlerStruct, handle, NULL, 0,
                                               rsvg_handle_get_base_uri (handle));
-        priv->ctxt->options |= XML_PARSE_NONET;
+        _rsvg_set_xml_parse_options(priv->ctxt, handle);
 
         /* if false, external entities work, but internal ones don't. if true, internal entities
            work, but external ones don't. favor internal entities, in order to not cause a
diff --git a/rsvg-convert.c b/rsvg-convert.c
index be86b45..276e789 100644
--- a/rsvg-convert.c
+++ b/rsvg-convert.c
@@ -34,10 +34,12 @@
 #include <stdlib.h>
 #include <string.h>
 #include <locale.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gio/gunixinputstream.h>
 
 #include "rsvg-css.h"
 #include "rsvg.h"
-#include "rsvg-private.h"
 #include "rsvg-size-callback.h"
 
 #ifdef CAIRO_HAS_PS_SURFACE
@@ -65,44 +67,6 @@ display_error (GError * err)
     }
 }
 
-static RsvgHandle *
-rsvg_handle_new_from_stdio_file (FILE * f, GError ** error)
-{
-    RsvgHandle *handle;
-    gchar *current_dir;
-    gchar *base_uri;
-
-    handle = rsvg_handle_new ();
-
-    while (!feof (f)) {
-        guchar buffer[4096];
-        gsize length = fread (buffer, 1, sizeof (buffer), f);
-
-        if (length > 0) {
-            if (!rsvg_handle_write (handle, buffer, length, error)) {
-                g_object_unref (handle);
-                return NULL;
-            }
-        } else if (ferror (f)) {
-            g_object_unref (handle);
-            return NULL;
-        }
-    }
-
-    if (!rsvg_handle_close (handle, error)) {
-        g_object_unref (handle);
-        return NULL;
-    }
-
-    current_dir = g_get_current_dir ();
-    base_uri = g_build_filename (current_dir, "file.svg", NULL);
-    rsvg_handle_set_base_uri (handle, base_uri);
-    g_free (base_uri);
-    g_free (current_dir);
-
-    return handle;
-}
-
 static void
 rsvg_cairo_size_callback (int *width, int *height, gpointer data)
 {
@@ -136,8 +100,8 @@ main (int argc, char **argv)
     int keep_aspect_ratio = FALSE;
     guint32 background_color = 0;
     char *background_color_str = NULL;
-    char *base_uri = NULL;
     gboolean using_stdin = FALSE;
+    gboolean unlimited = FALSE;
     GError *error = NULL;
 
     int i;
@@ -146,6 +110,7 @@ main (int argc, char **argv)
     RsvgHandle *rsvg;
     cairo_surface_t *surface = NULL;
     cairo_t *cr = NULL;
+    RsvgHandleFlags flags = RSVG_HANDLE_FLAGS_NONE;
     RsvgDimensionData dimensions;
     FILE *output_file = stdout;
 
@@ -172,8 +137,8 @@ main (int argc, char **argv)
          N_("whether to preserve the aspect ratio [optional; defaults to FALSE]"), NULL},
         {"background-color", 'b', 0, G_OPTION_ARG_STRING, &background_color_str,
          N_("set the background color [optional; defaults to None]"), N_("[black, white, #abccee, 
#aaa...]")},
+        {"unlimited", 'u', 0, G_OPTION_ARG_NONE, &unlimited, N_("Allow huge SVG files"), NULL},
         {"version", 'v', 0, G_OPTION_ARG_NONE, &bVersion, N_("show version information"), NULL},
-        {"base-uri", 'b', 0, G_OPTION_ARG_STRING, &base_uri, N_("base uri"), NULL},
         {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("[FILE...]")},
         {NULL}
     };
@@ -227,23 +192,36 @@ main (int argc, char **argv)
 
     rsvg_set_default_dpi_x_y (dpi_x, dpi_y);
 
+    if (unlimited)
+        flags |= RSVG_HANDLE_FLAG_UNLIMITED;
+
     for (i = 0; i < n_args; i++) {
+        GFile *file;
+        GInputStream *stream;
+
+        if (using_stdin) {
+            file = NULL;
+            stream = g_unix_input_stream_new (STDIN_FILENO, FALSE);
+        } else {
+            file = g_file_new_for_commandline_arg (args[i]);
+            stream = (GInputStream *) g_file_read (file, NULL, &error);
+            if (stream == NULL)
+                goto done;
+        }
 
-        if (using_stdin)
-            rsvg = rsvg_handle_new_from_stdio_file (stdin, &error);
-        else
-            rsvg = rsvg_handle_new_from_file (args[i], &error);
+        rsvg = rsvg_handle_new_from_stream_sync (stream, file, flags, NULL, &error);
 
-        if (!rsvg) {
+    done:
+        g_clear_object (&stream);
+        g_clear_object (&file);
+
+        if (error != NULL) {
             fprintf (stderr, _("Error reading SVG:"));
             display_error (error);
             fprintf (stderr, "\n");
             exit (1);
         }
 
-        if (base_uri)
-            rsvg_handle_set_base_uri (rsvg, base_uri);
-
         /* in the case of multi-page output, all subsequent SVGs are scaled to the first's size */
         rsvg_handle_set_size_callback (rsvg, rsvg_cairo_size_callback, &dimensions, NULL);
 
diff --git a/rsvg-css.h b/rsvg-css.h
index f6db946..6abeba9 100644
--- a/rsvg-css.h
+++ b/rsvg-css.h
@@ -27,8 +27,11 @@
 #define RSVG_CSS_H
 
 #include <glib.h>
+
+#ifdef RSVG_COMPILATION
 #include <pango/pango.h>
 #include "rsvg-private.h"
+#endif
 
 G_BEGIN_DECLS
 
@@ -44,10 +47,13 @@ G_BEGIN_DECLS
 #define RSVG_ASPECT_RATIO_XMAX_YMAX (1 << 8)
 #define RSVG_ASPECT_RATIO_SLICE (1 << 31)
 
+/* This one is semi-public for mis-use in rsvg-convert */
+guint32            rsvg_css_parse_color        (const char *str, gboolean * inherit);
+
+#ifdef RSVG_COMPILATION
+
 G_GNUC_INTERNAL
 int        rsvg_css_parse_aspect_ratio     (const char *str);
-/* for some reason this one's public... */
-guint32            rsvg_css_parse_color        (const char *str, gboolean * inherit);
 G_GNUC_INTERNAL
 guint       rsvg_css_parse_opacity         (const char *str);
 G_GNUC_INTERNAL
@@ -79,6 +85,8 @@ gboolean      rsvg_css_parse_overflow       (const char *str, gboolean * inherit
 G_GNUC_INTERNAL
 char        **rsvg_css_parse_xml_attribute_string   (const char *attribute_string);
 
+#endif /* RSVG_COMPILATION */
+
 G_END_DECLS
 
 #endif  
diff --git a/rsvg.h b/rsvg.h
index ebcdb61..801008b 100644
--- a/rsvg.h
+++ b/rsvg.h
@@ -155,10 +155,14 @@ gboolean rsvg_handle_has_sub (RsvgHandle * handle, const char *id);
 /**
  * RsvgHandleFlags:
  * @RSVG_HANDLE_FLAGS_NONE: none
+ * @RSVG_HANDLE_FLAG_UNLIMITED: Allow any SVG XML without size limitations.
+ *   For security reasons, this should only be used for trusted input!
+ *   Since: 2.40.3
  */
 typedef enum /*< flags >*/ 
 {
-    RSVG_HANDLE_FLAGS_NONE        = 0
+    RSVG_HANDLE_FLAGS_NONE        = 0,
+    RSVG_HANDLE_FLAG_UNLIMITED    = 1 << 0
 } RsvgHandleFlags;
 
 RsvgHandle *rsvg_handle_new_with_flags (RsvgHandleFlags flags);
diff --git a/test-display.c b/test-display.c
index 4b8929b..e16414d 100644
--- a/test-display.c
+++ b/test-display.c
@@ -612,6 +612,7 @@ main (int argc, char **argv)
     char *bg_color = NULL;
     char *base_uri = NULL;
     gboolean keep_aspect_ratio = FALSE;
+    gboolean unlimited = FALSE;
     char *id = NULL;
     GInputStream *input;
     GFileInfo *file_info;
@@ -622,6 +623,8 @@ main (int argc, char **argv)
     int from_stdin = 0;
     ViewerCbInfo info;
 
+    RsvgHandleFlags flags = RSVG_HANDLE_FLAGS_NONE;
+
     char **args = NULL;
     gint n_args = 0;
 
@@ -646,6 +649,8 @@ main (int argc, char **argv)
          N_("<string>")},
         {"keep-aspect", 'k', 0, G_OPTION_ARG_NONE, &keep_aspect_ratio,
          N_("Preserve the image's aspect ratio"), NULL},
+        {"unlimited", 'u', 0, G_OPTION_ARG_NONE, &unlimited,
+         N_("Allow huge SVG files"), NULL},
         {"version", 'v', 0, G_OPTION_ARG_NONE, &bVersion, N_("Show version information"), NULL},
         {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("[FILE...]")},
         {NULL}
@@ -688,6 +693,9 @@ main (int argc, char **argv)
 
     compressed = FALSE;
 
+    if (unlimited)
+        flags |= RSVG_HANDLE_FLAG_UNLIMITED;
+
     if (from_stdin) {
 #if 0 // defined (G_OS_UNIX)
         input = g_unix_input_stream_new (STDIN_FILENO, FALSE);
@@ -749,7 +757,7 @@ main (int argc, char **argv)
 
     info.handle = rsvg_handle_new_from_stream_sync (input, 
                                                     base_file, 
-                                                    RSVG_HANDLE_FLAGS_NONE,
+                                                    flags,
                                                     NULL /* cancellable */,
                                                     &err);
     if (base_file != NULL)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]