[gimp] Issue #8385: problem importing SVG containing huge data.
- From: Jehan <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] Issue #8385: problem importing SVG containing huge data.
- Date: Tue, 19 Jul 2022 16:29:46 +0000 (UTC)
commit 240dc129124ee72b0785706fcb2bf7b094394017
Author: Jehan <jehan girinstud io>
Date: Tue Jul 19 18:12:51 2022 +0200
Issue #8385: problem importing SVG containing huge data.
libxml has a XML_PARSE_HUGE parsing option, which can be turned on with
the RSVG_HANDLE_FLAG_UNLIMITED option in librsvg.
We cannot just set this option by default because it is a security
hazard as a maliciously malformed SVG could use this to consume too much
memory.
Instead, let's just propose the option interactively when we fail to
create a rsvg handle. Unfortunately right now we can't single out this
specific error because librsvg actually returns an unrelated (false
positive created by the huge data) error. So we just propose the option
for any kind of handle creation failure.
Furthermore, the option is only available on interactive plug-in calls
so far. In particular, the PDB API doesn't have an option allowing a
script writer to run "file-svg-load" with the huge data option.
As for the thumbnail API, it is never meant to be used interactively and
not really as a common script function, so it won't have the huge data
option either.
plug-ins/common/file-svg.c | 166 +++++++++++++++++++++++++++++++++++----------
1 file changed, 131 insertions(+), 35 deletions(-)
---
diff --git a/plug-ins/common/file-svg.c b/plug-ins/common/file-svg.c
index ad6e182cc0..2b664fdd34 100644
--- a/plug-ins/common/file-svg.c
+++ b/plug-ins/common/file-svg.c
@@ -89,14 +89,19 @@ static GimpValueArray * svg_load_thumb (GimpProcedure *procedure,
gpointer run_data);
static GimpImage * load_image (GFile *file,
+ RsvgHandleFlags rsvg_flags,
GError **error);
static GdkPixbuf * load_rsvg_pixbuf (GFile *file,
SvgLoadVals *vals,
+ RsvgHandleFlags rsvg_flags,
+ gboolean *allow_retry,
GError **error);
static gboolean load_rsvg_size (GFile *file,
SvgLoadVals *vals,
+ RsvgHandleFlags rsvg_flags,
GError **error);
-static gboolean load_dialog (GFile *file,
+static GimpPDBStatusType load_dialog (GFile *file,
+ RsvgHandleFlags *rsvg_flags,
GError **error);
@@ -233,9 +238,11 @@ svg_load (GimpProcedure *procedure,
const GimpValueArray *args,
gpointer run_data)
{
- GimpValueArray *return_vals;
- GimpImage *image;
- GError *error = NULL;
+ GimpValueArray *return_vals;
+ GimpImage *image;
+ GError *error = NULL;
+ RsvgHandleFlags rsvg_flags = RSVG_HANDLE_FLAGS_NONE;
+ GimpPDBStatusType status;
gegl_init (NULL, NULL);
@@ -251,10 +258,9 @@ svg_load (GimpProcedure *procedure,
case GIMP_RUN_INTERACTIVE:
gimp_get_data (LOAD_PROC, &load_vals);
- if (! load_dialog (file, &error))
- return gimp_procedure_new_return_values (procedure,
- GIMP_PDB_CANCEL,
- NULL);
+ status = load_dialog (file, &rsvg_flags, &error);
+ if (status != GIMP_PDB_SUCCESS)
+ return gimp_procedure_new_return_values (procedure, status, error);
break;
case GIMP_RUN_WITH_LAST_VALS:
@@ -262,7 +268,7 @@ svg_load (GimpProcedure *procedure,
break;
}
- image = load_image (file, &error);
+ image = load_image (file, rsvg_flags, &error);
if (! image)
return gimp_procedure_new_return_values (procedure,
@@ -307,7 +313,7 @@ svg_load_thumb (GimpProcedure *procedure,
gegl_init (NULL, NULL);
- if (load_rsvg_size (file, &load_vals, NULL))
+ if (load_rsvg_size (file, &load_vals, RSVG_HANDLE_FLAGS_NONE, NULL))
{
width = load_vals.width;
height = load_vals.height;
@@ -317,7 +323,7 @@ svg_load_thumb (GimpProcedure *procedure,
load_vals.width = - size;
load_vals.height = - size;
- image = load_image (file, &error);
+ image = load_image (file, RSVG_HANDLE_FLAGS_NONE, &error);
if (! image)
return gimp_procedure_new_return_values (procedure,
@@ -338,8 +344,9 @@ svg_load_thumb (GimpProcedure *procedure,
}
static GimpImage *
-load_image (GFile *file,
- GError **load_error)
+load_image (GFile *file,
+ RsvgHandleFlags rsvg_flags,
+ GError **load_error)
{
GimpImage *image;
GimpLayer *layer;
@@ -348,7 +355,7 @@ load_image (GFile *file,
gint height;
GError *error = NULL;
- pixbuf = load_rsvg_pixbuf (file, &load_vals, &error);
+ pixbuf = load_rsvg_pixbuf (file, &load_vals, rsvg_flags, NULL, &error);
if (! pixbuf)
{
@@ -436,18 +443,26 @@ load_set_size_callback (gint *width,
/* This function renders a pixbuf from an SVG file according to vals. */
static GdkPixbuf *
-load_rsvg_pixbuf (GFile *file,
- SvgLoadVals *vals,
- GError **error)
+load_rsvg_pixbuf (GFile *file,
+ SvgLoadVals *vals,
+ RsvgHandleFlags rsvg_flags,
+ gboolean *allow_retry,
+ GError **error)
{
GdkPixbuf *pixbuf = NULL;
RsvgHandle *handle;
- handle = rsvg_handle_new_from_gfile_sync (file, RSVG_HANDLE_FLAGS_NONE, NULL,
- error);
+ handle = rsvg_handle_new_from_gfile_sync (file, rsvg_flags, NULL, error);
if (! handle)
- return NULL;
+ {
+ /* The "huge data" error will be seen from the handle creation
+ * already. No need to retry when the error occurs later.
+ */
+ if (allow_retry)
+ *allow_retry = TRUE;
+ return NULL;
+ }
rsvg_handle_set_dpi (handle, vals->resolution);
rsvg_handle_set_size_callback (handle, load_set_size_callback, vals, NULL);
@@ -463,16 +478,16 @@ static GtkWidget *size_label = NULL;
/* This function retrieves the pixel size from an SVG file. */
static gboolean
-load_rsvg_size (GFile *file,
- SvgLoadVals *vals,
- GError **error)
+load_rsvg_size (GFile *file,
+ SvgLoadVals *vals,
+ RsvgHandleFlags rsvg_flags,
+ GError **error)
{
RsvgHandle *handle;
RsvgDimensionData dim;
gboolean has_size;
- handle = rsvg_handle_new_from_gfile_sync (file, RSVG_HANDLE_FLAGS_NONE, NULL,
- error);
+ handle = rsvg_handle_new_from_gfile_sync (file, rsvg_flags, NULL, error);
if (! handle)
return FALSE;
@@ -576,11 +591,14 @@ static void
load_dialog_resolution_callback (GimpSizeEntry *res,
GFile *file)
{
- SvgLoadVals vals = { 0.0, 0, 0 };
+ SvgLoadVals vals = { 0.0, 0, 0 };
+ RsvgHandleFlags rsvg_flags;
+
+ rsvg_flags = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (file), "rsvg-flags"));
load_vals.resolution = vals.resolution = gimp_size_entry_get_refval (res, 0);
- if (! load_rsvg_size (file, &vals, NULL))
+ if (! load_rsvg_size (file, &vals, rsvg_flags, NULL))
return;
g_signal_handlers_block_by_func (size, load_dialog_size_callback, NULL);
@@ -626,9 +644,10 @@ load_dialog_set_ratio (gdouble x,
g_signal_handlers_unblock_by_func (yadj, load_dialog_ratio_callback, NULL);
}
-static gboolean
-load_dialog (GFile *file,
- GError **load_error)
+static GimpPDBStatusType
+load_dialog (GFile *file,
+ RsvgHandleFlags *rsvg_flags,
+ GError **load_error)
{
GtkWidget *dialog;
GtkWidget *frame;
@@ -646,6 +665,8 @@ load_dialog (GFile *file,
GtkAdjustment *adj;
gboolean run;
GError *error = NULL;
+ gchar *text;
+ gboolean allow_retry = FALSE;
SvgLoadVals vals =
{
SVG_DEFAULT_RESOLUTION,
@@ -653,7 +674,83 @@ load_dialog (GFile *file,
- SVG_PREVIEW_SIZE
};
- preview = load_rsvg_pixbuf (file, &vals, &error);
+ preview = load_rsvg_pixbuf (file, &vals, *rsvg_flags, &allow_retry, &error);
+
+ gimp_ui_init (PLUG_IN_BINARY);
+
+ if (! preview && *rsvg_flags == RSVG_HANDLE_FLAGS_NONE && allow_retry)
+ {
+ /* We need to ask explicitly before using the "unlimited" size
+ * option (XML_PARSE_HUGE in libxml) because it is considered
+ * unsafe, possibly consumming too much memory with malicious XML
+ * files.
+ */
+ dialog = gimp_dialog_new (_("Disable safety size limits?"),
+ PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, LOAD_PROC,
+
+ _("_No"), GTK_RESPONSE_NO,
+ _("_Yes"), GTK_RESPONSE_YES,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_NO);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ /* Unfortunately the error returned by librsvg is unclear. While
+ * libxml explicitly returns a "parser error : internal error:
+ * Huge input lookup", librsvg does not seem to relay this error.
+ * It sends a further parsing error, false positive provoked by
+ * the huge input error.
+ * If we were able to single out the huge data error, we could
+ * just directly return from the plug-in as a failure run in other
+ * cases. Instead of this, we need to ask each and every time, in
+ * case it might be the huge data error.
+ */
+ label = gtk_label_new (_("A parsing error occurred.\n"
+ "Disabling safety limits may help. "
+ "Malicious SVG files may use this to consume too much memory."));
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_line_wrap_mode (GTK_LABEL (label), PANGO_WRAP_WORD);
+ gtk_label_set_max_width_chars (GTK_LABEL (label), 80);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (NULL);
+ text = g_strdup_printf ("<b>%s</b>",
+ _("For security reasons, this should only be used for trusted input!"));
+ gtk_label_set_markup (GTK_LABEL (label), text);
+ g_free (text);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (_("Retry without limits preventing to parse huge data?"));
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_YES);
+ gtk_widget_destroy (dialog);
+
+ if (run)
+ {
+ *rsvg_flags = RSVG_HANDLE_FLAG_UNLIMITED;
+ g_clear_error (&error);
+ preview = load_rsvg_pixbuf (file, &vals, *rsvg_flags, NULL, &error);
+ }
+ }
if (! preview)
{
@@ -668,8 +765,6 @@ load_dialog (GFile *file,
return GIMP_PDB_EXECUTION_ERROR;
}
- gimp_ui_init (PLUG_IN_BINARY);
-
/* Scalable Vector Graphics is SVG, should perhaps not be translated */
dialog = gimp_dialog_new (_("Render Scalable Vector Graphics"),
PLUG_IN_ROLE,
@@ -718,7 +813,7 @@ load_dialog (GFile *file,
/* query the initial size after the size label is created */
vals.resolution = load_vals.resolution;
- load_rsvg_size (file, &vals, NULL);
+ load_rsvg_size (file, &vals, *rsvg_flags, NULL);
svg_width = vals.width;
svg_height = vals.height;
@@ -868,6 +963,7 @@ load_dialog (GFile *file,
5.0, GIMP_MAX_RESOLUTION);
gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (res), 0, load_vals.resolution);
+ g_object_set_data (G_OBJECT (file), "rsvg-flags", GINT_TO_POINTER (rsvg_flags));
g_signal_connect (res, "value-changed",
G_CALLBACK (load_dialog_resolution_callback),
file);
@@ -914,5 +1010,5 @@ load_dialog (GFile *file,
gtk_widget_destroy (dialog);
- return run;
+ return run ? GIMP_PDB_SUCCESS : GIMP_PDB_CANCEL;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]