Why is delete [] causing heap corruption?

Started by
21 comments, last by GameDev.net 17 years, 8 months ago
Quote:Original post by Paulius Maruska
This is probably not that important, but for portability sake you should change the code like this:

// allocate a temporary buffer
int bufferSize = bitmapPtr->infoHeader.biSizeImage / sizeof(unsigned int);// <= not 4
unsigned int *tempBuffer = new unsigned int[ bufferSize ];

// read in the buffer, bitmapFile is an ifstream
bitmapFile.read( (char *)tempBuffer, bitmapPtr->infoHeader.biSizeImage );

// this doesn't work
delete [] tempBuffer;


That won't really make it more portable... if sizeof(unsigned int) isn't 4, your version and the original version will probably work equally bad, assuming the allocated buffer is actually used for something.
Advertisement
Okay, so I tested the size of bitmapPtr->infoHeader.biSizeImage and it's 25602 bytes, which is not divisble by 4. It winds up being 6400.5, which I guess makes it 6400 in the allocation line. Then in the read line it reading 6400.5, which I guess is a problem. Would this cause heap corruption?

In this part of the code I'm reading in a 32-bit bitmap's pixels, so I was assuming the size of the image should be divisible by 4, which is not. Should I just treat the tempBuffer as a char* to avoid this problem?

Here's the function code so you can see what's going on

struct Bitmap32{	BITMAPFILEHEADER	fileHeader;	BITMAPINFOHEADER	infoHeader;	unsigned int		*buffer;};int MyLoadBitmap( char *fileName, Bitmap32 *bitmapPtr ){	// - loads a bitmap with the specified filename and saves the image in a 32-bit buffer	// R [1] success	// R [0] failure	ifstream bitmapFile;	bitmapFile.open( fileName, ios::binary | ios::in );	if( bitmapFile.is_open() )	{		// read in the file header		bitmapFile.read( (char *)&(bitmapPtr->fileHeader), sizeof(BITMAPFILEHEADER) );		// make sure it's a bitmap file		if( bitmapPtr->fileHeader.bfType != 0x4D42 )		{			bitmapFile.close();			return 0;	// file was not a bitmap		}		// read in the info header		bitmapFile.read( (char *)&(bitmapPtr->infoHeader), sizeof(BITMAPINFOHEADER) );		// make sure the bitmap is 32-bit		if( bitmapPtr->infoHeader.biBitCount != 32 )		{			bitmapFile.close();			return 0;	// bitmap was not 32-bit		}		// allocate a temporary buffer		int				bufferSize = bitmapPtr->infoHeader.biSizeImage / 4;		unsigned int	*tempBuffer = new unsigned int[ bufferSize ];		// read in the buffer		bitmapFile.read( (char *)tempBuffer, bitmapPtr->infoHeader.biSizeImage );		delete [] tempBuffer; // this is just to test the delete problem		// flip the buffer because windows stores the colors are BGRA and opengl wants RGBA		unsigned char red	= 0;		unsigned char green = 0;		unsigned char blue	= 0;		unsigned char alpha	= 0;		unsigned int *curPos = tempBuffer;		for( int i = 0; i < bufferSize; i++ )		{			blue	= *curPos;			green	= *curPos >> 8;			red		= *curPos >> 16;			alpha	= *curPos >> 24;			*curPos = ((alpha << 24) + ((blue) << 16) + ((green) << 8) + (red));			curPos++;		}		// delete any previous bitmap buffer data		if( bitmapPtr->buffer != NULL )		{			delete [] bitmapPtr->buffer;			bitmapPtr->buffer = NULL;		}		// assign the buffer to point to the new buffer		bitmapPtr->buffer = tempBuffer;		return 1;	}	else		return 0;	// file could not be opened} // end of MyLoadBitmap
That's the point; you end up not allocating those last two bytes because the .5 gets truncated. Allocate char's instead of doing weird division stuff; that way, you don't depend on the size of an int being 4 and only your data being aligned to 4 bytes (which it isn't).
Additionally the bitmap format is stored as BGR, not BGRA, so it's only three bytes per pixel. Note that each scanline is padded to a multiple of four bytes though.

Σnigma

[Edited by - Enigma on August 12, 2006 1:38:24 PM]
Quote:Original post by Enigma
Additionally the bitmap format is stored as BGR, not BGRA, so it's only three bytes per pixel. Note that each scanline is padded to a multiple of four bytes though.

Since it's a 32-bit bitmap wouldn't that make it BGRA because the alpha channel needs to be stored in there too, right?

Yes, you're right. My apologies. It's so long since I've dealt with anything other than 24-bit bitmaps I'd forgotten that 32-bit versions actually exist. However, since your size is not a multiple of four there is something wrong. Do you actually have an uncompressed bitmap? Try printing out the biCompression member of your BITMAPINFOHEADER. If it's not zero then you're dealing with a compressed bitmap. If it is zero then something is seriously wrong!

EDIT: Technically there's still no alpha channel in a 32-bit bitmap though. According to MSDN the high order byte of each pixel DWORD is simply unused.

Σnigma
Well I switched tempBuffer from unsigned int* to unsigned char* and modified everything else accordingly and it works now! No more heap corruption :)

I really need to not assume things while programming because it looks like that's what got me trouble this time. As you probably already know, I was reading in more then I allocated memory for.

Enigma, my bitmap is uncompressed according to biCompression which is zero, so the byte size of the image should have been divisble by 4, but I was wrong to assume so. I honestly don't know why my image has 2 extra bytes, because it's 80x80 and 32-bit so it should take up 25600 bytes, but instead it takes up 25602 according to biSizeImage in my BITMAPINFOHEADER.

Well, thanks for the help everyone. These forums are always a great help :)
Quote:Original post by sammyjojo
I really need to not assume things while programming because it looks like that's what got me trouble this time. As you probably already know, I was reading in more then I allocated memory for.


Seeing as you're on the "anti-assuming-track", don't you think it might be a good idea to not trust the bitmap for the allocation size at all? The file might be corrupt, you know - giving completely insane sizes...

Trusting arbitrary input that way never leads to anything good. As an additional stability measure, how about adding some restrictions or sanity checks? The simplest sanity check might be to never allocate anything above a given size of your own choice. Exact numbers would depend on your usage patterns in that case. A more elaborate sanity check might be to compare the file size to the size of the pixel data given in the file, and see it it seems reasonable...
Quote:Original post by sammyjojo
I honestly don't know why my image has 2 extra bytes, because it's 80x80 and 32-bit so it should take up 25600 bytes, but instead it takes up 25602 according to biSizeImage in my BITMAPINFOHEADER.


- Where did the file come from?
- Does the value you're getting check with the file size (accounting for header)?
Does this work?

// allocate a temporary buffer
int bufferSize = (bitmapPtr->infoHeader.biSizeImage + 3) >> 2;
unsigned int* tempBuffer = new unsigned int[ bufferSize ];

// read in the buffer, bitmapFile is an ifstream
bitmapFile.read( (char *)tempBuffer, bitmapPtr->infoHeader.biSizeImage );

// this doesn't work
delete [] tempBuffer;

This topic is closed to new replies.

Advertisement