mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-03 17:33:25 +00:00
plug-ins: CWE-190: Integer Overflow or Wraparound in Despeckle
As reported by Seungho Kim our despeckle filter doesn't check for integer overflow when allocating buffers, nor do we check for failed allocations. A potential integer overflow vulnerability exists in the GIMP "Despeckle" plug-in. The issue occurs due to unchecked multiplication of image dimensions (width, height) and bytes-per-pixel (img_bpp), which can result in allocating insufficient memory and subsequently performing out-of-bounds writes. This could lead to heap corruption and potential denial-of-service (DoS) or arbitrary code execution in certain scenarios. Vulnerability Details •width and height are of type guint (signed 32-bit int). •Multiplying width * height * img_bpp can result in a value exceeding the bounds of gsize. •g_new() does not perform overflow protection; if the size wraps around, less memory than needed will be allocated. •Subsequent pixel processing loops write beyond the allocated memory region (src, dst). Proof of Concept (PoC) Open a specially crafted image with very large dimensions (e.g., 70,000 x 70,000 pixels) and apply the Despeckle filter. GIMP may crash due to heap corruption, or undefined behavior may occur. We applied the suggested changes and in addition adjusted the despeckle function to be able to set error messages, and check for NULL allocations.
This commit is contained in:
parent
d7901a8890
commit
548bc3a46d
1 changed files with 49 additions and 13 deletions
|
@ -98,8 +98,9 @@ static GimpValueArray * despeckle_run (GimpProcedure *proced
|
||||||
GimpProcedureConfig *config,
|
GimpProcedureConfig *config,
|
||||||
gpointer run_data);
|
gpointer run_data);
|
||||||
|
|
||||||
static void despeckle (GimpDrawable *drawable,
|
static gboolean despeckle (GimpDrawable *drawable,
|
||||||
GObject *config);
|
GObject *config,
|
||||||
|
GError **error);
|
||||||
static void despeckle_median (GObject *config,
|
static void despeckle_median (GObject *config,
|
||||||
guchar *src,
|
guchar *src,
|
||||||
guchar *dst,
|
guchar *dst,
|
||||||
|
@ -224,13 +225,12 @@ despeckle_run (GimpProcedure *procedure,
|
||||||
gpointer run_data)
|
gpointer run_data)
|
||||||
{
|
{
|
||||||
GimpDrawable *drawable;
|
GimpDrawable *drawable;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
gegl_init (NULL, NULL);
|
gegl_init (NULL, NULL);
|
||||||
|
|
||||||
if (gimp_core_object_array_get_length ((GObject **) drawables) != 1)
|
if (gimp_core_object_array_get_length ((GObject **) drawables) != 1)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
g_set_error (&error, GIMP_PLUG_IN_ERROR, 0,
|
g_set_error (&error, GIMP_PLUG_IN_ERROR, 0,
|
||||||
_("Procedure '%s' only works with one drawable."),
|
_("Procedure '%s' only works with one drawable."),
|
||||||
PLUG_IN_PROC);
|
PLUG_IN_PROC);
|
||||||
|
@ -250,7 +250,10 @@ despeckle_run (GimpProcedure *procedure,
|
||||||
if (run_mode == GIMP_RUN_INTERACTIVE && ! despeckle_dialog (procedure, G_OBJECT (config), drawable))
|
if (run_mode == GIMP_RUN_INTERACTIVE && ! despeckle_dialog (procedure, G_OBJECT (config), drawable))
|
||||||
return gimp_procedure_new_return_values (procedure, GIMP_PDB_CANCEL, NULL);
|
return gimp_procedure_new_return_values (procedure, GIMP_PDB_CANCEL, NULL);
|
||||||
|
|
||||||
despeckle (drawable, G_OBJECT (config));
|
if (! despeckle (drawable, G_OBJECT (config), &error))
|
||||||
|
return gimp_procedure_new_return_values (procedure,
|
||||||
|
GIMP_PDB_EXECUTION_ERROR,
|
||||||
|
error);
|
||||||
|
|
||||||
return gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL);
|
return gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL);
|
||||||
}
|
}
|
||||||
|
@ -323,9 +326,10 @@ get_u8_format (GimpDrawable *drawable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static gboolean
|
||||||
despeckle (GimpDrawable *drawable,
|
despeckle (GimpDrawable *drawable,
|
||||||
GObject *config)
|
GObject *config,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
GeglBuffer *src_buffer;
|
GeglBuffer *src_buffer;
|
||||||
GeglBuffer *dest_buffer;
|
GeglBuffer *dest_buffer;
|
||||||
|
@ -335,10 +339,11 @@ despeckle (GimpDrawable *drawable,
|
||||||
gint img_bpp;
|
gint img_bpp;
|
||||||
gint x, y;
|
gint x, y;
|
||||||
gint width, height;
|
gint width, height;
|
||||||
|
gsize bufsize = 0;
|
||||||
|
|
||||||
if (! gimp_drawable_mask_intersect (drawable,
|
if (! gimp_drawable_mask_intersect (drawable,
|
||||||
&x, &y, &width, &height))
|
&x, &y, &width, &height))
|
||||||
return;
|
return TRUE;
|
||||||
|
|
||||||
format = get_u8_format (drawable);
|
format = get_u8_format (drawable);
|
||||||
img_bpp = babl_format_get_bytes_per_pixel (format);
|
img_bpp = babl_format_get_bytes_per_pixel (format);
|
||||||
|
@ -346,8 +351,26 @@ despeckle (GimpDrawable *drawable,
|
||||||
src_buffer = gimp_drawable_get_buffer (drawable);
|
src_buffer = gimp_drawable_get_buffer (drawable);
|
||||||
dest_buffer = gimp_drawable_get_shadow_buffer (drawable);
|
dest_buffer = gimp_drawable_get_shadow_buffer (drawable);
|
||||||
|
|
||||||
src = g_new (guchar, width * height * img_bpp);
|
if (! g_size_checked_mul (&bufsize, width, height) ||
|
||||||
dst = g_new (guchar, width * height * img_bpp);
|
! g_size_checked_mul (&bufsize, bufsize, img_bpp))
|
||||||
|
{
|
||||||
|
g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
|
||||||
|
_("Image dimensions too large: width %d x height %d"),
|
||||||
|
width, height);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
src = g_try_malloc (bufsize);
|
||||||
|
dst = g_try_malloc (bufsize);
|
||||||
|
|
||||||
|
if (src == NULL || dst == NULL)
|
||||||
|
{
|
||||||
|
g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
|
||||||
|
_("There was not enough memory to complete the operation."));
|
||||||
|
g_free (src);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x, y, width, height), 1.0,
|
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x, y, width, height), 1.0,
|
||||||
format, src,
|
format, src,
|
||||||
|
@ -368,6 +391,8 @@ despeckle (GimpDrawable *drawable,
|
||||||
|
|
||||||
g_free (dst);
|
g_free (dst);
|
||||||
g_free (src);
|
g_free (src);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -448,6 +473,7 @@ preview_update (GtkWidget *widget,
|
||||||
{
|
{
|
||||||
GimpPreview *preview = GIMP_PREVIEW (widget);
|
GimpPreview *preview = GIMP_PREVIEW (widget);
|
||||||
GimpDrawable *drawable = g_object_get_data (config, "drawable");
|
GimpDrawable *drawable = g_object_get_data (config, "drawable");
|
||||||
|
gsize bufsize = 0;
|
||||||
GeglBuffer *src_buffer;
|
GeglBuffer *src_buffer;
|
||||||
const Babl *format;
|
const Babl *format;
|
||||||
guchar *dst;
|
guchar *dst;
|
||||||
|
@ -464,8 +490,18 @@ preview_update (GtkWidget *widget,
|
||||||
|
|
||||||
src_buffer = gimp_drawable_get_buffer (drawable);
|
src_buffer = gimp_drawable_get_buffer (drawable);
|
||||||
|
|
||||||
dst = g_new (guchar, width * height * img_bpp);
|
if (! g_size_checked_mul (&bufsize, width, height) ||
|
||||||
src = g_new (guchar, width * height * img_bpp);
|
! g_size_checked_mul (&bufsize, bufsize, img_bpp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
src = g_try_malloc (bufsize);
|
||||||
|
dst = g_try_malloc (bufsize);
|
||||||
|
|
||||||
|
if (src == NULL || dst == NULL)
|
||||||
|
{
|
||||||
|
g_free (src);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y1, width, height), 1.0,
|
gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y1, width, height), 1.0,
|
||||||
format, src,
|
format, src,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue