Discussion:
Heap Corruption when using delete
(too old to reply)
Jack
2009-12-16 09:47:13 UTC
Permalink
PBYTE binfile = new BYTE[size.QuadPart+0x1e0-4];

//
delete binfile;
binfile = NULL;

These statements are located in the same method.
What would be the probable causes of heap corruption
(written past the end of the heap buffer, buffer overrun i guess)
Thanks
Jack
Jack
2009-12-16 09:53:44 UTC
Permalink
One might ask why I used new BYTE[...];
instead of std::vector<BYTE>

because I want to advance the pointer at my own will
such as this
binfile += 0x1e0;

binfile.push_back(....)
where ... is of type "static BYTE" also feeds back with errors.

Thanks
Jack
Giovanni Dicanio
2009-12-16 11:13:09 UTC
Permalink
Post by Jack
One might ask why I used new BYTE[...];
instead of std::vector<BYTE>
because I want to advance the pointer at my own will
such as this
binfile += 0x1e0;
If you use std::vector, you can use a raw C pointer to point inside vector,
e.g.:

std::vector< BYTE > v( << some size >> );

BYTE * binfile = &v[0];

The benefit of using vector instead of new[] is that your code is now
exception safe (e.g. if an exception is thrown, the vector releases the heap
allocated bytes).

HTH,
Giovanni
Jack
2009-12-16 11:17:13 UTC
Permalink
Hi Giovanni,
Post by Giovanni Dicanio
std::vector< BYTE > v( << some size >> );
BYTE * binfile = &v[0];
Sounds like that is what I am after.
Thanks Giovanni
Jack
Jack
2009-12-16 11:44:27 UTC
Permalink
No, still crashed

GetFileSizeEx(h, &size);

std::vector< BYTE > v( size.QuadPart+0x1e0-4 );

BYTE * binfile = &v[0];


memset (binfile, 0, size.QuadPart+0x1e4-4);
memcpy (binfile, template_bin, 0x1e0);
binfile += 0x1e0;

hFileMapping = CreateFileMapping (h, NULL, PAGE_READONLY, 0, 0, NULL);
if (hFileMapping == 0)
{
CloseHandle(h);
MessageBoxA(NULL, "Couldn't open file mapping", "Error", MB_OK);

return E_FAIL;
}

//// Get a whole file into memory
PBYTE g_pMappedFileBase = (PBYTE) MapViewOfFile (hFileMapping,
FILE_MAP_READ, 0, 0, 0);
if (g_pMappedFileBase == 0)
{
CloseHandle (hFileMapping);
CloseHandle (h);
return E_FAIL;
}

if (memcmp(g_pMappedFileBase, "hdr1", 4) == 1)
{
CloseHandle(hFileMapping);
CloseHandle(h);
MessageBoxA(NULL, "Error Loading Header", "Error", MB_OK);
return E_FAIL;
}

memcpy (binfile, g_pMappedFileBase+4, size.QuadPart-4);
binfile -= 0x1e0; // back to origin

// mucking around with it :)

Thanks
Jack
Igor Tandetnik
2009-12-16 12:38:04 UTC
Permalink
Post by Jack
No, still crashed
std::vector< BYTE > v( size.QuadPart+0x1e0-4 );
memset (binfile, 0, size.QuadPart+0x1e4-4);
You are writing past the end of allocated buffer.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Jack
2009-12-16 12:48:58 UTC
Permalink
Hi Igor,
Thanks for your help first!
I've posted the whole method under Jochen's post :)
Could you please cross-check the coding for me?
Under jochen's post...
Thanks
Jack
Igor Tandetnik
2009-12-16 13:04:59 UTC
Permalink
Post by Jack
I've posted the whole method under Jochen's post :)
Could you please cross-check the coding for me?
It has the exact same problem. Plus the fact that you are no longer doing "new" (delegating to vector to perform the allocation), but are still doing "delete" (on the pointer that you didn't allocate).
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Jack
2009-12-16 13:12:48 UTC
Permalink
Hello Igor,
I hv deleted the "delete binfile" line,but still
buffer overrun
Thanks
Jack
Igor Tandetnik
2009-12-16 13:50:16 UTC
Permalink
Post by Jack
I hv deleted the "delete binfile" line,but still
buffer overrun
Because you are still writing past the end of allocated buffer.

std::vector< BYTE > v( size.QuadPart+0x1e0-4 );
memset (binfile, 0, size.QuadPart+0x1e4-4);

Note 0x1e0 in the first line, 0x1e4 in the second.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Bo Persson
2009-12-16 17:48:31 UTC
Permalink
Post by Igor Tandetnik
Post by Jack
I hv deleted the "delete binfile" line,but still
buffer overrun
Because you are still writing past the end of allocated buffer.
std::vector< BYTE > v( size.QuadPart+0x1e0-4 );
memset (binfile, 0, size.QuadPart+0x1e4-4);
Note 0x1e0 in the first line, 0x1e4 in the second.
This is one very good reason for using named constants and no magic
numbers!

Also, the vector is already initialized to zero by the first line, so
there is no need whatsoever for the memset.


Bo Persson
Jochen Kalmbach [MVP]
2009-12-16 10:16:23 UTC
Permalink
Hi Jack!
Post by Jack
PBYTE binfile = new BYTE[size.QuadPart+0x1e0-4];
These statements are located in the same method.
What would be the probable causes of heap corruption
(written past the end of the heap buffer, buffer overrun i guess)
You write beyond the allocated length...
--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
Jack
2009-12-16 10:23:39 UTC
Permalink
Post by Jochen Kalmbach [MVP]
You write beyond the allocated length...
Thanks Jochen,
To hardcode (experiment) this, I have actually "rewind"
the pointer to the start of the buffer, then issue "delete binfile" to no
avail.
How can I specify the size of deletion? it seems that I can't have any
control of the deletion anyway...

Thanks
Jack
Jochen Kalmbach [MVP]
2009-12-16 10:28:35 UTC
Permalink
Hi Jack!
Post by Jack
Post by Jochen Kalmbach [MVP]
You write beyond the allocated length...
To hardcode (experiment) this, I have actually "rewind"
the pointer to the start of the buffer, then issue "delete binfile" to no
avail.
????
You *must* pass the *same* point to "delete" which was the result of the
"new" allocation!
Post by Jack
How can I specify the size of deletion? it seems that I can't have any
control of the deletion anyway...
The previous allocated size will be freed... no need to specify anything...
--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
Jack
2009-12-16 10:37:36 UTC
Permalink
Thanks Jochen once again as you come back to this
Frustrated because when I added the line
delete binfile; it just crashed with heap corruption
When I cancelled it,
There were 4 bytes of memory leak chunks repeatedly.
Thanks
Jack
Giovanni Dicanio
2009-12-16 11:09:28 UTC
Permalink
Post by Jack
PBYTE binfile = new BYTE[size.QuadPart+0x1e0-4];
//
delete binfile;
binfile = NULL;
These statements are located in the same method.
What would be the probable causes of heap corruption
(written past the end of the heap buffer, buffer overrun i guess)
You should do (note [ and ] )

delete [] binfile;

Giovanni
Stephen Howe
2009-12-16 11:55:42 UTC
Permalink
Post by Jack
PBYTE binfile = new BYTE[size.QuadPart+0x1e0-4];
delete binfile;
This is incorrect C++.

If you do new [], it should be matched with delete [], not delete

new[] and delete [] go together (for multiple items)
new and delete go together (for single item)

Cheers

Stephen Howe
Jack
2009-12-16 12:12:13 UTC
Permalink
Hi Stephen,
Actually, I have tried both delete and delete[], but doesn't work either.
Thanks for your help!!
Jack
Jochen Kalmbach [MVP]
2009-12-16 12:25:19 UTC
Permalink
Hi Jack!
Post by Jack
Actually, I have tried both delete and delete[], but doesn't work either.
In this special case (POD) it makes no difference...

You need to show the code between the "new" and "delete"!
--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
Jack
2009-12-16 12:33:03 UTC
Permalink
There u go, :)

HRESULT CMesh::SetMeshData(char *szfilename)
{
LARGE_INTEGER size;
HANDLE hFileMapping;
int cchFileName;
char szPath[256];
char *szTemp;
DWORD cchPath;
CAllocateHierarchy Alloc;

// unload these functions to a dll
::GetModuleFileNameA(NULL, szPath, sizeof(szPath));

strcat (szPath, szfilename);


//// Open .dat mesh
HANDLE h = CreateFileA(szPath, GENERIC_READ, NULL, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
{
MessageBoxA(NULL, "Couldn't open file with CreateFile()", "Error",
MB_OK);
return E_FAIL;
}

GetFileSizeEx(h, &size);

std::vector< BYTE > v( size.QuadPart+0x1e0-4 );

BYTE * binfile = &v[0];


//PBYTE binfile = new BYTE[size.QuadPart+0x1e0-4]; // maybe get the file
size 1st
memset (binfile, 0, size.QuadPart+0x1e4-4);
//binfile.clear();
memcpy (binfile, template_bin, 0x1e0);
//binfile.push_back(template_bin);//, 0x1e0);
binfile += 0x1e0;

hFileMapping = CreateFileMapping (h, NULL, PAGE_READONLY, 0, 0, NULL);
if (hFileMapping == 0)
{
CloseHandle(h);
MessageBoxA(NULL, "Couldn't open file mapping", "Error", MB_OK);

return E_FAIL;
}

//// Get a whole file into memory
PBYTE g_pMappedFileBase = (PBYTE) MapViewOfFile (hFileMapping,
FILE_MAP_READ, 0, 0, 0);
if (g_pMappedFileBase == 0)
{
CloseHandle (hFileMapping);
CloseHandle (h);
return E_FAIL;
}

if (memcmp(g_pMappedFileBase, "hdr1", 4) == 1)
{
CloseHandle(hFileMapping);
CloseHandle(h);
MessageBoxA(NULL, "Error Loading Header", "Error", MB_OK);
return E_FAIL;
}

memcpy (binfile, g_pMappedFileBase+4, size.QuadPart-4);
binfile -= 0x1e0; // back to origin

HRESULT hr = D3DXLoadMeshHierarchyFromXInMemory((LPCVOID) binfile,
size.QuadPart + 0x1e0-4, D3DXMESH_MANAGED, m_pDevice, &Alloc,
NULL, (LPD3DXFRAME*)&(m_pFrameRoot), &m_pAnimController);



if (FAILED(hr))
{
MessageBoxA(NULL, "Can't load mesh", "Error", MB_OK);
}


if (binfile) {

delete binfile;
binfile = NULL;

}


return S_OK;


}
Giovanni Dicanio
2009-12-16 17:27:35 UTC
Permalink
Post by Jack
HRESULT CMesh::SetMeshData(char *szfilename)
You should respect const-correctness, and use 'const char *' as input
parameter.
Better, move to Unicode (or at least use TCHAR, which works in both
ANSI/MBCS and Unicode builds).
Moreover, usual Hungarian Notation for pointers to "raw C" NUL-terminated
strings is *psz*, not 'sz'; 'sz' is usually for static buffers like:

char szBuf[ 100 ];

(in fact, sizeof(pszSomething) is always 4 on 32-bit systems, i.e. the size
of a pointer; instead sizeof(szSomething) is the size in bytes of given
buffer; so it can be good to differentiate between these two cases using
different prefixes).
So, I would write:

HRESULT CMesh::SetMeshData( LPCTSTR pszFilename )
Post by Jack
{
LARGE_INTEGER size;
HANDLE hFileMapping;
int cchFileName;
char szPath[256];
char *szTemp;
DWORD cchPath;
cchFileName, szTemp and cchPath seem not used in your code: I would just
remove their definitions from method body.
Moreover, use WCHAR or TCHAR, instead of obsolete char, e.g.:

TCHAR szPath[ ... ];
Post by Jack
CAllocateHierarchy Alloc;
// unload these functions to a dll
::GetModuleFileNameA(NULL, szPath, sizeof(szPath));
Just use the Unicode or TCHAR versions of Win32 APIs (and use _countof or
ARRAYSIZE instead of sizeof, to get proper size in *elements count*, not in
bytes):

GetModuleFileName( NULL, szPath, _countof( szPath ) );

Moreover, I would check the return value of Win32 APIs like the above one.
Post by Jack
strcat (szPath, szfilename);
Use CString class to concatenate strings, or use safe string functions like
StringCchCat, but you should avoid use of old strcat & Co.
Post by Jack
//// Open .dat mesh
HANDLE h = CreateFileA(szPath, GENERIC_READ, NULL, NULL, OPEN_EXISTING,
CreateFile is just fine (get rid of the CreateFileA).
Post by Jack
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
{
MessageBoxA(NULL, "Couldn't open file with CreateFile()", "Error",
MB_OK);
return E_FAIL;
I think that it is better to return an HRESULT based on GetLastError() than
a generic E_FAIL.
e.g.

DWORD errorCode = GetLastError();
return HRESULT_FROM_WIN32( errorCode );

Moreover, I would prefer tracing error messages like above one using
ATLTRACE instead of MessageBox.
All that applies also to other Win32 calls you had (like CreateFileMapping
and MapViewOfFile).
Post by Jack
}
GetFileSizeEx(h, &size);
I would check the return value of GetFileSizeEx, too.
Post by Jack
std::vector< BYTE > v( size.QuadPart+0x1e0-4 );
Note that there is a subtle thing here.
In case of allocation failure, std::vector throws a C++ exception (I think
std::bad_alloc).
But you are returning HRESULT's.
So, I would safely guard the vector construction using a try/catch, and in
case of std::bad_alloc exception return a proper HRESULT code like
E_OUTOFMEMORY.
Post by Jack
BYTE * binfile = &v[0];
//PBYTE binfile = new BYTE[size.QuadPart+0x1e0-4]; // maybe get the file
size 1st
memset (binfile, 0, size.QuadPart+0x1e4-4);
I don't like these "magic" numbers like 0x1E4 - 4.
I would define a named constant for that:

static const size_t something = 0x1E0;

Moreover, you passed 'size.QuadPart+0x1e0-4' to the constructor, but you are
memset'ting a different (bigger size).
I would just use v.size() method to get size of data in bytes (considering
that your vector instance is a vector of BYTE's).

memset( &v[0], 0, v.size() );
Post by Jack
//binfile.clear();
memcpy (binfile, template_bin, 0x1e0);
//binfile.push_back(template_bin);//, 0x1e0);
binfile += 0x1e0;
hFileMapping = CreateFileMapping (h, NULL, PAGE_READONLY, 0, 0, NULL);
if (hFileMapping == 0)
{
CloseHandle(h);
MessageBoxA(NULL, "Couldn't open file mapping", "Error", MB_OK);
return E_FAIL;
}
//// Get a whole file into memory
PBYTE g_pMappedFileBase = (PBYTE) MapViewOfFile (hFileMapping,
Usually the g_ prefix is for *g*lobal variables, not for local ones.
Using g_ prefix for a local variable seems to me confusing.
Post by Jack
FILE_MAP_READ, 0, 0, 0);
if (g_pMappedFileBase == 0)
{
CloseHandle (hFileMapping);
CloseHandle (h);
return E_FAIL;
}
if (memcmp(g_pMappedFileBase, "hdr1", 4) == 1)
{
CloseHandle(hFileMapping);
CloseHandle(h);
MessageBoxA(NULL, "Error Loading Header", "Error", MB_OK);
return E_FAIL;
}
memcpy (binfile, g_pMappedFileBase+4, size.QuadPart-4);
binfile -= 0x1e0; // back to origin
HRESULT hr = D3DXLoadMeshHierarchyFromXInMemory((LPCVOID) binfile,
size.QuadPart + 0x1e0-4, D3DXMESH_MANAGED, m_pDevice, &Alloc,
NULL, (LPD3DXFRAME*)&(m_pFrameRoot), &m_pAnimController);
if (FAILED(hr))
{
MessageBoxA(NULL, "Can't load mesh", "Error", MB_OK);
}
You should return 'hr' here, not continuing execution to 'return S_OK;' that
follows.
Post by Jack
if (binfile) {
delete binfile;
binfile = NULL;
std::vector deletes itself (RAII): the above delete is wrong.
Note that you may want to use RAII paradigma also for file handle and memory
mapping handle, i.e. you could write a simple class to wrap these handles
and the destructor calls CloseHandle for you.
In this way, you don't need to repeat CloseHandle calls in every failure
exit points in your function: destructors will automatically be called for
you, and your code is simplified.
Post by Jack
}
return S_OK;
}
Giovanni
Barry Schwarz
2009-12-17 05:49:41 UTC
Permalink
On Wed, 16 Dec 2009 20:33:03 +0800, "Jack" <***@knight.com> wrote:

snip
Post by Jack
std::vector< BYTE > v( size.QuadPart+0x1e0-4 );
BYTE * binfile = &v[0];
//PBYTE binfile = new BYTE[size.QuadPart+0x1e0-4]; // maybe get the file
size 1st
memset (binfile, 0, size.QuadPart+0x1e4-4);
You are overrunning the memory pointed to by binfile by 4 bytes.
Post by Jack
//binfile.clear();
memcpy (binfile, template_bin, 0x1e0);
//binfile.push_back(template_bin);//, 0x1e0);
binfile += 0x1e0;
snip
Post by Jack
if (binfile) {
delete binfile;
You commented out the dynamic allocation so this makes no sense.
Post by Jack
binfile = NULL;
}
return S_OK;
}
--
Remove del for email
Jack
2009-12-17 14:43:23 UTC
Permalink
Update:

// No memory leak after comment operators removed
/* hr = D3DXLoadMeshHierarchyFromXInMemory((LPCVOID) pBin, j,
D3DXMESH_MANAGED, m_pDevice, &Alloc,
NULL, (LPD3DXFRAME*)&m_pFrameRoot, &m_pAnimController);*/

I worked hard to remove the heap corruption problem,
and now it's gone, but new problem arises,
When I comment out the above code,
there is no memory leaks, it suffers from memory leak
when this Direct3D function call is removed.

Detected memory leaks!
Dumping objects ->
{2034} normal block at 0x0213C5B8, 4 bytes long.
Data: < > 00 00 00 00
{2025} normal block at 0x0213BFD8, 4 bytes long.
Data: < > 00 00 00 00
{2016} normal block at 0x0213B9F8, 4 bytes long.
Data: < > 00 00 00 00
{2007} normal block at 0x0213B088, 4 bytes long.
Data: < > 00 00 00 00
{1998} normal block at 0x0213A760, 4 bytes long.
Data: < > 00 00 00 00
{1989} normal block at 0x02139DC0, 4 bytes long.
Data: < > 00 00 00 00
{1978} normal block at 0x02139680, 4 bytes long.
Data: < > 00 00 00 00
{1969} normal block at 0x021390A0, 4 bytes long.
Data: < > 00 00 00 00
.....
More and more


Thanks
Jack
Jack
2009-12-17 16:09:12 UTC
Permalink
Post by Jack
// No memory leak after comment operators removed
/* hr = D3DXLoadMeshHierarchyFromXInMemory((LPCVOID) pBin, j,
D3DXMESH_MANAGED, m_pDevice, &Alloc,
NULL, (LPD3DXFRAME*)&m_pFrameRoot, &m_pAnimController);*/
I worked hard to remove the heap corruption problem,
and now it's gone, but new problem arises,
When I comment out the above code,
there is no memory leaks, it suffers from memory leak
when this Direct3D function call is removed.
or with the function call.
Post by Jack
Detected memory leaks!
Dumping objects ->
{2034} normal block at 0x0213C5B8, 4 bytes long.
Data: < > 00 00 00 00
{2025} normal block at 0x0213BFD8, 4 bytes long.
Data: < > 00 00 00 00
{2016} normal block at 0x0213B9F8, 4 bytes long.
Data: < > 00 00 00 00
{2007} normal block at 0x0213B088, 4 bytes long.
Data: < > 00 00 00 00
{1998} normal block at 0x0213A760, 4 bytes long.
Data: < > 00 00 00 00
{1989} normal block at 0x02139DC0, 4 bytes long.
Data: < > 00 00 00 00
{1978} normal block at 0x02139680, 4 bytes long.
Data: < > 00 00 00 00
{1969} normal block at 0x021390A0, 4 bytes long.
Data: < > 00 00 00 00
.....
More and more
Thanks
Jack
Stephen Howe
2009-12-16 14:28:44 UTC
Permalink
Post by Jochen Kalmbach [MVP]
Post by Jack
Actually, I have tried both delete and delete[], but doesn't work either.
In this special case (POD) it makes no difference...
Oh yes it does. POD'ness does not come into it. Yes I know there is no destructors

delete [] retrieves the number of items
delete does not.

It is perfectly possible, depending on implementation, that this screws up the heap.
The number of items has been implemented by overallocation of memory blocks but it has also been implemented by associative
arrays. Using delete could cause some non-cleanup of bookkeeping for delete [].

Stephen Howe
Jochen Kalmbach [MVP]
2009-12-16 14:38:19 UTC
Permalink
Hi Stephen!
Post by Stephen Howe
Oh yes it does. POD'ness does not come into it. Yes I know there is no destructors
delete [] retrieves the number of items
delete does not.
In the current CRT/MS implementation it makes no difference.
But you are right: it is bad code and might break in the future...
--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
Stephen Howe
2009-12-16 16:37:01 UTC
Permalink
Post by Jochen Kalmbach [MVP]
In the current CRT/MS implementation it makes no difference.
That is probably an optimisation.
The CRT/MS arranges that operator delete is called for array PODs, knowing that it makes no difference and it is a tad faster.
But the programmer should not assume that.

Stephen Howe
Bo Persson
2009-12-16 17:45:48 UTC
Permalink
Post by Jochen Kalmbach [MVP]
Hi Jack!
Post by Jack
Actually, I have tried both delete and delete[], but doesn't work either.
In this special case (POD) it makes no difference...
This is also specific for the MS compilers, and not a general rule.


Bo Persson
Stephen Howe
2009-12-16 14:42:16 UTC
Permalink
Post by Jack
Hi Stephen,
Actually, I have tried both delete and delete[], but doesn't work either.
Thanks for your help!!
Jack
Your looking in the wrong place.
If delete or delete [] does not "work", the heap may have been corrupted some time ago.
So it is the previous code that uses the heap that needs looking at.

A program is like a wild west gunfighter
A program may "die" a long way from where it is "shot"
Debugging is observing the "death" ofa program and tracing backwards to where the first program error occured.
Programmers should remember that an error may cause subsequent errors and you may be looking at a subsequent error, not the
original causing error. So your delete may not be the original problem. You need to trace backwards, find the first place that
the heap is corrupted, fix that.

Sprinkle your debug code with _heapchk()
It should return _HEAPOK or _HEAPEMPTY

If it returns any other value, you have corrupted the heap between this call and the last good call to _heapchk()

Note, it is an error to

1. Call free(), delete or delete [] on memory more than once on the same pointer except if NULL
2. Call free() on memory not obtained by malloc(), calloc() or realloc()
3. Call delete on memory not obtained by new
4. Call delete [] on memory not obtained by new []
5. Read or writing to memory after free(), delete or delete [] has been called.
6. Writing after the end of block obtained by new, new[], malloc(), calloc() or realloc()
7. Writing before the beginning of a block obtained by new, new[], malloc(), calloc() or realloc()
8. Supplying realloc() a pointer that is not NULL and not obtained by realloc(), calloc() or malloc()

Stephen Howe
Continue reading on narkive:
Loading...