_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.");
| Вернуться в корень Архива |