// Routine to check all active packets to see if they still look valid. // Optionally, also check that the non-NULL address passed does not lie within // any of the currently allocated packets. void DbgValidateActivePackets(void *Start, void *End) { DbgAllocHeader *h = g_AllocListFirst; while (h) { DbgValidateHeader(h); if (Start) { void *head = (void *)h; void *tail = (void *)&CDA_DATA(h, h->m_Length + CDA_OPT_GUARD_BYTES); _ASSERTE((End <= head) || (Start >= tail)); } h = h->m_Next; } }
// Free a packet allocated with DbgAlloc. void __stdcall DbgFree(void *b, void **ppvCallstack, BOOL isArray) { SCAN_IGNORE_FAULT; // tell the static contract analysis tool to ignore FAULTS due to calls of 'new' in code called by this function STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_DEBUG_ONLY; if (!g_DbgEnabled) { if (b) // check for null pointer Win98 doesn't like being // called to free null pointers. ClrFreeInProcessHeap(0, b); return; } // Technically it's possible to get here without having gone through // DbgAlloc (since it's legal to deallocate a NULL pointer), so we // better check for initializtion to be on the safe side. if (!g_HeapInitialized) DbgAllocInit(); CDA_LOCK(); // Check all active packets still look OK. if (g_ConstantRecheck) DbgValidateActivePackets(NULL, NULL); // Count this call to DbgFree. CDA_STATS_INC(Frees); if (b == NULL) { CDA_STATS_INC(NullFrees); CDA_UNLOCK(); return; } // Locate the packet header in front of the data packet. DbgAllocHeader *h = CDA_DATA_TO_HEADER(b); // Verify not calling delete [] on new, and vice versa //_ASSERTE (h->m_IsArray == isArray); // Check that the header looks OK. DbgValidateHeader(h); // Count the total number of bytes we've freed so far. CDA_STATS_ADD(FreeBytes, h->m_Length); // Unlink the packet from the live packet queue. if (h->m_Prev) h->m_Prev->m_Next = h->m_Next; else g_AllocListFirst = h->m_Next; if (h->m_Next) h->m_Next->m_Prev = h->m_Prev; else g_AllocListLast = h->m_Prev; // Zap our link pointers so we'll spot corruption sooner. h->m_Next = (DbgAllocHeader *)(UINT_PTR)CDA_INV_PATTERN; h->m_Prev = (DbgAllocHeader *)(UINT_PTR)CDA_INV_PATTERN; // Zap the tag fields in the header so we'll spot double deallocations // straight away. h->m_Magic1 = CDA_INV_PATTERN; *CDA_MAGIC2(h) = CDA_INV_PATTERN; // Poison the user's data area so that continued access to it after the // deallocation will likely cause an assertion that much sooner. if (g_PoisonPackets) memset(b, CDA_DEALLOC_PATTERN, h->m_Length); // Record the callstack of the deallocator (handy for debugging double // deallocation problems). for (unsigned i = 0; i < g_CallStackDepth; i++) CDA_DEALLOC_STACK(h, i) = ppvCallstack[i]; // put the pack on the free list for a while. Delete the one that it replaces. if (g_PoisonPackets) { DbgAllocHeader* tmp = g_AllocFreeQueue[g_AllocFreeQueueCur]; g_AllocFreeQueue[g_AllocFreeQueueCur] = h; h = tmp; g_AllocFreeQueueCur++; if (g_AllocFreeQueueCur >= g_FreeQueueSize) g_AllocFreeQueueCur = 0; } CDA_UNLOCK(); if (h) { if (g_PagePerAlloc) { // In page per alloc mode we decommit the pages allocated, but leave // them reserved so that we never reuse the same virtual addresses. ClrVirtualFree(h, h->m_Length + CDA_SIZEOF_HEADER() + CDA_OPT_GUARD_BYTES, MEM_DECOMMIT); } else ClrHeapFree(g_HeapHandle, 0, h); } }