IMG_pcx.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 06 Aug 2003 20:45:36 +0000
changeset 91 26b6d4a09dfd
parent 53 96b084473b47
child 95 7fcb46931afc
permissions -rw-r--r--
Date: Mon, 04 Aug 2003 21:50:52 +0200
From: Holger Schemel
Subject: [SDL] SDL_image PCX loader crashes (patch included)

I've just discovered and fixed a problem with the current
SDL_image 1.2.3 which can crash on every PCX file with an
image width not being a multiple of 8 pixels if stored in
bitplane format (with 4 or less bitplanes).

In this case, the PCX loader happily writes beyond the
allocated bitmap data buffer of the image surface in each
line, which ends with a Segmentation Fault in the last
line if you have bad luck and your allocated memory page
ends near the last byte of the bitmap data. (In most cases
you may have luck, which made this crash very difficult to
track down in my case.)

As this error is not fixed in the CVS yet, I've attached a
patch to fix this bug (three lines in "IMG_pcx.c"). The fix
prevents reading/writing the memory beyond the current line
of the image being loaded.

This was a particularly nasty error for me, because all my
image files have a width which is a multiple of 8 or even
16/32, so the error never showed up, but some people creating
custom artwork for my game Rocks'n'Diamonds created images
less "perfect" ;-) and the game crashed on those image files
only from time to time.
slouken@0
     1
/*
slouken@53
     2
    SDL_image:  An example image loading library for use with SDL
slouken@53
     3
    Copyright (C) 1999, 2000, 2001  Sam Lantinga
slouken@0
     4
slouken@0
     5
    This library is free software; you can redistribute it and/or
slouken@0
     6
    modify it under the terms of the GNU Library General Public
slouken@0
     7
    License as published by the Free Software Foundation; either
slouken@0
     8
    version 2 of the License, or (at your option) any later version.
slouken@0
     9
slouken@0
    10
    This library is distributed in the hope that it will be useful,
slouken@0
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@0
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@0
    13
    Library General Public License for more details.
slouken@0
    14
slouken@0
    15
    You should have received a copy of the GNU Library General Public
slouken@0
    16
    License along with this library; if not, write to the Free
slouken@0
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@0
    18
slouken@0
    19
    Sam Lantinga
slouken@53
    20
    slouken@libsdl.org
slouken@0
    21
*/
slouken@0
    22
slouken@53
    23
/* $Id$ */
slouken@53
    24
slouken@20
    25
/*
slouken@20
    26
 * PCX file reader:
slouken@20
    27
 * Supports:
slouken@20
    28
 *  1..4 bits/pixel in multiplanar format (1 bit/plane/pixel)
slouken@20
    29
 *  8 bits/pixel in single-planar format (8 bits/plane/pixel)
slouken@20
    30
 *  24 bits/pixel in 3-plane format (8 bits/plane/pixel)
slouken@20
    31
 *
slouken@24
    32
 * (The <8bpp formats are expanded to 8bpp surfaces)
slouken@24
    33
 *
slouken@20
    34
 * Doesn't support:
slouken@20
    35
 *  single-planar packed-pixel formats other than 8bpp
slouken@20
    36
 *  4-plane 32bpp format with a fourth "intensity" plane
slouken@20
    37
 */
slouken@0
    38
#include <stdio.h>
slouken@20
    39
#include <stdlib.h>
slouken@0
    40
slouken@0
    41
#include "SDL_endian.h"
slouken@0
    42
slouken@0
    43
#include "SDL_image.h"
slouken@0
    44
slouken@0
    45
#ifdef LOAD_PCX
slouken@0
    46
slouken@0
    47
struct PCXheader {
slouken@0
    48
	Uint8 Manufacturer;
slouken@0
    49
	Uint8 Version;
slouken@0
    50
	Uint8 Encoding;
slouken@0
    51
	Uint8 BitsPerPixel;
slouken@0
    52
	Sint16 Xmin, Ymin, Xmax, Ymax;
slouken@0
    53
	Sint16 HDpi, VDpi;
slouken@0
    54
	Uint8 Colormap[48];
slouken@0
    55
	Uint8 Reserved;
slouken@0
    56
	Uint8 NPlanes;
slouken@0
    57
	Sint16 BytesPerLine;
slouken@0
    58
	Sint16 PaletteInfo;
slouken@0
    59
	Sint16 HscreenSize;
slouken@0
    60
	Sint16 VscreenSize;
slouken@0
    61
	Uint8 Filler[54];
slouken@0
    62
};
slouken@0
    63
slouken@0
    64
/* See if an image is contained in a data source */
slouken@0
    65
int IMG_isPCX(SDL_RWops *src)
slouken@0
    66
{
slouken@0
    67
	int is_PCX;
slouken@0
    68
	const int ZSoft_Manufacturer = 10;
slouken@0
    69
	const int PC_Paintbrush_Version = 5;
slouken@0
    70
	const int PCX_RunLength_Encoding = 1;
slouken@0
    71
	struct PCXheader pcxh;
slouken@0
    72
slouken@0
    73
	is_PCX = 0;
slouken@0
    74
	if ( SDL_RWread(src, &pcxh, sizeof(pcxh), 1) == 1 ) {
slouken@0
    75
		if ( (pcxh.Manufacturer == ZSoft_Manufacturer) &&
slouken@0
    76
		     (pcxh.Version == PC_Paintbrush_Version) &&
slouken@0
    77
		     (pcxh.Encoding == PCX_RunLength_Encoding) ) {
slouken@0
    78
			is_PCX = 1;
slouken@0
    79
		}
slouken@0
    80
	}
slouken@0
    81
	return(is_PCX);
slouken@0
    82
}
slouken@0
    83
slouken@0
    84
/* Load a PCX type image from an SDL datasource */
slouken@0
    85
SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src)
slouken@0
    86
{
slouken@0
    87
	struct PCXheader pcxh;
slouken@0
    88
	Uint32 Rmask;
slouken@0
    89
	Uint32 Gmask;
slouken@0
    90
	Uint32 Bmask;
slouken@0
    91
	Uint32 Amask;
slouken@20
    92
	SDL_Surface *surface = NULL;
slouken@0
    93
	int width, height;
slouken@20
    94
	int y, bpl;
slouken@20
    95
	Uint8 *row, *buf = NULL;
slouken@20
    96
	char *error = NULL;
slouken@20
    97
	int bits, src_bits;
slouken@0
    98
slouken@0
    99
	if ( ! src ) {
slouken@0
   100
		goto done;
slouken@0
   101
	}
slouken@0
   102
slouken@0
   103
	if ( ! SDL_RWread(src, &pcxh, sizeof(pcxh), 1) ) {
slouken@20
   104
		error = "file truncated";
slouken@0
   105
		goto done;
slouken@0
   106
	}
slouken@0
   107
	pcxh.Xmin = SDL_SwapLE16(pcxh.Xmin);
slouken@0
   108
	pcxh.Ymin = SDL_SwapLE16(pcxh.Ymin);
slouken@0
   109
	pcxh.Xmax = SDL_SwapLE16(pcxh.Xmax);
slouken@0
   110
	pcxh.Ymax = SDL_SwapLE16(pcxh.Ymax);
slouken@0
   111
	pcxh.BytesPerLine = SDL_SwapLE16(pcxh.BytesPerLine);
slouken@0
   112
slouken@0
   113
	/* Create the surface of the appropriate type */
slouken@0
   114
	width = (pcxh.Xmax - pcxh.Xmin) + 1;
slouken@0
   115
	height = (pcxh.Ymax - pcxh.Ymin) + 1;
slouken@20
   116
	Rmask = Gmask = Bmask = Amask = 0;
slouken@20
   117
	src_bits = pcxh.BitsPerPixel * pcxh.NPlanes;
slouken@20
   118
	if((pcxh.BitsPerPixel == 1 && pcxh.NPlanes >= 1 && pcxh.NPlanes <= 4)
slouken@20
   119
	   || (pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 1)) {
slouken@20
   120
		bits = 8;
slouken@20
   121
	} else if(pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 3) {
slouken@20
   122
		bits = 24;
slouken@0
   123
		if ( SDL_BYTEORDER == SDL_LIL_ENDIAN ) {
slouken@0
   124
			Rmask = 0x000000FF;
slouken@0
   125
			Gmask = 0x0000FF00;
slouken@0
   126
			Bmask = 0x00FF0000;
slouken@0
   127
		} else {
slouken@4
   128
			Rmask = 0xFF0000;
slouken@4
   129
			Gmask = 0x00FF00;
slouken@4
   130
			Bmask = 0x0000FF;
slouken@0
   131
		}
slouken@20
   132
	} else {
slouken@20
   133
		error = "unsupported PCX format";
slouken@20
   134
		goto done;
slouken@0
   135
	}
slouken@0
   136
	surface = SDL_AllocSurface(SDL_SWSURFACE, width, height,
slouken@20
   137
				   bits, Rmask, Gmask, Bmask, Amask);
slouken@20
   138
	if ( surface == NULL )
slouken@0
   139
		goto done;
slouken@20
   140
slouken@20
   141
	bpl = pcxh.NPlanes * pcxh.BytesPerLine;
slouken@20
   142
	buf = malloc(bpl);
slouken@20
   143
	row = surface->pixels;
slouken@20
   144
	for ( y=0; y<surface->h; ++y ) {
slouken@20
   145
		/* decode a scan line to a temporary buffer first */
slouken@20
   146
		int i, count = 0;
slouken@20
   147
		Uint8 ch;
slouken@20
   148
		Uint8 *dst = (src_bits == 8) ? row : buf;
slouken@20
   149
		for(i = 0; i < bpl; i++) {
slouken@20
   150
			if(!count) {
slouken@20
   151
				if(!SDL_RWread(src, &ch, 1, 1)) {
slouken@20
   152
					error = "file truncated";
slouken@20
   153
					goto done;
slouken@20
   154
				}
slouken@20
   155
				if( (ch & 0xc0) == 0xc0) {
slouken@20
   156
					count = ch & 0x3f;
slouken@20
   157
					if(!SDL_RWread(src, &ch, 1, 1)) {
slouken@20
   158
						error = "file truncated";
slouken@20
   159
						goto done;
slouken@20
   160
					}
slouken@20
   161
				} else
slouken@20
   162
					count = 1;
slouken@20
   163
			}
slouken@20
   164
			dst[i] = ch;
slouken@20
   165
			count--;
slouken@20
   166
		}
slouken@20
   167
slouken@20
   168
		if(src_bits <= 4) {
slouken@20
   169
			/* expand planes to 1 byte/pixel */
slouken@20
   170
			Uint8 *src = buf;
slouken@20
   171
			int plane;
slouken@20
   172
			for(plane = 0; plane < pcxh.NPlanes; plane++) {
slouken@20
   173
				int i, j, x = 0;
slouken@20
   174
				for(i = 0; i < pcxh.BytesPerLine; i++) {
slouken@20
   175
					Uint8 byte = *src++;
slouken@20
   176
					for(j = 7; j >= 0; j--) {
slouken@20
   177
						unsigned bit = (byte >> j) & 1;
slouken@91
   178
						/* skip padding bits */
slouken@91
   179
						if (i * 8 + j >= width)
slouken@91
   180
							continue;
slouken@20
   181
						row[x++] |= bit << plane;
slouken@20
   182
					}
slouken@20
   183
				}
slouken@20
   184
			}
slouken@20
   185
 		} else if(src_bits == 24) {
slouken@20
   186
			/* de-interlace planes */
slouken@20
   187
			Uint8 *src = buf;
slouken@20
   188
			int plane;
slouken@20
   189
			for(plane = 0; plane < pcxh.NPlanes; plane++) {
slouken@20
   190
				int x;
slouken@20
   191
				dst = row + plane;
slouken@20
   192
				for(x = 0; x < width; x++) {
slouken@20
   193
					*dst = *src++;
slouken@20
   194
					dst += pcxh.NPlanes;
slouken@20
   195
				}
slouken@20
   196
			}
slouken@20
   197
		}
slouken@20
   198
slouken@20
   199
		row += surface->pitch;
slouken@0
   200
	}
slouken@0
   201
slouken@20
   202
	if(bits == 8) {
slouken@20
   203
		SDL_Color *colors = surface->format->palette->colors;
slouken@20
   204
		int nc = 1 << src_bits;
slouken@20
   205
		int i;
slouken@20
   206
slouken@20
   207
		surface->format->palette->ncolors = nc;
slouken@20
   208
		if(src_bits == 8) {
slouken@20
   209
			Uint8 ch;
slouken@20
   210
			/* look for a 256-colour palette */
slouken@20
   211
			do {
slouken@20
   212
				if ( !SDL_RWread(src, &ch, 1, 1)) {
slouken@20
   213
					error = "file truncated";
slouken@0
   214
					goto done;
slouken@0
   215
				}
slouken@20
   216
			} while ( ch != 12 );
slouken@20
   217
slouken@20
   218
			for(i = 0; i < 256; i++) {
slouken@20
   219
				SDL_RWread(src, &colors[i].r, 1, 1);
slouken@20
   220
				SDL_RWread(src, &colors[i].g, 1, 1);
slouken@20
   221
				SDL_RWread(src, &colors[i].b, 1, 1);
slouken@20
   222
			}
slouken@20
   223
		} else {
slouken@20
   224
			for(i = 0; i < nc; i++) {
slouken@20
   225
				colors[i].r = pcxh.Colormap[i * 3];
slouken@20
   226
				colors[i].g = pcxh.Colormap[i * 3 + 1];
slouken@20
   227
				colors[i].b = pcxh.Colormap[i * 3 + 2];
slouken@0
   228
			}
slouken@0
   229
		}
slouken@0
   230
	}
slouken@0
   231
slouken@0
   232
done:
slouken@20
   233
	free(buf);
slouken@20
   234
	if ( error ) {
slouken@0
   235
		SDL_FreeSurface(surface);
slouken@20
   236
		IMG_SetError(error);
slouken@0
   237
		surface = NULL;
slouken@0
   238
	}
slouken@0
   239
	return(surface);
slouken@0
   240
}
slouken@0
   241
slouken@0
   242
#else
slouken@0
   243
slouken@0
   244
/* See if an image is contained in a data source */
slouken@0
   245
int IMG_isPCX(SDL_RWops *src)
slouken@0
   246
{
slouken@0
   247
	return(0);
slouken@0
   248
}
slouken@0
   249
slouken@0
   250
/* Load a PCX type image from an SDL datasource */
slouken@0
   251
SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src)
slouken@0
   252
{
slouken@0
   253
	return(NULL);
slouken@0
   254
}
slouken@0
   255
slouken@0
   256
#endif /* LOAD_PCX */