From b7219d51b150fa10018ccc810824940ea56284f1 Mon Sep 17 00:00:00 2001 From: Jacob Boerema Date: Tue, 8 Nov 2022 14:10:05 -0500 Subject: [PATCH] plug-ins: improve security in flame plug-in - Use g_malloc* functions instead of malloc, so we don't continue on failed allocations unless we test for NULL. - Make sure we don't iterate past the known number of control points (ncps). - Safely allocate, initialize and free points. Since points seems to be used uninitialized, we use g_malloc0 to set everything to 0. (cherry picked from commit 981979bb39a9453f33d8c33f12ef19ff15be25ba) --- plug-ins/flame/libifs.c | 17 +++++++++++++---- plug-ins/flame/rect.c | 13 +++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/plug-ins/flame/libifs.c b/plug-ins/flame/libifs.c index 16259d46a5..461ac5c96d 100644 --- a/plug-ins/flame/libifs.c +++ b/plug-ins/flame/libifs.c @@ -692,6 +692,8 @@ interpolate (control_point cps[], int i, j, i1, i2; double c0, c1, t; + g_return_if_fail (ncps > 0); + if (ncps == 1) { *result = cps[0]; @@ -710,12 +712,14 @@ interpolate (control_point cps[], else { i1 = 0; - while (cps[i1].time < time) + while (i1 < ncps && cps[i1].time < time) i1++; i1--; i2 = i1 + 1; - if (time - cps[i1].time > -1e-7 && - time - cps[i1].time < 1e-7) + + if (i2 == ncps || + (time - cps[i1].time > -1e-7 && + time - cps[i1].time < 1e-7)) { *result = cps[i1]; return; @@ -1373,7 +1380,8 @@ estimate_bounding_box (control_point *cp, int low_target = batch * eps; int high_target = batch - low_target; point min, max, delta; - point *points = malloc (sizeof (point) * batch); + point *points = g_malloc0 (sizeof (point) * batch); + iterate (cp, batch, 20, points); min[0] = min[1] = 1e10; @@ -1420,6 +1428,7 @@ estimate_bounding_box (control_point *cp, delta[0] = delta[0] / 2.0; delta[1] = delta[1] / 2.0; } + g_free (points); } /* this has serious flaws in it */ diff --git a/plug-ins/flame/rect.c b/plug-ins/flame/rect.c index 4951bfdfc8..0ff0f5f89c 100644 --- a/plug-ins/flame/rect.c +++ b/plug-ins/flame/rect.c @@ -20,6 +20,7 @@ #include +#include "libgimp/gimp.h" /* for batch * interpolate @@ -122,7 +123,7 @@ render_rectangle (frame_spec *spec, if ((filter_width ^ oversample) & 1) filter_width++; - filter = malloc (sizeof (double) * filter_width * filter_width); + filter = g_malloc (sizeof (double) * filter_width * filter_width); /* fill in the coefs */ for (i = 0; i < filter_width; i++) for (j = 0; j < filter_width; j++) @@ -135,8 +136,8 @@ render_rectangle (frame_spec *spec, } normalize_vector(filter, filter_width * filter_width); } - temporal_filter = malloc (sizeof (double) * nbatches); - temporal_deltas = malloc (sizeof (double) * nbatches); + temporal_filter = g_malloc (sizeof (double) * nbatches); + temporal_deltas = g_malloc (sizeof (double) * nbatches); if (nbatches > 1) { double t; @@ -173,11 +174,11 @@ render_rectangle (frame_spec *spec, { if (last_block != NULL) free (last_block); - last_block = malloc (memory_rqd); + last_block = g_try_malloc (memory_rqd); if (last_block == NULL) { - fprintf (stderr, "render_rectangle: cannot malloc %d bytes.\n", - memory_rqd); + g_printerr ("render_rectangle: cannot malloc %d bytes.\n", + memory_rqd); exit (1); } last_block_size = memory_rqd; --- From 5481b110f955213cca2e893f71b275a19f2530f7 Mon Sep 17 00:00:00 2001 From: Jacob Boerema Date: Mon, 31 Oct 2022 14:22:44 -0400 Subject: [PATCH] plug-ins: fix crash when reading corrupt flame settings file Thanks to a report by Stefan Cornelius, we became aware that the flame plug-in does no checking for correct input when loading a pre-saved settings file. I reworked the parser to read one or more values based on the type of token, making sure we also don't read past the end of our token buffer. All int values have a min and max value set. If any unexpected input is encountered, we will give a warning. (cherry picked from commit 89c83ef4c7a83bca5ca40f79d638ff365eb61464) --- plug-ins/flame/libifs.c | 341 +++++++++++++++++++++++++++++----------- 1 file changed, 251 insertions(+), 90 deletions(-) diff --git a/plug-ins/flame/libifs.c b/plug-ins/flame/libifs.c index 7f158c853f..16259d46a5 100644 --- a/plug-ins/flame/libifs.c +++ b/plug-ins/flame/libifs.c @@ -911,8 +911,6 @@ compare_xforms (const void *va, /* * given a pointer to a string SS, fill fields of a control point CP. - * return a pointer to the first unused char in SS. totally barfucious, - * must integrate with tcl soon... */ void @@ -921,10 +919,9 @@ parse_control_point (char **ss, { char *argv[MAXARGS]; int argc, i, j; - int set_cm = 0, set_image_size = 0, set_nbatches = 0, set_white_level = 0, set_cmap_inter = 0; - int set_spatial_oversample = 0; - double *slot = NULL, xf, cm, t, nbatches, white_level, spatial_oversample, cmap_inter; - double image_size[2]; + gint64 xf_index = 0; + gint parse_errors = 0; + double *slot = NULL, t; for (i = 0; i < NXFORMS; i++) { @@ -949,94 +946,258 @@ parse_control_point (char **ss, } tokenize (ss, argv, &argc); - for (i = 0; i < argc; i++) + + i = 0; + while (i < argc) { - if (streql("xform", argv[i])) - slot = &xf; - else if (streql("time", argv[i])) - slot = &cp->time; - else if (streql("brightness", argv[i])) - slot = &cp->brightness; - else if (streql("contrast", argv[i])) - slot = &cp->contrast; - else if (streql("gamma", argv[i])) - slot = &cp->gamma; - else if (streql("zoom", argv[i])) - slot = &cp->zoom; - else if (streql("image_size", argv[i])) - { - slot = image_size; - set_image_size = 1; - } - else if (streql("center", argv[i])) - slot = cp->center; - else if (streql("pulse", argv[i])) - slot = (double *) cp->pulse; - else if (streql("wiggle", argv[i])) - slot = (double *) cp->wiggle; - else if (streql("pixels_per_unit", argv[i])) - slot = &cp->pixels_per_unit; - else if (streql("spatial_filter_radius", argv[i])) - slot = &cp->spatial_filter_radius; - else if (streql("sample_density", argv[i])) - slot = &cp->sample_density; - else if (streql("nbatches", argv[i])) - { - slot = &nbatches; - set_nbatches = 1; - } - else if (streql("white_level", argv[i])) - { - slot = &white_level; - set_white_level = 1; - } - else if (streql("spatial_oversample", argv[i])) - { - slot = &spatial_oversample; - set_spatial_oversample = 1; - } - else if (streql("cmap", argv[i])) - { - slot = &cm; - set_cm = 1; - } - else if (streql("density", argv[i])) - slot = &cp->xform[(int)xf].density; - else if (streql("color", argv[i])) - slot = &cp->xform[(int)xf].color; - else if (streql("coefs", argv[i])) - { - slot = cp->xform[(int)xf].c[0]; - cp->xform[(int)xf].density = 1.0; - } - else if (streql("var", argv[i])) - slot = cp->xform[(int)xf].var; - else if (streql("cmap_inter", argv[i])) - { - slot = &cmap_inter; - set_cmap_inter = 1; + gint itoken; + + itoken = i; + if (i < argc) + { + /* First value belonging to token. */ + i++; } else - *slot++ = g_strtod(argv[i], NULL); - } - if (set_cm) - { - cp->cmap_index = (int) cm; - get_cmap(cp->cmap_index, cp->cmap, 256); - } - if (set_image_size) - { - cp->width = (int) image_size[0]; - cp->height = (int) image_size[1]; + { + g_printerr ("Not enough parameters. File may be corrupt!\n"); + parse_errors++; + break; + } + + if (streql ("xform", argv[itoken])) + { + if (! g_ascii_string_to_signed (argv[i++], 10, 0, NXFORMS-1, &xf_index, NULL)) + { + g_printerr ("Invalid xform index '%s'\n", argv[i-1]); + parse_errors++; + xf_index = 0; + } + } + else if (streql ("density", argv[itoken])) + { + cp->xform[xf_index].density = g_strtod (argv[i++], NULL); + } + else if (streql ("color", argv[itoken])) + { + cp->xform[xf_index].color = g_strtod (argv[i++], NULL); + } + else if (streql ("coefs", argv[itoken])) + { + /* We need 6 coef values and we know are at the first */ + if (i + 5 >= argc) + { + g_printerr ("Not enough parameters. File may be corrupt!\n"); + parse_errors++; + break; + } + slot = cp->xform[xf_index].c[0]; + for (j = 0; j < 6; j++) + { + *slot++ = g_strtod (argv[i++], NULL); + } + cp->xform[xf_index].density = 1.0; + } + else if (streql ("var", argv[itoken])) + { + /* We need NVARS var values and we know are at the first */ + if (i + NVARS > argc) + { + g_printerr ("Not enough parameters. File may be corrupt!\n"); + parse_errors++; + break; + } + slot = cp->xform[xf_index].var; + for (j = 0; j < NVARS; j++) + { + *slot++ = g_strtod (argv[i++], NULL); + } + } + else if (streql ("time", argv[itoken])) + { + cp->time = g_strtod (argv[i++], NULL); + } + else if (streql ("brightness", argv[itoken])) + { + cp->brightness = g_strtod (argv[i++], NULL); + } + else if (streql ("contrast", argv[itoken])) + { + cp->contrast = g_strtod (argv[i++], NULL); + } + else if (streql ("gamma", argv[itoken])) + { + cp->gamma = g_strtod (argv[i++], NULL); + } + else if (streql ("zoom", argv[itoken])) + { + cp->zoom = g_strtod (argv[i++], NULL); + } + else if (streql ("image_size", argv[itoken])) + { + gint64 w, h; + + /* We need 2 values and we know are at the first */ + if (i + 1 >= argc) + { + g_printerr ("Not enough parameters. File may be corrupt!\n"); + parse_errors++; + break; + } + if (! g_ascii_string_to_signed (argv[i++], 10, 1, GIMP_MAX_IMAGE_SIZE, &w, NULL)) + { + g_printerr ("Ignoring invalid image width '%s'\n", argv[i-1]); + parse_errors++; + } + else if (! g_ascii_string_to_signed (argv[i++], 10, 1, GIMP_MAX_IMAGE_SIZE, &h, NULL)) + { + g_printerr ("Ignoring invalid image_size heigth '%s'\n", argv[i-1]); + parse_errors++; + } + else + { + cp->width = w; + cp->height = h; + } + } + else if (streql ("center", argv[itoken])) + { + /* We need 2 values and we know are at the first */ + if (i + 1 >= argc) + { + g_printerr ("Not enough parameters. File may be corrupt!\n"); + parse_errors++; + break; + } + cp->center[0] = g_strtod (argv[i++], NULL); + cp->center[1] = g_strtod (argv[i++], NULL); + } + else if (streql ("pixels_per_unit", argv[itoken])) + { + cp->pixels_per_unit = g_strtod (argv[i++], NULL); + } + else if (streql ("pulse", argv[itoken])) + { + /* We need 4 values and we know are at the first */ + if (i + 3 >= argc) + { + g_printerr ("Not enough parameters. File may be corrupt!\n"); + parse_errors++; + break; + } + slot = &cp->pulse[0][0]; + for (j = 0; j < 4; j++) + { + *slot++ = g_strtod (argv[i++], NULL); + } + } + else if (streql ("wiggle", argv[itoken])) + { + /* We need 4 values and we know are at the first */ + if (i + 3 >= argc) + { + g_printerr ("Not enough parameters. File may be corrupt!\n"); + parse_errors++; + break; + } + slot = &cp->wiggle[0][0]; + for (j = 0; j < 4; j++) + { + *slot++ = g_strtod (argv[i++], NULL); + } + } + else if (streql ("spatial_oversample", argv[itoken])) + { + gint64 oversample; + + /* Values in the gui seem to be between 1 and 4 */ + if (! g_ascii_string_to_signed (argv[i++], 10, 1, 4, &oversample, NULL)) + { + g_printerr ("Ignoring invalid spatial oversample value '%s'\n", argv[i-1]); + parse_errors++; + } + else + { + cp->spatial_oversample = oversample; + } + } + else if (streql ("spatial_filter_radius", argv[itoken])) + { + cp->spatial_filter_radius = g_strtod (argv[i++], NULL); + } + else if (streql ("sample_density", argv[itoken])) + { + cp->sample_density = g_strtod (argv[i++], NULL); + } + else if (streql ("nbatches", argv[itoken])) + { + gint64 nbatches; + + /* Not sure what the maximum should be. It always seems to be set to 1. */ + if (! g_ascii_string_to_signed (argv[i++], 10, 0, 2, &nbatches, NULL)) + { + g_printerr ("Ignoring invalid nbatches value '%s'\n", argv[i-1]); + parse_errors++; + } + else + { + cp->nbatches = nbatches; + } + } + else if (streql ("white_level", argv[itoken])) + { + gint64 wl; + + if (! g_ascii_string_to_signed (argv[i++], 10, 0, 255, &wl, NULL)) + { + g_printerr ("Ignoring invalid white level value '%s'\n", argv[i-1]); + parse_errors++; + } + else + { + cp->white_level = wl; + } + } + else if (streql ("cmap", argv[itoken])) + { + gint64 cmi; + + /* -1 = random */ + if (! g_ascii_string_to_signed (argv[i++], 10, -1, 255, &cmi, NULL)) + { + g_printerr ("Ignoring invalid color map value '%s'\n", argv[i-1]); + parse_errors++; + } + else + { + cp->cmap_index = cmi; + } + } + else if (streql ("cmap_inter", argv[itoken])) + { + gint64 cmi; + + /* 0 or 1 */ + if (! g_ascii_string_to_signed (argv[i++], 10, 0, 1, &cmi, NULL)) + { + g_printerr ("Ignoring invalid color interpolate value '%s'\n", argv[i-1]); + parse_errors++; + } + else + { + cp->cmap_inter = cmi; + } + } + else + { + g_printerr ("Invalid token '%s'. File may be corrupt!\n", argv[itoken]); + parse_errors++; + } } - if (set_cmap_inter) - cp->cmap_inter = (int) cmap_inter; - if (set_nbatches) - cp->nbatches = (int) nbatches; - if (set_spatial_oversample) - cp->spatial_oversample = (int) spatial_oversample; - if (set_white_level) - cp->white_level = (int) white_level; + + if (parse_errors > 0) + g_warning ("Input file contains %d errors. File may be corrupt!", parse_errors); + for (i = 0; i < NXFORMS; i++) { t = 0.0; -- From b36cc3ab3f0440edc611f35cc16442839526aa09 Mon Sep 17 00:00:00 2001 From: Jacob Boerema Date: Mon, 31 Oct 2022 14:00:54 -0400 Subject: [PATCH] plug-ins: fix missing input buffer length checking in flame The flame plug-in can read stored settings from a file. The expected input is that a ; signifies the end of input. However, with user input we cannot depend on this to be true, so we need to make sure that we do not read past the end of our input buffer. To do so, we add a length check. (cherry picked from commit 536c7cbc4b7c5fe5e1d03ac249d74f851a0e2e87) --- plug-ins/flame/libifs.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plug-ins/flame/libifs.c b/plug-ins/flame/libifs.c index a4af48bb30..7f158c853f 100644 --- a/plug-ins/flame/libifs.c +++ b/plug-ins/flame/libifs.c @@ -842,10 +842,12 @@ tokenize (char **ss, char *argv[], int *argc) { - char *s = *ss; - int i = 0, state = 0; + char *s = *ss; + int i = 0, state = 0; + gint len = 0; - while (*s != ';') + len = strlen (s); + while (*s != ';' && len > 0) { char c = *s; switch (state) @@ -870,6 +872,7 @@ tokenize (char **ss, state = 0; } s++; + len--; } *s = 0; *ss = s + 1; -- From b3186d72ee559b8ba78900acba134967338bf642 Mon Sep 17 00:00:00 2001 From: Jacob Boerema Date: Mon, 31 Oct 2022 13:57:14 -0400 Subject: [PATCH] plug-ins: fix failure to load flame saved settings from file We were using the plug-in name with underscores, which is incorrect. Since nobody ever complained about this, this doesn't seem to be used very often. We will use the const that defines the plug-in name instead. (cherry picked from commit 193596397e6671ccb3684683adf1d2af48c14d05) --- plug-ins/flame/flame.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plug-ins/flame/flame.c b/plug-ins/flame/flame.c index e7480cba1c..f2e3898f65 100644 --- a/plug-ins/flame/flame.c +++ b/plug-ins/flame/flame.c @@ -436,7 +436,7 @@ file_response_callback (GtkFileChooser *chooser, fclose (f); /* i want to update the existing dialogue, but it's too painful */ - gimp_set_data ("plug_in_flame", &config, sizeof (config)); + gimp_set_data (PLUG_IN_PROC, &config, sizeof (config)); /* gtk_widget_destroy(dialog); */ set_flame_preview (); set_edit_preview (); -- diff -urNp a/plug-ins/flame/libifs.c b/plug-ins/flame/libifs.c --- a/plug-ins/flame/libifs.c 2023-01-11 12:37:26.893377867 +0100 +++ b/plug-ins/flame/libifs.c 2023-01-11 12:38:31.093911423 +0100 @@ -865,15 +865,18 @@ tokenize (char **ss, i++; state = 1; } + break; case 1: if (g_ascii_isspace (c)) { *s = 0; state = 0; } + break; case 2: if (c == '\n') state = 0; + break; } s++; len--;