From 48c54f3881fe917c020688ecb60875926fe51527 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 22 Aug 2026 09:15:03 -0700
Subject: [PATCH] Added additional checking when loading XCF files
Fixes https://github.com/libsdl-org/SDL_image/issues/757
---
src/IMG_xcf.c | 55 ++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 46 insertions(+), 9 deletions(-)
diff --git a/src/IMG_xcf.c b/src/IMG_xcf.c
index c3472bde3..740973faa 100644
--- a/src/IMG_xcf.c
+++ b/src/IMG_xcf.c
@@ -34,6 +34,7 @@
#ifndef SDL_SIZE_MAX
#define SDL_SIZE_MAX ((size_t)-1)
#endif
+#define MAX_XCF_SIZE 20000 /* arbitrary limit to avoid integer overflow. */
#ifdef DEBUG
static char prop_names [][30] = {
@@ -368,6 +369,13 @@ static xcf_header *read_xcf_header(SDL_IOStream *src)
free_xcf_header(h);
return NULL;
}
+
+ if ((h->width > MAX_XCF_SIZE) || (h->height > MAX_XCF_SIZE)) {
+ SDL_SetError("Gimp image too large (%ux%u)", (unsigned int)h->width, (unsigned int)h->height);
+ free_xcf_header(h);
+ return NULL;
+ }
+
if (h->sign[9] == 'v' && h->sign[10] >= '0' && h->sign[10] <= '9' && h->sign[11] >= '0' && h->sign[11] <= '9' && h->sign[12] >= '0' && h->sign[12] <= '9')
h->file_version = (h->sign[10] - '0') * 100 + (h->sign[11] - '0') * 10 + (h->sign[12] - '0');
else
@@ -446,6 +454,12 @@ static xcf_layer *read_xcf_layer(SDL_IOStream *src, const xcf_header *h)
return NULL;
}
+ if ((l->width > MAX_XCF_SIZE) || (l->height > MAX_XCF_SIZE)) {
+ SDL_SetError("Gimp layer too large (%ux%u)", (unsigned int)l->width, (unsigned int)l->height);
+ free_xcf_layer(l);
+ return NULL;
+ }
+
l->name = read_string(src);
#ifdef DEBUG
SDL_Log("layer (%d,%d) type=%u '%s'\n", l->width, l->height, l->layer_type, l->name);
@@ -495,6 +509,12 @@ static xcf_channel *read_xcf_channel(SDL_IOStream *src, const xcf_header *h)
return NULL;
}
+ if ((l->width > MAX_XCF_SIZE) || (l->height > MAX_XCF_SIZE)) {
+ SDL_SetError("Gimp channel too large (%ux%u)", (unsigned int)l->width, (unsigned int)l->height);
+ free_xcf_channel(l);
+ return NULL;
+ }
+
l->name = read_string(src);
#ifdef DEBUG
SDL_Log("channel (%u,%u) '%s'\n", l->width, l->height, l->name);
@@ -555,6 +575,12 @@ static xcf_hierarchy *read_xcf_hierarchy(SDL_IOStream *src, const xcf_header *he
return NULL;
}
+ if ((h->width > MAX_XCF_SIZE) || (h->height > MAX_XCF_SIZE)) {
+ SDL_SetError("Gimp image too large (%ux%u)", (unsigned int)h->width, (unsigned int)h->height);
+ free_xcf_hierarchy(h);
+ return NULL;
+ }
+
i = 0;
do {
h->level_file_offsets = (Uint64 *)SDL_realloc(h->level_file_offsets, sizeof(*h->level_file_offsets) * (i+1));
@@ -581,15 +607,26 @@ static xcf_level *read_xcf_level(SDL_IOStream *src, const xcf_header *h)
if (!l) {
return NULL;
}
- if (!SDL_ReadU32BE (src, &l->width) ||
- !SDL_ReadU32BE (src, &l->height)) {
+ if (!SDL_ReadU32BE(src, &l->width) ||
+ !SDL_ReadU32BE(src, &l->height)) {
+ free_xcf_level(l);
+ return NULL;
+ }
+
+ if ((l->width > MAX_XCF_SIZE) || (l->height > MAX_XCF_SIZE)) {
+ SDL_SetError("Gimp level too large (%ux%u)", (unsigned int)l->width, (unsigned int)l->height);
free_xcf_level(l);
return NULL;
}
i = 0;
do {
- l->tile_file_offsets = (Uint64 *)SDL_realloc(l->tile_file_offsets, sizeof(*l->tile_file_offsets) * (i+1));
+ Uint64 *tile_file_offsets = (Uint64 *)SDL_realloc(l->tile_file_offsets, sizeof(*l->tile_file_offsets) * (i+1));
+ if (!tile_file_offsets) {
+ free_xcf_level(l);
+ return NULL;
+ }
+ l->tile_file_offsets = tile_file_offsets;
l->tile_file_offsets[i] = read_offset(src, h);
} while (l->tile_file_offsets[i++]);
@@ -767,19 +804,19 @@ do_layer_surface(SDL_Surface *surface, SDL_IOStream *src, xcf_header *head, xcf_
return 1;
}
- if ((hierarchy->width > 20000) || (hierarchy->height > 20000)) { /* arbitrary limit to avoid integer overflow. */
- SDL_SetError("Gimp image too large (%ux%u)", (unsigned int) hierarchy->width, (unsigned int) hierarchy->height);
- free_xcf_hierarchy(hierarchy);
- return 1;
- }
-
level = NULL;
for (i = 0; hierarchy->level_file_offsets[i]; i++) {
if (SDL_SeekIO(src, hierarchy->level_file_offsets[i], SDL_IO_SEEK_SET) < 0)
break;
+
if (i > 0) /* skip level except the 1st one, just like GIMP does */
continue;
+
level = read_xcf_level(src, head);
+ if (!level) {
+ free_xcf_hierarchy(hierarchy);
+ return 1;
+ }
ty = tx = 0;
for (j = 0; level->tile_file_offsets[j]; j++) {