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