File indexing completed on 2025-03-09 04:09:54
0001 /* Generic functions for reading XCF files 0002 * 0003 * This file was written by Henning Makholm <henning@makholm.net> 0004 * It is hereby in the public domain. 0005 * 0006 * In jurisdictions that do not recognise grants of copyright to the 0007 * public domain: I, the author and (presumably, in those jurisdictions) 0008 * copyright holder, hereby permit anyone to distribute and use this code, 0009 * in source code or binary form, with or without modifications. This 0010 * permission is world-wide and irrevocable. 0011 * 0012 * Of course, I will not be liable for any errors or shortcomings in the 0013 * code, since I give it away without asking any compenstations. 0014 * 0015 * If you use or distribute this code, I would appreciate receiving 0016 * credit for writing it, in whichever way you find proper and customary. 0017 */ 0018 0019 #include "xcftools.h" 0020 #include <string.h> 0021 #include <errno.h> 0022 #include <limits.h> 0023 #ifdef HAVE_ICONV 0024 # include <iconv.h> 0025 #elif !defined(ICONV_CONST) 0026 # define ICONV_CONST const 0027 #endif 0028 0029 uint8_t *xcf_file = 0 ; 0030 size_t xcf_length ; 0031 int use_utf8 = 0 ; 0032 0033 int 0034 xcfOffset(uint32_t addr,int spaceafter, uint32_t* apparent) 0035 { 0036 if (!apparent) { 0037 return XCF_ERROR; 0038 } 0039 if(xcfCheckspace(addr,4,"(xcfOffset)") != XCF_OK) { 0040 return XCF_ERROR; 0041 } 0042 *apparent = xcfL(addr); 0043 if (xcfCheckspace(*apparent,spaceafter, 0044 "Too large offset (%" PRIX32 ") at position %" PRIX32, 0045 *apparent,addr) != XCF_OK) { 0046 return XCF_ERROR; 0047 } 0048 return XCF_OK; 0049 } 0050 0051 int 0052 xcfNextprop(uint32_t *master,uint32_t *body, PropType *typeOut) 0053 { 0054 int response; 0055 0056 if (typeOut == 0) { 0057 return XCF_ERROR; 0058 } 0059 0060 uint32_t ptr, length, total, minlength ; 0061 PropType type ; 0062 ptr = *master ; 0063 if ((response = xcfCheckspace(ptr,8,"(property header)")) != XCF_OK) { 0064 return XCF_ERROR; 0065 } 0066 type = xcfL(ptr); 0067 length = xcfL(ptr+4); 0068 *body = ptr+8 ; 0069 0070 switch(type) { 0071 case PROP_COLORMAP: 0072 { 0073 uint32_t ncolors ; 0074 if ((response = xcfCheckspace(ptr+8,4,"(colormap length)")) != XCF_OK) { 0075 return XCF_ERROR; 0076 } 0077 ncolors = xcfL(ptr+8) ; 0078 if( ncolors > 256 ) { 0079 FatalBadXCF("Colormap has %" PRIu32 " entries",ncolors); 0080 return XCF_ERROR; 0081 } 0082 0083 /* Surprise! Some older version of the Gimp computed the wrong length 0084 * word, and the _reader_ always just reads three bytes per color 0085 * and ignores the length tag! Duplicate this so we too can read 0086 * the buggy XCF files. 0087 */ 0088 length = minlength = 4+3*ncolors; 0089 break; 0090 } 0091 case PROP_COMPRESSION: minlength = 1; break; 0092 case PROP_OPACITY: minlength = 4; break; 0093 case PROP_APPLY_MASK: minlength = 4; break; 0094 case PROP_OFFSETS: minlength = 8; break; 0095 case PROP_MODE: minlength = 4; break; 0096 default: minlength = 0; break; 0097 } 0098 if( length < minlength ) { 0099 FatalBadXCF("Short %s property at %" PRIX32 " (%" PRIu32 "<%" PRIu32 ")", 0100 showPropType(type),ptr,length,minlength); 0101 return XCF_ERROR; 0102 } 0103 *master = ptr+8+length ; 0104 total = 8 + length + (type != PROP_END ? 8 : 0) ; 0105 if( total < length ) { /* Check overwrap */ 0106 FatalBadXCF("Overlong property at %" PRIX32, ptr); 0107 return XCF_ERROR; 0108 } 0109 if((response = xcfCheckspace(ptr,total,"Overlong property at %" PRIX32,ptr)) != 0) { 0110 return XCF_ERROR; 0111 } 0112 *typeOut = type; 0113 return XCF_OK; 0114 } 0115 0116 const char* 0117 xcfString(uint32_t ptr,uint32_t *after) 0118 { 0119 uint32_t length ; 0120 unsigned i ; 0121 ICONV_CONST char *utf8master ; 0122 0123 if (xcfCheckspace(ptr,4,"(string length)") != XCF_OK) { 0124 return XCF_PTR_EMPTY; 0125 } 0126 length = xcfL(ptr) ; 0127 ptr += 4 ; 0128 if (xcfCheckspace(ptr,length,"(string)") != XCF_OK) { 0129 return XCF_PTR_EMPTY; 0130 } 0131 utf8master = (ICONV_CONST char*)(xcf_file+ptr) ; 0132 if( after ) *after = ptr + length ; 0133 if( length == 0 || utf8master[length-1] != 0 ) { 0134 FatalBadXCF("String at %" PRIX32 " not zero-terminated",ptr-4); 0135 return XCF_PTR_EMPTY; 0136 } 0137 length-- ; 0138 0139 if( use_utf8 ) return utf8master ; 0140 0141 /* We assume that the local character set includes ASCII... 0142 * Check if conversion is needed at all 0143 */ 0144 for( i=0 ; ; i++ ) { 0145 if( i == length ) 0146 return utf8master ; /* Only ASCII after all */ 0147 if( utf8master[i] == 0 ) { 0148 FatalBadXCF("String at %" PRIX32 " has embedded zeroes",ptr-4); 0149 return XCF_PTR_EMPTY; 0150 } 0151 if( (int8_t) utf8master[i] < 0 ) 0152 break ; 0153 } 0154 #ifdef HAVE_ICONV 0155 { 0156 size_t targetsize = length+1 ; 0157 int sloppy_translation = 0 ; 0158 iconv_t cd = iconv_open("//TRANSLIT","UTF-8"); 0159 if( cd == (iconv_t) -1 ) { 0160 cd = iconv_open("","UTF-8"); 0161 sloppy_translation = 1 ; 0162 } 0163 if( cd == (iconv_t) -1 ) 0164 iconv_close(cd) ; /* Give up; perhaps iconv doesn't know UTF-8 */ 0165 else 0166 while(1) { 0167 char *buffer = xcfmalloc(targetsize) ; 0168 ICONV_CONST char *inbuf = utf8master ; 0169 char *outbuf = buffer ; 0170 size_t incount = length ; 0171 size_t outcount = targetsize ; 0172 while(1) { /* Loop for systems without //ICONV support */ 0173 size_t result = iconv(cd,&inbuf,&incount,&outbuf,&outcount) ; 0174 if( result == (size_t)-1 && errno == EILSEQ && 0175 sloppy_translation && outcount > 0 ) { 0176 *outbuf++ = '?' ; 0177 outcount-- ; 0178 while( (int8_t)*inbuf < 0 ) inbuf++, incount-- ; 0179 continue ; 0180 } 0181 if( result != (size_t)-1 ) { 0182 if( outcount == 0 ) 0183 errno = E2BIG ; 0184 else { 0185 *outbuf = 0 ; 0186 iconv_close(cd) ; 0187 return buffer ; 0188 } 0189 } 0190 break ; 0191 } 0192 if( errno == EILSEQ || errno == EINVAL ) { 0193 FatalBadXCF("Bad UTF-8 encoding '%s' at %" PRIXPTR, 0194 inbuf,(uintptr_t)((inbuf-utf8master)+ptr)); 0195 return XCF_PTR_EMPTY; 0196 } 0197 if( errno == E2BIG ) { 0198 targetsize += 1+incount ; 0199 xcffree(buffer) ; 0200 continue ; 0201 } 0202 FatalUnexpected("!iconv on layer name at %" PRIX32, ptr); 0203 return XCF_PTR_EMPTY: 0204 } 0205 } 0206 #endif 0207 { 0208 static int warned = 0 ; 0209 if( !warned ) { 0210 fprintf(stderr,_("Warning: one or more layer names could not be\n" 0211 " translated to the local character set.\n")); 0212 warned = 1 ; 0213 } 0214 } 0215 return utf8master ; 0216 } 0217 0218 /* ****************************************************************** */ 0219 0220 int computeDimensions(struct tileDimensions *d) 0221 { 0222 // [ CVE-2019-5086 and CVE-2019-5087 ] 0223 // This part of code is the check to prevent integer overflow, see CVE-2019-5086 and CVE-2019-5087 0224 0225 if ((d->c.l + d->width) * 4 > INT_MAX) { 0226 FatalBadXCF(("Width is too large (%d)! Stopping execution...\n"), (d->c.l + d->width)); 0227 return XCF_ERROR; 0228 } 0229 0230 if ((d->c.t + d->height) * 4 > INT_MAX) { 0231 FatalBadXCF(("Height is too large (%d)! Stopping execution...\n"), (d->c.t + d->height)); 0232 return XCF_ERROR; 0233 } 0234 // [ CVE-2019-5086 and CVE-2019-5087 ] 0235 0236 d->c.r = d->c.l + d->width ; 0237 d->c.b = d->c.t + d->height ; 0238 d->tilesx = (d->width+TILE_WIDTH-1)/TILE_WIDTH ; 0239 d->tilesy = (d->height+TILE_HEIGHT-1)/TILE_HEIGHT ; 0240 d->ntiles = d->tilesx * d->tilesy ; 0241 0242 return XCF_OK; 0243 } 0244 0245 struct xcfImage XCF ; 0246 0247 int 0248 getBasicXcfInfo(void) 0249 { 0250 uint32_t ptr, data, layerfile ; 0251 PropType type ; 0252 int i, j ; 0253 0254 int errorStatus; 0255 uint32_t ptrout; 0256 0257 if (xcfCheckspace(0,14+7*4,"(very short)") != XCF_OK) { 0258 return XCF_ERROR; 0259 } 0260 0261 if( strcmp((char*)xcf_file,"gimp xcf file") == 0 ) 0262 XCF.version = 0 ; 0263 else if( xcf_file[13] == 0 && 0264 sscanf((char*)xcf_file,"gimp xcf v%d",&XCF.version) == 1 ) 0265 ; 0266 else { 0267 FatalBadXCF(_("Not an XCF file at all (magic not recognized)")); 0268 return XCF_ERROR; 0269 } 0270 0271 if (XCF.version < 0 || XCF.version > 2) { 0272 fprintf(stderr, _("Warning: XCF version %d not supported (trying anyway...)\n"), XCF.version); 0273 } 0274 0275 XCF.compression = COMPRESS_NONE ; 0276 XCF.colormapptr = 0 ; 0277 0278 ptr = 14 ; 0279 XCF.width = xcfL(ptr); ptr += 4 ; 0280 XCF.height = xcfL(ptr); ptr += 4 ; 0281 XCF.type = xcfL(ptr); ptr += 4 ; 0282 while( (errorStatus = xcfNextprop(&ptr,&data, &type)) != XCF_ERROR && type != PROP_END ) { 0283 if (errorStatus != XCF_OK) { 0284 return XCF_ERROR; 0285 } 0286 0287 switch(type) { 0288 case PROP_COLORMAP: 0289 XCF.colormapptr = data ; 0290 break ; 0291 case PROP_COMPRESSION: 0292 XCF.compression = xcf_file[data] ; 0293 break ; 0294 default: 0295 /* Ignore unknown properties */ 0296 break ; 0297 } 0298 } 0299 0300 layerfile = ptr ; 0301 XCF.numLayers = 0; 0302 while (1) { 0303 errorStatus = xcfOffset(ptr,8*4, &ptrout); 0304 if (errorStatus != XCF_OK) { 0305 return XCF_ERROR; 0306 } 0307 if (!ptrout) { 0308 break; 0309 } 0310 XCF.numLayers++; 0311 ptr+=4; 0312 } 0313 XCF.layers = xcfmalloc(XCF.numLayers * sizeof(struct xcfLayer)) ; 0314 for( i = 0 ; i < XCF.numLayers ; i++ ) { 0315 struct xcfLayer *L = XCF.layers + i ; 0316 ptr = xcfL(layerfile+4*(XCF.numLayers-1-i)) ; 0317 0318 L->mode = GIMP_NORMAL_MODE ; 0319 L->opacity = 255 ; 0320 L->isVisible = 1 ; 0321 L->hasMask = 0 ; 0322 L->dim.width = xcfL(ptr); ptr+=4 ; 0323 L->dim.height = xcfL(ptr); ptr+=4 ; 0324 L->type = xcfL(ptr); ptr+=4 ; 0325 L->name = xcfString(ptr,&ptr); 0326 if (L->name == XCF_PTR_EMPTY) { 0327 return XCF_ERROR; 0328 } 0329 L->propptr = ptr ; 0330 0331 L->isGroup = 0; 0332 L->pathLength = 0; 0333 L->path = NULL; 0334 0335 while( (errorStatus = xcfNextprop(&ptr,&data, &type)) != XCF_ERROR && type != PROP_END ) { 0336 if (errorStatus != XCF_OK) { 0337 xcffree(XCF.layers); 0338 XCF.layers = XCF_PTR_EMPTY; 0339 return XCF_ERROR; 0340 } 0341 switch(type) { 0342 case PROP_OPACITY: 0343 L->opacity = xcfL(data); 0344 if( L->opacity > 255 ) 0345 L->opacity = 255 ; 0346 break ; 0347 case PROP_VISIBLE: 0348 L->isVisible = xcfL(data) != 0 ; 0349 break ; 0350 case PROP_APPLY_MASK: 0351 L->hasMask = xcfL(data) != 0 ; 0352 break ; 0353 case PROP_OFFSETS: 0354 L->dim.c.l = (int32_t)(xcfL(data )) ; 0355 L->dim.c.t = (int32_t)(xcfL(data+4)) ; 0356 break ; 0357 case PROP_MODE: 0358 L->mode = xcfL(data); 0359 break ; 0360 case PROP_GROUP_ITEM: 0361 L->isGroup = 1 ; 0362 break; 0363 case PROP_ITEM_PATH: 0364 L->pathLength = (ptr - data - 2) / 4 ; 0365 0366 if ( L->pathLength != 0 ) { 0367 0368 L->path = xcfmalloc( L->pathLength * sizeof(unsigned) ) ; 0369 0370 for ( j = 0; j!=L->pathLength; j++ ) 0371 *(L->path + j) = (unsigned)xcfL(data + 4 * j); 0372 } 0373 break; 0374 default: 0375 /* Ignore unknown properties */ 0376 break ; 0377 } 0378 } 0379 if ((errorStatus = xcfCheckspace(ptr,8,"(end of layer %s)",L->name)) != XCF_OK) { 0380 xcffree(XCF.layers); 0381 XCF.layers = XCF_PTR_EMPTY; 0382 return XCF_ERROR; 0383 } 0384 L->pixels.tileptrs = 0 ; 0385 if (xcfOffset(ptr , 4*4, &(L->pixels.hierarchy)) != XCF_OK) { 0386 xcffree(XCF.layers); 0387 XCF.layers = XCF_PTR_EMPTY; 0388 return XCF_ERROR; 0389 } 0390 L->mask.tileptrs = 0 ; 0391 if (xcfOffset(ptr+4, 4*4, &(L->mask.hierarchy)) != XCF_OK) { 0392 xcffree(XCF.layers); 0393 XCF.layers = XCF_PTR_EMPTY; 0394 return XCF_ERROR; 0395 } 0396 0397 if (computeDimensions(&L->dim) != XCF_OK) { 0398 return XCF_ERROR; 0399 } 0400 } 0401 return XCF_OK; 0402 }