15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


_ASSERTE: Bogus heap pointer

Marty Halvorson -- marty@Lanl.GOV
Tuesday, May 07, 1996

Platform and tools:

	VC++ 4.1 / Windows NT 3.51
    Dell PC w/ 150 MHz Pentium, 48 MB RAM, 2 GB disk

I have a problem with closing my application.  It gets an _ASSERTE failure
while deleting a class.  The comments for the failure indicate either a bogus
heap pointer or a heap pointer to other than the local heap.

Background:
We are building a scientific instrument.  The instrument itself has a small
micro-processor that communicates with the instrument console (which is a PC
running Windows NT) via three sockets.  The instrument is running a real time
system.  The socket code for the console is contained in a DLL.

The instrument is created using "new" in the view class.  This is done to
allow the PC to talk to multiple instruments.  The instrument then creates,
again using "new" two additional classes (new is used to allow passing the
IP address of the instrument), each of which creates a single socket
connected to the instrument.  One of the socket classes then creates a
thread that waits for data from the instrument itself.  The third socket is
not created, as third socket is used only for retrieval of data from any
experiment being run.

When closing the application, the view "delete"s the instrument class using
the pointer returned by "new".  All the appropriate destructors get called in
the proper order (i.e. view destructor is called before instrument destructor
is called before socket class destructor).  The value of the instrument
pointer at delete time is identical to the value returned from new, the
thread ID is the same at delete time as when the instrument was created.

At this point I don't even have any ideas of what else to try.  Any
suggestions will be tried.

The relevant code follows.  Sorry for the length, I've included everything
that may have a bearing on the failure.

In the for what it's worth category.  I am relatively new to MFC (~ 8
months).  I am not new to programming though.  I've been doing Unix socket
and multi-thread code for the past few years, and writing code for 30+
years.

Thanks and Peace

			Marty Halvorson
            marty@lanl.gov


Assertion failure point:
/***
 *dbgheap.c - Debug CRT Heap Functions
 *
 *       Copyright (c) 1988-1996, Microsoft Corporation. All rights reserved.
 *
 *Purpose:
 *       Defines debug versions of heap functions.
 *
 ***************************************************************************
****/

			...
        /*
         * If this ASSERT fails, a bad pointer has been passed in. It may be
         * totally bogus, or it may have been allocated from another heap.
         * The pointer MUST come from the 'local' heap.
         */
        _ASSERTE(_CrtIsValidHeapPointer(pUserData));
			...




In View Class:

// Code to create the instrument class
void CHardDView::CreateCyto ()
{

	CytoParm Parms;
	Parms.Cytometer = pCytoHost;			// Pointer to instrument's name
	Parms.hWnd = pParent->GetSafeHwnd();	// So message pump works
	if (Parms.hWnd == NULL) {
    	...
		}
	// Create the instrument
	pCyto = new CNtbCytometer (&Parms);	// Start cytometer communcations

}


// Code to delete the instrument class
CHardDView::~CHardDView()
{
	if (pCyto != NULL) delete pCyto;
		...
}



In Instrument class:

// Code to instantiate the instrument
CNtbCytometer::CNtbCytometer(CytoParm * ParmList)
{
	HWND hThis;
// Attach the applications window to this instantation of the class
	if (FromHandlePermanent(ParmList->hWnd) != NULL) {
		hThis = this->GetSafeHwnd();
		}
	else {
		if (!Attach(ParmList->hWnd)) {
			// fatal error
			}
		hThis = this->GetSafeHwnd();
		if (hThis == NULL) {
			// fatal error
			}
		}
	hWndParent = ParmList->hWnd;

	WM_FCMSTATECHANGE = RegisterWindowMessage (strFCMStateChange);

// Start Windows Socket Application interface

		...

// Create events for each status command (max of 20,000 waiting events)
	g_hTest = CreateSemaphore (NULL, 0, 20000, "TestCommand");
		... // More of the same

// Create status and command classes
// These two classes each create a socket
	CyStat = new CNtbStatusData ();
	CyCmd  = new CNtbCmdData ();
// Set pointer to cytometer in socket classes
	CyCmd->Cytometer = this;
	CyStat->Cytometer = this;

// Initialize the command socket class
	// connect the socket to the instrument
	CyCmd->Initialize (CyIPAddr);

// Initialize the status socket class
	// connect the socket to the instrument, and
	// create a thread that waits for data to arrive
	CyStat->Initialize (CyIPAddr);

}

CNtbCytometer::~CNtbCytometer()
{
	delete CyStat;	// Class containing socket and thread
	delete CyCmd;	// Class containing socket
	
	CloseHandle (g_hTest);
		... // Close the rest

	Detach ();

	while (WSACleanup ()) {		// make sure the socket interface is cleaned up
		...
		}
}




// Code in class containing socket and thread
void CNtbStatusData::Initialize (u_long CyIPAddr)
{
// Create socket
		...
// Initialize status buffer queue
		...
// Create thread to handle incoming status data
	g_hStatusReady = CreateEvent (NULL, TRUE, FALSE, "StatusReady");

	DWORD IDStatusReady;
	hStatusReady = CreateThread (0, 0,
									CNtbStatusData::OnStatusEvent,
									(LPVOID) this,
									0, &IDStatusReady);

}

CNtbStatusData::~CNtbStatusData()
{
	// Release the thread waiting for status from the cytometer
	WSACancelBlockingCall();		// If waiting on read file
	SetEvent (g_hStatusReady);		// If waiting for event
	Sleep (0);						// Release time slice

	// close the socket
	if (pStatus != INVALID_SOCKET)
		closesocket (pStatus);

	// Wait till the thread is finished
	WaitForSingleObject (hStatusReady, 10*OneSecond);

	// Close the event handle
	CloseHandle (g_hStatusReady);

	// Clean up the status queue
		...
}




Joseph Koral -- jkoral@ftp.com
Thursday, May 09, 1996

[Mini-digest: 2 responses]

Marty Halvorson wrote:

>> It gets an _ASSERTE failure while deleting a class.  The comments for =

>> the failure indicate either a bogus heap pointer or a heap pointer to =
other=20
>> than the local heap.

>> The socket code for the console is contained in a DLL. When closing =
the=20
>> application, the view "delete"s the instrument class using the =
pointer=20
>> returned by "new". =20

There's a major bug in Visual C++ 4.1 (and all previous versions, 1.0, =
1.5, 2.0, 4.0, etc.) that can potentially cause heap corruption and =
subsequent runtime errors.

The Knowledge Base article that details the bug (Q122675,  Bug: Wrong =
Operator Delete Called for Exported Class) is out of date, and Microsoft =
recently confirmed for that it is indeed still broken in 4.0 and 4.1 (at =
first I was told that according to their online KB, this was fixed in =
4.0).

There is a new/delete mismatch when allocating objects of a class =
exported from a DLL (typically non-MFC).  This can be easily =
reproducible with the following simple app, where foo is a class =
exported from a DLL:

     void main( )
     {
          foo *p =3D new foo;
          delete p;
     }

This trips an ASSERT inside the RTL's heap management.  (Under NT, it =
also triggers a breakpoint a little later when validating a heap entry).

In this example, the new operator in the EXE allocated the foo object, =
but the delete operator in the DLL deallocated it.  This results in a =
new/delete mismatch which corrupts the heap.

A quick test to verify this is your problem is to try using ::new and =
::delete.  This will force the global operator new and delete in the EXE =
and will confirm that this is the problem.

I won't repeat the Knowledge Base article which describes the bug in =
detail, including a sample and suggested workarounds.  But here's the =
basic cause and a solution:

At compile time, the helper functions 'Scalar deleting destructor' and =
'Vector deleting destructor' are generated to call an object's =
destructor and free the associated memory.

These helper functions are generated in the context of the DLL, and use =
the DLL's global delete operator.  However, the allocation of the object =
uses the global new operator in the EXE, hence the mismatch.

One solution, is to override the new and delete operators for the =
exported class.  These operators, which simply call the global new and =
delete, will force both allocation and deallocation to take place in the =
context of the DLL.

- Joseph

-----From: "Mike Blaszczak" 

From: 	owner-mfc-l@netcom.com on behalf of Marty Halvorson
Sent: 	Tuesday, May 07, 1996 11:40

>	VC++ 4.1 / Windows NT 3.51

Thanks.

> I have a problem with closing my application.  It gets an _ASSERTE failure
> while deleting a class.  The comments for the failure indicate either a 
bogus
> heap pointer or a heap pointer to other than the local heap.

It turns out that this comment is accurate: the pointer is either bogus or a 
pointer to memory that is in another module's heap.

If you build an application (that is, an EXE) that uses the debug C Runtimes 
and then build a DLL which uses the retail C Runtimes, you can't allow memory 
allocated by the DLL to be freed by the EXE, or vice-versa, because the 
different C Runtimes are using different heaps.  That scenario will cause this 
assertion.

The more nebulous reason for this assertion is the first reason indicated in 
the comment: a bogus heap pointer.  That can mean lots of things:

+ The pointer isn't to a heap-allocated object; maybe you called delete on the 
address of a static or a local.

+ The pointer was incremented or decremented after it was allocated:

      char* pstr = new char[95];
      pstr++;
      delete [] pstr;

+ The information after or before the pointer was trashed after it was 
allocated, and the pointer really is accurate but nobody can tell that becuase 
you smashed the memory:

   char *pstr = new char[10];
   char *pstrCopy = pstr;
   pstrCopy -= 5;
   strcpy(pstrCopy, "I am the baddest in the land!"); // more than 10 chars
   delete [] pstr;

+ The memory has already been freed:

   char *pstr = new char[10];
   delete [] *pstr;
   delete [] *pstr;

There might be other ways to pitch this assertion, but I can't think of any 
right now.

You say:

 > The value of the instrument
 > pointer at delete time is identical to the value returned from new, the
 > thread ID is the same at delete time as when the instrument was created.

Which suggests that a couple of the causes above can be eliminated... leaving 
the "stepping on memory" or "deleting twice" causes as candidates.  You should 
figure out if you're being sloppy with a buffer.  This sounds like a scenario 
where a tool like BoundsChecker might help you.

.B ekiM
TCHAR sz[] = _T("That's the news: see you at the bar.");




| Вернуться в корень Архива |