xcf: Fix potential buffer overflow on corrupt or maliciously-crafted XCF file.
2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
22 /* This is a XCF image file loading framework */
24 #include "SDL_endian.h"
25 #include "SDL_image.h"
30 static char prop_names [][30] = {
41 "preserve_transparency",
64 PROP_ACTIVE_LAYER = 2,
65 PROP_ACTIVE_CHANNEL = 3,
67 PROP_FLOATING_SELECTION = 5,
72 PROP_PRESERVE_TRANSPARENCY = 10,
76 PROP_SHOW_MASKED = 14,
79 PROP_COMPRESSION = 17,
111 Uint32 drawable_offset;
112 } floating_selection; // 5
117 int preserve_transparency;
124 unsigned char color [3];
144 xcf_prop * properties;
146 Uint32 * layer_file_offsets;
147 Uint32 * channel_file_offsets;
149 xcf_compr_type compr;
151 unsigned char * cm_map;
159 xcf_prop * properties;
161 Uint32 hierarchy_file_offset;
162 Uint32 layer_mask_offset;
173 xcf_prop * properties;
175 Uint32 hierarchy_file_offset;
188 Uint32 * level_file_offsets;
195 Uint32 * tile_file_offsets;
198 typedef unsigned char * xcf_tile;
200 typedef unsigned char * (* load_tile_type) (SDL_RWops *, Uint32, int, int, int);
203 /* See if an image is contained in a data source */
204 int IMG_isXCF(SDL_RWops *src)
212 start = SDL_RWtell(src);
214 if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
215 if (SDL_strncmp(magic, "gimp xcf ", 9) == 0) {
219 SDL_RWseek(src, start, RW_SEEK_SET);
223 static char * read_string (SDL_RWops * src) {
227 tmp = SDL_ReadBE32 (src);
229 data = (char *) SDL_malloc (sizeof (char) * tmp);
230 SDL_RWread (src, data, tmp, 1);
240 static Uint32 Swap32 (Uint32 v) {
242 ((v & 0x000000FF) << 16)
244 | ((v & 0x00FF0000) >> 16)
245 | ((v & 0xFF000000));
248 static void xcf_read_property (SDL_RWops * src, xcf_prop * prop) {
250 prop->id = SDL_ReadBE32 (src);
251 prop->length = SDL_ReadBE32 (src);
254 printf ("%.8X: %s: %d\n", SDL_RWtell (src), prop->id < 25 ? prop_names [prop->id] : "unknown", prop->length);
259 prop->data.colormap.num = SDL_ReadBE32 (src);
260 prop->data.colormap.cmap = (char *) SDL_malloc (sizeof (char) * prop->data.colormap.num * 3);
261 SDL_RWread (src, prop->data.colormap.cmap, prop->data.colormap.num*3, 1);
265 prop->data.offset.x = SDL_ReadBE32 (src);
266 prop->data.offset.y = SDL_ReadBE32 (src);
269 prop->data.opacity = SDL_ReadBE32 (src);
271 case PROP_COMPRESSION:
273 if (prop->length > sizeof(prop->data)) {
274 len = sizeof(prop->data);
278 SDL_RWread(src, &prop->data, len, 1);
281 prop->data.visible = SDL_ReadBE32 (src);
284 // SDL_RWread (src, &prop->data, prop->length, 1);
285 SDL_RWseek (src, prop->length, RW_SEEK_CUR);
289 static void free_xcf_header (xcf_header * h) {
291 SDL_free (h->cm_map);
292 if (h->layer_file_offsets)
293 SDL_free (h->layer_file_offsets);
297 static xcf_header * read_xcf_header (SDL_RWops * src) {
301 h = (xcf_header *) SDL_malloc (sizeof (xcf_header));
305 SDL_RWread (src, h->sign, 14, 1);
306 h->width = SDL_ReadBE32 (src);
307 h->height = SDL_ReadBE32 (src);
308 h->image_type = SDL_ReadBE32 (src);
310 h->properties = NULL;
311 h->layer_file_offsets = NULL;
312 h->compr = COMPR_NONE;
316 // Just read, don't save
318 xcf_read_property (src, &prop);
319 if (prop.id == PROP_COMPRESSION)
320 h->compr = (xcf_compr_type)prop.data.compression;
321 else if (prop.id == PROP_COLORMAP) {
322 // unused var: int i;
324 unsigned char *cm_map;
326 cm_num = prop.data.colormap.num;
327 cm_map = (unsigned char *) SDL_realloc(h->cm_map, sizeof (unsigned char) * 3 * cm_num);
331 SDL_memcpy (h->cm_map, prop.data.colormap.cmap, 3*sizeof (char)*h->cm_num);
333 SDL_free (prop.data.colormap.cmap);
340 } while (prop.id != PROP_END);
345 static void free_xcf_layer (xcf_layer * l) {
350 static xcf_layer * read_xcf_layer (SDL_RWops * src) {
354 l = (xcf_layer *) SDL_malloc (sizeof (xcf_layer));
355 l->width = SDL_ReadBE32 (src);
356 l->height = SDL_ReadBE32 (src);
357 l->layer_type = SDL_ReadBE32 (src);
359 l->name = read_string (src);
362 xcf_read_property (src, &prop);
363 if (prop.id == PROP_OFFSETS) {
364 l->offset_x = prop.data.offset.x;
365 l->offset_y = prop.data.offset.y;
366 } else if (prop.id == PROP_VISIBLE) {
367 l->visible = prop.data.visible ? 1 : 0;
369 } while (prop.id != PROP_END);
371 l->hierarchy_file_offset = SDL_ReadBE32 (src);
372 l->layer_mask_offset = SDL_ReadBE32 (src);
377 static void free_xcf_channel (xcf_channel * c) {
382 static xcf_channel * read_xcf_channel (SDL_RWops * src) {
386 l = (xcf_channel *) SDL_malloc (sizeof (xcf_channel));
387 l->width = SDL_ReadBE32 (src);
388 l->height = SDL_ReadBE32 (src);
390 l->name = read_string (src);
394 xcf_read_property (src, &prop);
397 l->opacity = prop.data.opacity << 24;
400 l->color = ((Uint32) prop.data.color[0] << 16)
401 | ((Uint32) prop.data.color[1] << 8)
402 | ((Uint32) prop.data.color[2]);
408 l->visible = prop.data.visible ? 1 : 0;
413 } while (prop.id != PROP_END);
415 l->hierarchy_file_offset = SDL_ReadBE32 (src);
420 static void free_xcf_hierarchy (xcf_hierarchy * h) {
421 SDL_free (h->level_file_offsets);
425 static xcf_hierarchy * read_xcf_hierarchy (SDL_RWops * src) {
429 h = (xcf_hierarchy *) SDL_malloc (sizeof (xcf_hierarchy));
430 h->width = SDL_ReadBE32 (src);
431 h->height = SDL_ReadBE32 (src);
432 h->bpp = SDL_ReadBE32 (src);
434 h->level_file_offsets = NULL;
437 h->level_file_offsets = (Uint32 *) SDL_realloc (h->level_file_offsets, sizeof (Uint32) * (i+1));
438 h->level_file_offsets [i] = SDL_ReadBE32 (src);
439 } while (h->level_file_offsets [i++]);
444 static void free_xcf_level (xcf_level * l) {
445 SDL_free (l->tile_file_offsets);
449 static xcf_level * read_xcf_level (SDL_RWops * src) {
453 l = (xcf_level *) SDL_malloc (sizeof (xcf_level));
454 l->width = SDL_ReadBE32 (src);
455 l->height = SDL_ReadBE32 (src);
457 l->tile_file_offsets = NULL;
460 l->tile_file_offsets = (Uint32 *) SDL_realloc (l->tile_file_offsets, sizeof (Uint32) * (i+1));
461 l->tile_file_offsets [i] = SDL_ReadBE32 (src);
462 } while (l->tile_file_offsets [i++]);
467 static void free_xcf_tile (unsigned char * t) {
471 static unsigned char * load_xcf_tile_none (SDL_RWops * src, Uint32 len, int bpp, int x, int y) {
472 unsigned char * load;
474 load = (unsigned char *) SDL_malloc (len); // expect this is okay
475 SDL_RWread (src, load, len, 1);
480 static unsigned char * load_xcf_tile_rle (SDL_RWops * src, Uint32 len, int bpp, int x, int y) {
481 unsigned char * load, * t, * data, * d;
483 int i, size, count, j, length;
486 if (len == 0) { /* probably bogus data. */
490 t = load = (unsigned char *) SDL_malloc (len);
491 reallen = SDL_RWread (src, t, 1, len);
493 data = (unsigned char *) SDL_calloc (1, x*y*bpp);
494 for (i = 0; i < bpp; i++) {
504 length = 255 - (length - 1);
506 length = (*t << 8) + t[1];
510 if (((size_t) (t - load) + length) >= len) {
511 break; /* bogus data */
512 } else if (length > size) {
513 break; /* bogus data */
519 while (length-- > 0) {
526 length = (*t << 8) + t[1];
530 if (((size_t) (t - load)) >= len) {
531 break; /* bogus data */
532 } else if (length > size) {
533 break; /* bogus data */
541 for (j = 0; j < length; j++) {
549 break; /* just drop out, untouched data initialized to zero. */
558 static Uint32 rgb2grey (Uint32 a) {
560 l = (Uint8)(0.2990 * ((a & 0x00FF0000) >> 16)
561 + 0.5870 * ((a & 0x0000FF00) >> 8)
562 + 0.1140 * ((a & 0x000000FF)));
564 return (l << 16) | (l << 8) | l;
567 static void create_channel_surface (SDL_Surface * surf, xcf_image_type itype, Uint32 color, Uint32 opacity) {
575 case IMAGE_GREYSCALE:
576 c = opacity | rgb2grey (color);
579 SDL_FillRect (surf, NULL, c);
583 do_layer_surface(SDL_Surface * surface, SDL_RWops * src, xcf_header * head, xcf_layer * layer, load_tile_type load_tile)
585 xcf_hierarchy *hierarchy;
592 Uint32 x, y, tx, ty, ox, oy;
595 SDL_RWseek(src, layer->hierarchy_file_offset, RW_SEEK_SET);
596 hierarchy = read_xcf_hierarchy(src);
598 if (hierarchy->bpp > 4) { /* unsupported. */
599 SDL_Log("Unknown Gimp image bpp (%u)\n", (unsigned int) hierarchy->bpp);
600 free_xcf_hierarchy(hierarchy);
604 if ((hierarchy->width > 20000) || (hierarchy->height > 20000)) { /* arbitrary limit to avoid integer overflow. */
605 SDL_Log("Gimp image too large (%ux%u)\n", (unsigned int) hierarchy->width, (unsigned int) hierarchy->height);
606 free_xcf_hierarchy(hierarchy);
611 for (i = 0; hierarchy->level_file_offsets[i]; i++) {
612 SDL_RWseek(src, hierarchy->level_file_offsets[i], RW_SEEK_SET);
613 level = read_xcf_level(src);
616 for (j = 0; level->tile_file_offsets[j]; j++) {
617 SDL_RWseek(src, level->tile_file_offsets[j], RW_SEEK_SET);
618 ox = tx + 64 > level->width ? level->width % 64 : 64;
619 oy = ty + 64 > level->height ? level->height % 64 : 64;
621 if (level->tile_file_offsets[j + 1]) {
622 tile = load_tile(src, level->tile_file_offsets[j + 1] - level->tile_file_offsets[j], hierarchy->bpp, ox, oy);
624 tile = load_tile(src, ox * oy * 6, hierarchy->bpp, ox, oy);
629 free_xcf_hierarchy(hierarchy);
632 free_xcf_level(level);
640 for (y = ty; y < ty + oy; y++) {
641 if ((ty >= surface->h) || ((tx+ox) > surface->w)) {
644 row = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch + tx * 4);
645 switch (hierarchy->bpp) {
647 for (x = tx; x < tx + ox; x++)
648 *row++ = Swap32(*p++);
651 for (x = tx; x < tx + ox; x++) {
653 *row |= ((Uint32)*p8++ << 16);
654 *row |= ((Uint32)*p8++ << 8);
655 *row |= ((Uint32)*p8++ << 0);
660 /* Indexed / Greyscale + Alpha */
661 switch (head->image_type) {
663 for (x = tx; x < tx + ox; x++) {
664 *row = ((Uint32)(head->cm_map[*p8 * 3]) << 16);
665 *row |= ((Uint32)(head->cm_map[*p8 * 3 + 1]) << 8);
666 *row |= ((Uint32)(head->cm_map[*p8++ * 3 + 2]) << 0);
667 *row |= ((Uint32)*p8++ << 24);
671 case IMAGE_GREYSCALE:
672 for (x = tx; x < tx + ox; x++) {
673 *row = ((Uint32)*p8 << 16);
674 *row |= ((Uint32)*p8 << 8);
675 *row |= ((Uint32)*p8++ << 0);
676 *row |= ((Uint32)*p8++ << 24);
681 SDL_Log("Unknown Gimp image type (%d)\n", head->image_type);
683 free_xcf_hierarchy(hierarchy);
686 free_xcf_level(level);
691 /* Indexed / Greyscale */
692 switch (head->image_type) {
694 for (x = tx; x < tx + ox; x++) {
696 | ((Uint32)(head->cm_map[*p8 * 3]) << 16)
697 | ((Uint32)(head->cm_map[*p8 * 3 + 1]) << 8)
698 | ((Uint32)(head->cm_map[*p8 * 3 + 2]) << 0);
702 case IMAGE_GREYSCALE:
703 for (x = tx; x < tx + ox; x++) {
705 | (((Uint32)(*p8)) << 16)
706 | (((Uint32)(*p8)) << 8)
707 | (((Uint32)(*p8)) << 0);
712 SDL_Log("Unknown Gimp image type (%d)\n", head->image_type);
716 free_xcf_level(level);
718 free_xcf_hierarchy(hierarchy);
727 if (tx >= level->width) {
731 if (ty >= level->height) {
735 free_xcf_level(level);
738 free_xcf_hierarchy(hierarchy);
743 SDL_Surface *IMG_LoadXCF_RW(SDL_RWops *src)
746 const char *error = NULL;
747 SDL_Surface *surface, *lays;
750 xcf_channel ** channel;
751 int chnls, i, offsets;
754 unsigned char * (* load_tile) (SDL_RWops *, Uint32, int, int, int);
757 /* The error message has been set in SDL_RWFromFile */
760 start = SDL_RWtell(src);
762 /* Initialize the data we will clean up when we're done */
765 head = read_xcf_header(src);
770 switch (head->compr) {
772 load_tile = load_xcf_tile_none;
775 load_tile = load_xcf_tile_rle;
778 SDL_Log("Unsupported Compression.\n");
779 free_xcf_header (head);
783 /* Create the surface of the appropriate type */
784 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, head->width, head->height, 32,
785 0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
787 if ( surface == NULL ) {
788 error = "Out of memory";
794 while ((offset = SDL_ReadBE32 (src))) {
795 head->layer_file_offsets = (Uint32 *) SDL_realloc (head->layer_file_offsets, sizeof (Uint32) * (offsets+1));
796 head->layer_file_offsets [offsets] = (Uint32)offset;
799 fp = SDL_RWtell (src);
801 lays = SDL_CreateRGBSurface(SDL_SWSURFACE, head->width, head->height, 32,
802 0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
804 if ( lays == NULL ) {
805 error = "Out of memory";
809 // Blit layers backwards, because Gimp saves them highest first
810 for (i = offsets; i > 0; i--) {
812 SDL_RWseek (src, head->layer_file_offsets [i-1], RW_SEEK_SET);
814 layer = read_xcf_layer (src);
815 do_layer_surface (lays, src, head, layer, load_tile);
819 rs.h = layer->height;
820 rd.x = layer->offset_x;
821 rd.y = layer->offset_y;
823 rd.h = layer->height;
826 SDL_BlitSurface (lays, &rs, surface, &rd);
827 free_xcf_layer (layer);
830 SDL_FreeSurface (lays);
832 SDL_RWseek (src, fp, RW_SEEK_SET);
837 while ((offset = SDL_ReadBE32 (src))) {
838 channel = (xcf_channel **) SDL_realloc (channel, sizeof (xcf_channel *) * (chnls+1));
839 fp = SDL_RWtell (src);
840 SDL_RWseek (src, offset, RW_SEEK_SET);
841 channel [chnls++] = (read_xcf_channel (src));
842 SDL_RWseek (src, fp, RW_SEEK_SET);
848 chs = SDL_CreateRGBSurface(SDL_SWSURFACE, head->width, head->height, 32,
849 0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
852 error = "Out of memory";
855 for (i = 0; i < chnls; i++) {
856 // printf ("CNLBLT %i\n", i);
857 if (!channel [i]->selection && channel [i]->visible) {
858 create_channel_surface (chs, (xcf_image_type)head->image_type, channel [i]->color, channel [i]->opacity);
859 SDL_BlitSurface (chs, NULL, surface, NULL);
861 free_xcf_channel (channel [i]);
864 SDL_FreeSurface (chs);
868 free_xcf_header (head);
870 SDL_RWseek(src, start, RW_SEEK_SET);
872 SDL_FreeSurface(surface);
875 IMG_SetError("%s", error);
883 /* See if an image is contained in a data source */
884 int IMG_isXCF(SDL_RWops *src)
889 /* Load a XCF type image from an SDL datasource */
890 SDL_Surface *IMG_LoadXCF_RW(SDL_RWops *src)
895 #endif /* LOAD_XCF */