IMG_pcx.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 10 Jun 2019 13:07:58 -0700
changeset 642 7453e79c8cdb
parent 638 e3e9d7430674
child 643 b920be2b3fc6
permissions -rw-r--r--
Fixed TALOS-2019-0841, heap buffer overlow exploit
Also fixed loading some images with incorrect palette location
     1 /*
     2   SDL_image:  An example image loading library for use with SDL
     3   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     4 
     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.
     8 
     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:
    12 
    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.
    20 */
    21 
    22 /*
    23  * PCX file reader:
    24  * Supports:
    25  *  1..4 bits/pixel in multiplanar format (1 bit/plane/pixel)
    26  *  8 bits/pixel in single-planar format (8 bits/plane/pixel)
    27  *  24 bits/pixel in 3-plane format (8 bits/plane/pixel)
    28  *
    29  * (The <8bpp formats are expanded to 8bpp surfaces)
    30  *
    31  * Doesn't support:
    32  *  single-planar packed-pixel formats other than 8bpp
    33  *  4-plane 32bpp format with a fourth "intensity" plane
    34  */
    35 
    36 #include "SDL_endian.h"
    37 
    38 #include "SDL_image.h"
    39 
    40 #ifdef LOAD_PCX
    41 
    42 struct PCXheader {
    43     Uint8 Manufacturer;
    44     Uint8 Version;
    45     Uint8 Encoding;
    46     Uint8 BitsPerPixel;
    47     Sint16 Xmin, Ymin, Xmax, Ymax;
    48     Sint16 HDpi, VDpi;
    49     Uint8 Colormap[48];
    50     Uint8 Reserved;
    51     Uint8 NPlanes;
    52     Sint16 BytesPerLine;
    53     Sint16 PaletteInfo;
    54     Sint16 HscreenSize;
    55     Sint16 VscreenSize;
    56     Uint8 Filler[54];
    57 };
    58 
    59 /* See if an image is contained in a data source */
    60 int IMG_isPCX(SDL_RWops *src)
    61 {
    62     Sint64 start;
    63     int is_PCX;
    64     const int ZSoft_Manufacturer = 10;
    65     const int PC_Paintbrush_Version = 5;
    66     const int PCX_Uncompressed_Encoding = 0;
    67     const int PCX_RunLength_Encoding = 1;
    68     struct PCXheader pcxh;
    69 
    70     if ( !src )
    71         return 0;
    72     start = SDL_RWtell(src);
    73     is_PCX = 0;
    74     if ( SDL_RWread(src, &pcxh, sizeof(pcxh), 1) == 1 ) {
    75         if ( (pcxh.Manufacturer == ZSoft_Manufacturer) &&
    76              (pcxh.Version == PC_Paintbrush_Version) &&
    77              (pcxh.Encoding == PCX_RunLength_Encoding ||
    78               pcxh.Encoding == PCX_Uncompressed_Encoding) ) {
    79             is_PCX = 1;
    80         }
    81     }
    82     SDL_RWseek(src, start, RW_SEEK_SET);
    83     return(is_PCX);
    84 }
    85 
    86 /* Load a PCX type image from an SDL datasource */
    87 SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src)
    88 {
    89     Sint64 start;
    90     struct PCXheader pcxh;
    91     Uint32 Rmask;
    92     Uint32 Gmask;
    93     Uint32 Bmask;
    94     Uint32 Amask;
    95     SDL_Surface *surface = NULL;
    96     int width, height;
    97     int y, bpl;
    98     Uint8 *row, *buf = NULL;
    99     char *error = NULL;
   100     int bits, src_bits;
   101     int count = 0;
   102     Uint8 ch;
   103 
   104     if ( !src ) {
   105         /* The error message has been set in SDL_RWFromFile */
   106         return NULL;
   107     }
   108     start = SDL_RWtell(src);
   109 
   110     if ( !SDL_RWread(src, &pcxh, sizeof(pcxh), 1) ) {
   111         error = "file truncated";
   112         goto done;
   113     }
   114     pcxh.Xmin = SDL_SwapLE16(pcxh.Xmin);
   115     pcxh.Ymin = SDL_SwapLE16(pcxh.Ymin);
   116     pcxh.Xmax = SDL_SwapLE16(pcxh.Xmax);
   117     pcxh.Ymax = SDL_SwapLE16(pcxh.Ymax);
   118     pcxh.BytesPerLine = SDL_SwapLE16(pcxh.BytesPerLine);
   119 
   120 #if 0
   121     printf("Manufacturer = %d\n", pcxh.Manufacturer);
   122     printf("Version = %d\n", pcxh.Version);
   123     printf("Encoding = %d\n", pcxh.Encoding);
   124     printf("BitsPerPixel = %d\n", pcxh.BitsPerPixel);
   125     printf("Xmin = %d, Ymin = %d, Xmax = %d, Ymax = %d\n", pcxh.Xmin, pcxh.Ymin, pcxh.Xmax, pcxh.Ymax);
   126     printf("HDpi = %d, VDpi = %d\n", pcxh.HDpi, pcxh.VDpi);
   127     printf("NPlanes = %d\n", pcxh.NPlanes);
   128     printf("BytesPerLine = %d\n", pcxh.BytesPerLine);
   129     printf("PaletteInfo = %d\n", pcxh.PaletteInfo);
   130     printf("HscreenSize = %d\n", pcxh.HscreenSize);
   131     printf("VscreenSize = %d\n", pcxh.VscreenSize);
   132 #endif
   133 
   134     /* Create the surface of the appropriate type */
   135     width = (pcxh.Xmax - pcxh.Xmin) + 1;
   136     height = (pcxh.Ymax - pcxh.Ymin) + 1;
   137     Rmask = Gmask = Bmask = Amask = 0;
   138     src_bits = pcxh.BitsPerPixel * pcxh.NPlanes;
   139     if((pcxh.BitsPerPixel == 1 && pcxh.NPlanes >= 1 && pcxh.NPlanes <= 4)
   140        || (pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 1)) {
   141         bits = 8;
   142     } else if(pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 3) {
   143         bits = 24;
   144 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   145             Rmask = 0x000000FF;
   146             Gmask = 0x0000FF00;
   147             Bmask = 0x00FF0000;
   148 #else
   149             Rmask = 0xFF0000;
   150             Gmask = 0x00FF00;
   151             Bmask = 0x0000FF;
   152 #endif
   153     } else {
   154         error = "unsupported PCX format";
   155         goto done;
   156     }
   157     surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
   158                    bits, Rmask, Gmask, Bmask, Amask);
   159     if ( surface == NULL )
   160         goto done;
   161 
   162     bpl = pcxh.NPlanes * pcxh.BytesPerLine;
   163     if ( bpl > surface->pitch ) {
   164         error = "bytes per line is too large (corrupt?)";
   165         goto done;
   166     }
   167     buf = (Uint8 *)SDL_calloc(surface->pitch, 1);
   168     row = (Uint8 *)surface->pixels;
   169     for ( y=0; y<surface->h; ++y ) {
   170         /* decode a scan line to a temporary buffer first */
   171         int i;
   172         Uint8 *dst = buf;
   173         if ( pcxh.Encoding == 0 ) {
   174             if ( !SDL_RWread(src, dst, bpl, 1) ) {
   175                 error = "file truncated";
   176                 goto done;
   177             }
   178         } else {
   179             for ( i = 0; i < bpl; i++ ) {
   180                 if ( !count ) {
   181                     if ( !SDL_RWread(src, &ch, 1, 1) ) {
   182                         error = "file truncated";
   183                         goto done;
   184                     }
   185                     if ( ch < 0xc0 ) {
   186                         count = 1;
   187                     } else {
   188                         count = ch - 0xc0;
   189                         if( !SDL_RWread(src, &ch, 1, 1) ) {
   190                             error = "file truncated";
   191                             goto done;
   192                         }
   193                     }
   194                 }
   195                 dst[i] = ch;
   196                 count--;
   197             }
   198         }
   199 
   200         if ( src_bits <= 4 ) {
   201             /* expand planes to 1 byte/pixel */
   202             Uint8 *innerSrc = buf;
   203             int plane;
   204             for ( plane = 0; plane < pcxh.NPlanes; plane++ ) {
   205                 int j, k, x = 0;
   206                 for( j = 0; j < pcxh.BytesPerLine; j++ ) {
   207                     Uint8 byte = *innerSrc++;
   208                     for( k = 7; k >= 0; k-- ) {
   209                         unsigned bit = (byte >> k) & 1;
   210                         /* skip padding bits */
   211                         if (j * 8 + k >= width)
   212                             continue;
   213                         row[x++] |= bit << plane;
   214                     }
   215                 }
   216             }
   217         } else if ( src_bits == 24 ) {
   218             /* de-interlace planes */
   219             Uint8 *innerSrc = buf;
   220             int plane;
   221             for ( plane = 0; plane < pcxh.NPlanes; plane++ ) {
   222                 int x;
   223                 dst = row + plane;
   224                 for ( x = 0; x < width; x++ ) {
   225                     if ( dst >= row+surface->pitch ) {
   226                         error = "decoding out of bounds (corrupt?)";
   227                         goto done;
   228                     }
   229                     *dst = *innerSrc++;
   230                     dst += pcxh.NPlanes;
   231                 }
   232             }
   233         } else {
   234             SDL_memcpy(row, buf, bpl);
   235         }
   236 
   237         row += surface->pitch;
   238     }
   239 
   240     if ( bits == 8 ) {
   241         SDL_Color *colors = surface->format->palette->colors;
   242         int nc = 1 << src_bits;
   243         int i;
   244 
   245         surface->format->palette->ncolors = nc;
   246         if ( src_bits == 8 ) {
   247             Uint8 ch;
   248             /* look for a 256-colour palette */
   249             do {
   250                 if ( !SDL_RWread(src, &ch, 1, 1) ) {
   251                     /* Couldn't find the palette, try the end of the file */
   252                     SDL_RWseek(src, -768, RW_SEEK_END);
   253                     break;
   254                 }
   255             } while ( ch != 12 );
   256 
   257             for ( i = 0; i < 256; i++ ) {
   258                 SDL_RWread(src, &colors[i].r, 1, 1);
   259                 SDL_RWread(src, &colors[i].g, 1, 1);
   260                 SDL_RWread(src, &colors[i].b, 1, 1);
   261             }
   262         } else {
   263             for ( i = 0; i < nc; i++ ) {
   264                 colors[i].r = pcxh.Colormap[i * 3];
   265                 colors[i].g = pcxh.Colormap[i * 3 + 1];
   266                 colors[i].b = pcxh.Colormap[i * 3 + 2];
   267             }
   268         }
   269     }
   270 
   271 done:
   272     SDL_free(buf);
   273     if ( error ) {
   274         SDL_RWseek(src, start, RW_SEEK_SET);
   275         if ( surface ) {
   276             SDL_FreeSurface(surface);
   277             surface = NULL;
   278         }
   279         IMG_SetError("%s", error);
   280     }
   281     return(surface);
   282 }
   283 
   284 #else
   285 
   286 /* See if an image is contained in a data source */
   287 int IMG_isPCX(SDL_RWops *src)
   288 {
   289     return(0);
   290 }
   291 
   292 /* Load a PCX type image from an SDL datasource */
   293 SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src)
   294 {
   295     return(NULL);
   296 }
   297 
   298 #endif /* LOAD_PCX */