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 }