Commit 49b3e05a authored by Matthias Clasen's avatar Matthias Clasen
Browse files

Robustness fixes and test images for the jpeg, tiff, pnm, gif, xpm and tga

loaders.
parent a5f97543
Fri Sep 14 22:53:45 2001 Matthias Clasen <matthiasc@poet.de>
* io-jpeg.c (gdk_pixbuf__jpeg_image_begin_load): Handle
out-of-memory situation better.
* io-pnm.c: Robustness fixes for pnm loader
* io-gif.c: Make gif loader survive TEST_RANDOMLY_MODIFY
* io-xpm.c (parse_color): detect very long color strings
All of the above from Matthias Clasen <matthiasc@poet.de>,
the remaining from Soeren Sandmann <sandmann@daimi.au.dk>.
* test-images.h: add tests:
- four valid ppm's, eight invalid (matthiasc@poet.de)
- one invalid xpm (matthiasc@poet.de)
- one valid tga, one invalid (sandmann@daimi.au.dk)
- one invalid tiff (sandmann@daimi.au.dk
* test-loaders.c: enable tests for ppm, png, gif, tga, tiff, xpm
* io-tiff.c (gdk_pixbuf__tiff_image_stop_load): TIFFClientOpen can
fail - detect it when it happens
* io-tga.c (gdk_pixbuf__tga_load_increment): Fail if there is too
much data in file
* gdk-pixbuf-io.c: moved wbmb check after ico check to avoid false
positives
Fri Sep 14 00:30:48 2001 Tim Janik <timj@gtk.org>
* Makefile.am: don't used BUILT_SOURCES to build marshal
......
......@@ -228,12 +228,12 @@ static GdkPixbufModule file_formats [] = {
{ "pnm", pixbuf_check_pnm, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{ "ras", pixbuf_check_sunras, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{ "bmp", pixbuf_check_bmp, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{ "wbmp", pixbuf_check_wbmp, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{ "xbm", pixbuf_check_xbm, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{ "tga", pixbuf_check_tga, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
/* Moved at the bottom, because it causes false positives against many
of my TGA files. */
{ "ico", pixbuf_check_ico, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{ "ico", pixbuf_check_ico, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{ "wbmp", pixbuf_check_wbmp, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
......
......@@ -613,6 +613,14 @@ lzw_read_byte (GifContext *context)
}
while (code >= context->lzw_clear_code) {
if ((code >= (1 << MAX_LZW_BITS))
|| (context->lzw_sp >= context->lzw_stack + ((1 << (MAX_LZW_BITS)) * 2 + 1))) {
g_set_error (context->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Bad code encountered"));
return -2;
}
*(context->lzw_sp)++ = context->lzw_table[1][code];
if (code == context->lzw_table[0][code]) {
......@@ -1038,7 +1046,7 @@ gif_init (GifContext *context)
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("File does not appear to be a GIF file"));
return -1;
return -2;
}
strncpy (version, (char *) buf + 3, 3);
......@@ -1051,7 +1059,7 @@ gif_init (GifContext *context)
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Version %s of the GIF file format is not supported"),
version);
return -1;
return -2;
}
/* read the screen descriptor */
......
......@@ -337,7 +337,16 @@ gdk_pixbuf__jpeg_image_begin_load (ModulePreparedNotifyFunc prepared_func,
/* create libjpeg structures */
jpeg_create_decompress (&context->cinfo);
context->cinfo.src = (struct jpeg_source_mgr *) g_new0 (my_source_mgr, 1);
context->cinfo.src = (struct jpeg_source_mgr *) g_try_malloc (sizeof (my_source_mgr));
if (!context->cinfo.src) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Couldn't allocate memory for loading JPEG file"));
return NULL;
}
memset (context->cinfo.src, 0, sizeof (my_source_mgr));
src = (my_src_ptr) context->cinfo.src;
context->cinfo.err = jpeg_std_error (&context->jerr.pub);
......
......@@ -23,7 +23,6 @@
*/
#include <config.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
......@@ -203,7 +202,7 @@ pnm_skip_whitespace (PnmIOBuffer *inbuf, GError **error)
return PNM_SUSPEND;
}
} else if (!isspace (*inptr)) {
} else if (!g_ascii_isspace (*inptr)) {
inbuf->byte = inptr;
inbuf->nbytes = (guint) (inend - inptr);
return PNM_OK;
......@@ -221,9 +220,10 @@ static gint
pnm_read_next_value (PnmIOBuffer *inbuf, guint *value, GError **error)
{
register guchar *inptr, *word, *p;
guchar *inend, buf[128];
guchar *inend, buf[129];
gchar *endptr;
gint retval;
glong result;
g_return_val_if_fail (inbuf != NULL, PNM_FATAL_ERR);
g_return_val_if_fail (inbuf->byte != NULL, PNM_FATAL_ERR);
......@@ -237,24 +237,25 @@ pnm_read_next_value (PnmIOBuffer *inbuf, guint *value, GError **error)
inptr = inbuf->byte;
/* copy this pnm 'word' into a temp buffer */
for (p = inptr, word = buf; (p < inend) && !isspace (*p) && (p - inptr < 128); p++, word++)
for (p = inptr, word = buf; (p < inend) && !g_ascii_isspace (*p) && (*p != '#') && (p - inptr < 128); p++, word++)
*word = *p;
*word = '\0';
/* hmmm, there must be more data to this 'word' */
if (!isspace (*p))
return PNM_SUSPEND;
if (!g_ascii_isspace (*p) && (*p != '#') && (p - inptr < 128))
return PNM_SUSPEND;
/* get the value */
*value = strtol (buf, &endptr, 10);
if (*endptr != '\0') {
result = strtol (buf, &endptr, 10);
if (*endptr != '\0' || result < 0) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("PNM loader expected to find an integer, but didn't"));
return PNM_FATAL_ERR;
}
*value = result;
inbuf->byte = p;
inbuf->nbytes = (guint) (inend - p);
......@@ -281,7 +282,7 @@ pnm_read_header (PnmLoaderContext *context)
if (*inbuf->byte != 'P') {
g_set_error (context->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
GDK_PIXBUF_ERROR_HEADER_CORRUPT,
_("PNM file has an incorrect initial byte"));
return PNM_FATAL_ERR;
}
......@@ -311,7 +312,7 @@ pnm_read_header (PnmLoaderContext *context)
default:
g_set_error (context->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
GDK_PIXBUF_ERROR_HEADER_CORRUPT,
_("PNM file is not in a recognized PNM subformat"));
return PNM_FATAL_ERR;
}
......@@ -336,7 +337,7 @@ pnm_read_header (PnmLoaderContext *context)
if (!width) {
g_set_error (context->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
GDK_PIXBUF_ERROR_HEADER_CORRUPT,
_("PNM file has an image width of 0"));
return PNM_FATAL_ERR;
}
......@@ -357,7 +358,7 @@ pnm_read_header (PnmLoaderContext *context)
if (!height) {
g_set_error (context->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
GDK_PIXBUF_ERROR_HEADER_CORRUPT,
_("PNM file has an image height of 0"));
return PNM_FATAL_ERR;
}
......@@ -380,8 +381,24 @@ pnm_read_header (PnmLoaderContext *context)
if (context->maxval == 0) {
g_set_error (context->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Maximum color value in PNM file is 0"));
GDK_PIXBUF_ERROR_HEADER_CORRUPT,
_("Maximum color value in PNM file is 0"));
return PNM_FATAL_ERR;
}
if (context->maxval > 65535) {
g_set_error (context->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_HEADER_CORRUPT,
_("Maximum color value in PNM file is too large"));
return PNM_FATAL_ERR;
}
if (context->maxval > 255) {
g_set_error (context->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_HEADER_CORRUPT,
_("Can't handle PNM files with maximum color values greater than 255"));
return PNM_FATAL_ERR;
}
}
......@@ -677,7 +694,7 @@ gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
inbuf = &context.inbuf;
while (!feof (f)) {
while (TRUE) {
guint num_to_read;
/* keep buffer as full as possible */
......@@ -689,11 +706,14 @@ gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
nbytes = fread (inbuf->buffer + inbuf->nbytes, 1, num_to_read, f);
/* error checking */
if (nbytes == 0 && ferror (f)) {
if (nbytes == 0) {
/* we ran out of data? */
if (context.pixbuf)
gdk_pixbuf_unref (context.pixbuf);
g_warning ("io-pnm.c: Ran out of data.\n");
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Premature end-of-file encountered"));
return NULL;
}
......@@ -713,23 +733,47 @@ gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
/* scan until we hit image data */
if (!context.did_prescan) {
retval = pnm_skip_whitespace (inbuf,
context.error);
if (retval == PNM_FATAL_ERR)
return NULL;
else if (retval == PNM_SUSPEND)
continue;
switch (context.type) {
case PNM_FORMAT_PBM_RAW:
case PNM_FORMAT_PGM_RAW:
case PNM_FORMAT_PPM_RAW:
if (inbuf->nbytes <= 0)
continue;
/* raw formats require exactly one whitespace */
if (!g_ascii_isspace(*(inbuf->byte)))
{
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Raw PNM formats require exactly one whitespace before sample data"));
return NULL;
}
inbuf->nbytes--;
inbuf->byte++;
break;
default:
retval = pnm_skip_whitespace (inbuf,
context.error);
if (retval == PNM_FATAL_ERR)
return NULL;
else if (retval == PNM_SUSPEND)
continue;
break;
}
context.did_prescan = TRUE;
context.output_row = 0;
context.output_col = 0;
context.rowstride = context.width * 3;
context.pixels = g_malloc (context.height * context.width * 3);
context.pixels = g_try_malloc (context.height * context.width * 3);
if (!context.pixels) {
/* Failed to allocate memory */
g_warning ("Couldn't allocate pixel buf");
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Can't allocate memory for loading PNM image"));
return NULL;
}
}
......@@ -773,7 +817,14 @@ gdk_pixbuf__pnm_image_begin_load (ModulePreparedNotifyFunc prepared_func,
{
PnmLoaderContext *context;
context = g_new0 (PnmLoaderContext, 1);
context = g_try_malloc (sizeof (PnmLoaderContext));
if (!context) {
g_set_error(error, GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Insufficient memory to load PNM context struct"));
return NULL;
}
memset (context, 0, sizeof (PnmLoaderContext));
context->prepared_func = prepared_func;
context->updated_func = updated_func;
context->user_data = user_data;
......@@ -811,6 +862,11 @@ gdk_pixbuf__pnm_image_stop_load (gpointer data,
if (context->pixbuf)
gdk_pixbuf_unref (context->pixbuf);
#if 0
/* We should ignore trailing newlines and we can't
generally complain about trailing stuff at all, since
pnm allows to put multiple images in a file
*/
if (context->inbuf.nbytes > 0) {
g_set_error (error,
GDK_PIXBUF_ERROR,
......@@ -818,6 +874,7 @@ gdk_pixbuf__pnm_image_stop_load (gpointer data,
_("Unexpected end of PNM image data"));
retval = FALSE;
}
#endif
g_free (context);
......@@ -895,14 +952,33 @@ gdk_pixbuf__pnm_image_load_increment (gpointer data,
/* scan until we hit image data */
if (!context->did_prescan) {
retval = pnm_skip_whitespace (inbuf,
context->error);
if (retval == PNM_FATAL_ERR)
return FALSE;
else if (retval == PNM_SUSPEND)
continue;
switch (context->type) {
case PNM_FORMAT_PBM_RAW:
case PNM_FORMAT_PGM_RAW:
case PNM_FORMAT_PPM_RAW:
if (inbuf->nbytes <= 0)
continue;
/* raw formats require exactly one whitespace */
if (!g_ascii_isspace(*(inbuf->byte)))
{
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Raw PNM formats require exactly one whitespace before sample data"));
return FALSE;
}
inbuf->nbytes--;
inbuf->byte++;
break;
default:
retval = pnm_skip_whitespace (inbuf,
context->error);
if (retval == PNM_FATAL_ERR)
return FALSE;
else if (retval == PNM_SUSPEND)
continue;
break;
}
context->did_prescan = TRUE;
context->output_row = 0;
context->output_col = 0;
......
......@@ -17,6 +17,9 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
/*
* Some NOTES about the TGA loader (2001/06/07, nikke@swlibero.org)
*
* - The module doesn't currently provide support for TGA images where the
......@@ -117,6 +120,7 @@ struct _TGAColor {
struct _TGAContext {
TGAHeader *hdr;
guint rowstride;
guint completed_lines;
gboolean run_length_encoded;
TGAColormap *cmap;
......@@ -293,6 +297,7 @@ static gboolean fill_in_context(TGAContext *ctx, GError **err)
else if (ctx->hdr->type == TGA_TYPE_TRUECOLOR)
ctx->rowstride = ctx->pbuf->rowstride;
ctx->completed_lines = 0;
return TRUE;
}
......@@ -724,9 +729,16 @@ static gboolean gdk_pixbuf__tga_load_increment(gpointer data,
if (!parse_rle_data(ctx, err))
return FALSE;
} else {
while (ctx->in->size >= ctx->rowstride)
while (ctx->in->size >= ctx->rowstride) {
if (ctx->completed_lines >= ctx->pbuf->height) {
g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
_("Excess data in file"));
return FALSE;
}
if (!parse_data_for_row(ctx, err))
return FALSE;
ctx->completed_lines++;
}
}
return TRUE;
......
......@@ -257,11 +257,10 @@ gdk_pixbuf__tiff_image_load (FILE *f, GError **error)
fd = fileno (f);
tiff = TIFFFdOpen (fd, "libpixbuf-tiff", "r");
if (!tiff) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Failed to open TIFF image"));
if (!tiff || global_error) {
tiff_set_error (error,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Failed to open TIFF image"));
tiff_pop_handlers ();
G_UNLOCK (tiff_loader);
......@@ -404,11 +403,10 @@ gdk_pixbuf__tiff_image_stop_load (gpointer data,
tiff_seek, tiff_close,
tiff_size,
tiff_map_file, tiff_unmap_file);
if (!tiff) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_FAILED,
_("Failed to load TIFF image"));
if (!tiff || global_error) {
tiff_set_error (error,
GDK_PIXBUF_ERROR_FAILED,
_("Failed to load TIFF image"));
retval = FALSE;
} else {
GdkPixbuf *pixbuf;
......@@ -418,10 +416,16 @@ gdk_pixbuf__tiff_image_stop_load (gpointer data,
g_object_unref (G_OBJECT (pixbuf));
retval = pixbuf != NULL;
TIFFClose (tiff);
if (global_error)
{
tiff_set_error (error,
GDK_PIXBUF_ERROR_FAILED,
_("Failed to load TIFF image"));
retval = FALSE;
}
}
if (global_error)
g_warning ("Error left set in TIFF loader\n");
g_assert (!global_error);
g_free (context->buffer);
g_free (context);
......
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* GdkPixbuf library - XPM image loader
*
* Copyright (C) 1999 Mark Crichton
......@@ -926,7 +927,7 @@ find_color(const char *name,
* Partial implementation of X color name parsing interface.
*
* Results:
* Returns non-zero on success.
* Returns TRUE on success.
*
* Side effects:
* None.
......@@ -947,7 +948,8 @@ parse_color (const char *spec,
}
i /= 3;
sprintf(fmt, "%%%dx%%%dx%%%dx", i, i, i);
g_snprintf (fmt, 16, "%%%dx%%%dx%%%dx", i, i, i);
if (sscanf(spec+1, fmt, &red, &green, &blue) != 3) {
return FALSE;
}
......@@ -977,20 +979,13 @@ parse_color (const char *spec,
}
static gint
xpm_seek_string (FILE *infile, const gchar *str, gint skip_comments)
xpm_seek_string (FILE *infile, const gchar *str)
{
char instr[1024];
while (!feof (infile)) {
if (fscanf (infile, "%1023s", instr) < 0)
return FALSE;
if (skip_comments == TRUE && strcmp (instr, "/*") == 0) {
fscanf (infile, "%1023s", instr);
while (!feof (infile) && strcmp (instr, "*/") != 0)
fscanf (infile, "%1023s", instr);
fscanf (infile, "%1023s", instr);
}
if (strcmp (instr, str) == 0)
return TRUE;
}
......@@ -1176,7 +1171,7 @@ file_buffer (enum buf_op op, gpointer handle)
switch (op) {
case op_header:
if (xpm_seek_string (h->infile, "XPM", FALSE) != TRUE)
if (xpm_seek_string (h->infile, "XPM") != TRUE)
break;
if (xpm_seek_char (h->infile, '{') != TRUE)
......@@ -1258,11 +1253,35 @@ pixbuf_create_from_xpm (const gchar * (*get_buf) (enum buf_op op, gpointer handl
return NULL;
}
sscanf (buffer, "%d %d %d %d", &w, &h, &n_col, &cpp);
if (cpp >= 32) {
if (w <= 0) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("XPM file has image width <= 0"));
return NULL;
}
if (h <= 0) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("XPM file has image height <= 0"));
return NULL;
}
if (n_col <= 0) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("XPM file has invalid number of colors"));
return NULL;
}
if (cpp <= 0 || cpp >= 32) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("XPM has more than 31 chars per pixel"));
_("XPM has invalid number of chars per pixel"));
return NULL;
}
......@@ -1430,7 +1449,6 @@ gdk_pixbuf__xpm_image_begin_load (ModulePreparedNotifyFunc prepare_func,
XPMContext *context;
gint fd;
g_warning ("load start");
context = g_new (XPMContext, 1);
context->prepare_func = prepare_func;
context->update_func = update_func;
......
This diff is collapsed.
......@@ -285,7 +285,7 @@ randomly_modify (const guchar *image, guint size, gboolean verbose)
#define TEST(bytes, data_is_ok) \
do { \
g_print ("%-30s", " " #bytes " "); \
g_print ("%-40s", " " #bytes " "); \
fflush (stdout); \
if (test_loader (bytes, sizeof (bytes), data_is_ok)) \
g_print ("\tpassed\n"); \
......@@ -295,7 +295,7 @@ do { \
#define LOWMEMTEST(bytes) \
do { \
g_print ("%-30s", "memory " #bytes " "); \
g_print ("%-40s", "memory " #bytes " "); \
fflush (stdout); \
mem_test (bytes, sizeof (bytes)); \
g_print ("\tpassed\n"); \
......@@ -304,19 +304,19 @@ do { \
#define TEST_RANDOM(header, n_img, verbose) \
do { \
static guchar h[] = { header }; \
g_print ("%-30s", "random " #header " "); \
g_print ("%-40s", "random " #header " "); \
fflush (stdout); \
assault (h, sizeof (h), n_img, verbose); \
g_print ("\tpassed\n"); \
} while (0);
} while (0)
#define TEST_RANDOMLY_MODIFIED(image, verbose) \
do { \
g_print ("%-30s", "randomly modified " #image " "); \
g_print ("%-40s", "randomly modified " #image " "); \
fflush (stdout); \
randomly_modify (image, sizeof (image), verbose); \
g_print ("\tpassed\n"); \
} while (0);
} while (0)
......@@ -372,10 +372,24 @@ main (int argc, char **argv)
g_random_set_seed (seed);
g_type_init ();
g_log_set_fatal_mask (NULL, G_LOG_LEVEL_WARNING | G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
putenv ("GDK_PIXBUF_MODULEDIR="BUILT_MODULES_DIR);
TEST (valid_ppm_1, TRUE);
TEST (valid_ppm_2, TRUE);
TEST (valid_ppm_3, FALSE); /* image is valid, but we don't handle maxval > 255 */