From 757c186155adf6ec5462efe3c9c2846f54aed1d4 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 23 Jun 2022 07:49:30 -0700 Subject: [PATCH] Fix edge case of code heap initial request When the requested size passed to the LoaderCodeHeap::CreateCodeHeap is less than 16 bytes smaller than the COLLECTIBLE_CODEHEAP_SIZE, we incorrectly try to use the initially preallocated block. The problem is that besides the requested size, we also allocate JUMP_ALLOCATE_SIZE sized block. In the edge case mentioned above, it doesn't fit and we assert in debug builds of the runtime. In release build, it would throw OOM in such case. --- src/coreclr/inc/loaderheap.h | 2 ++ src/coreclr/utilcode/loaderheap.cpp | 26 ++++++++++++-------------- src/coreclr/vm/codeman.cpp | 6 +++++- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/coreclr/inc/loaderheap.h b/src/coreclr/inc/loaderheap.h index 324cf2f161c506..d310d17fb687c9 100644 --- a/src/coreclr/inc/loaderheap.h +++ b/src/coreclr/inc/loaderheap.h @@ -418,6 +418,8 @@ class UnlockedLoaderHeap BOOL IsExecutable(); BOOL IsInterleaved(); + size_t AllocMem_TotalSize(size_t dwRequestedSize); + public: #ifdef _DEBUG void DumpFreeList(); diff --git a/src/coreclr/utilcode/loaderheap.cpp b/src/coreclr/utilcode/loaderheap.cpp index 7638031add7db1..52358bd3f912e9 100644 --- a/src/coreclr/utilcode/loaderheap.cpp +++ b/src/coreclr/utilcode/loaderheap.cpp @@ -667,8 +667,6 @@ class LoaderHeapSniffer #endif -size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHeap); - //===================================================================================== // This freelist implementation is a first cut and probably needs to be tuned. // It should be tuned with the following assumptions: @@ -713,10 +711,10 @@ struct LoaderHeapFreeBlock // It's illegal to insert a free block that's smaller than the minimum sized allocation - // it may stay stranded on the freelist forever. #ifdef _DEBUG - if (!(dwTotalSize >= AllocMem_TotalSize(1, pHeap))) + if (!(dwTotalSize >= pHeap->AllocMem_TotalSize(1))) { LoaderHeapSniffer::ValidateFreeList(pHeap); - _ASSERTE(dwTotalSize >= AllocMem_TotalSize(1, pHeap)); + _ASSERTE(dwTotalSize >= pHeap->AllocMem_TotalSize(1)); } if (!(0 == (dwTotalSize & ALLOC_ALIGN_CONSTANT))) @@ -782,7 +780,7 @@ struct LoaderHeapFreeBlock delete pCur; break; } - else if (dwCurSize > dwSize && (dwCurSize - dwSize) >= AllocMem_TotalSize(1, pHeap)) + else if (dwCurSize > dwSize && (dwCurSize - dwSize) >= pHeap->AllocMem_TotalSize(1)) { // Partial match. Ok... pResult = pCur->m_pBlockAddress; @@ -878,21 +876,21 @@ struct LoaderHeapFreeBlock //===================================================================================== // Convert the requested size into the total # of bytes we'll actually allocate (including padding) -inline size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHeap) +size_t UnlockedLoaderHeap::AllocMem_TotalSize(size_t dwRequestedSize) { LIMITED_METHOD_CONTRACT; size_t dwSize = dwRequestedSize; // Interleaved heap cannot ad any extra to the requested size - if (!pHeap->IsInterleaved()) + if (!IsInterleaved()) { #ifdef _DEBUG dwSize += LOADER_HEAP_DEBUG_BOUNDARY; dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT)); #endif - if (!pHeap->m_fExplicitControl) + if (!m_fExplicitControl) { #ifdef _DEBUG dwSize += sizeof(LoaderHeapValidationTag); @@ -1347,7 +1345,7 @@ BOOL UnlockedLoaderHeap::GetMoreCommittedPages(size_t dwMinSize) // block list. // Otherwise the remaining bytes that are available will be wasted. size_t unusedRemainder = (size_t)(m_pPtrToEndOfCommittedRegion - m_pAllocPtr); - if (unusedRemainder >= AllocMem_TotalSize(m_dwGranularity, this)) + if (unusedRemainder >= AllocMem_TotalSize(m_dwGranularity)) { LoaderHeapFreeBlock::InsertFreeBlock(&m_pFirstFreeBlock, m_pAllocPtr, unusedRemainder, this); } @@ -1440,7 +1438,7 @@ void *UnlockedLoaderHeap::UnlockedAllocMem_NoThrow(size_t dwSize dwSize += s_random.Next() % 256; #endif - dwSize = AllocMem_TotalSize(dwSize, this); + dwSize = AllocMem_TotalSize(dwSize); again: @@ -1624,7 +1622,7 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem, } #endif - size_t dwSize = AllocMem_TotalSize(dwRequestedSize, this); + size_t dwSize = AllocMem_TotalSize(dwRequestedSize); #ifdef _DEBUG if ((m_dwDebugFlags & kCallTracing) && !IsInterleaved()) @@ -1745,7 +1743,7 @@ void *UnlockedLoaderHeap::UnlockedAllocAlignedMem_NoThrow(size_t dwRequestedSiz // know whether the allocation will fit within the current reserved range. // // Thus, we'll request as much heap growth as is needed for the worst case (extra == alignment) - size_t dwRoomSize = AllocMem_TotalSize(dwRequestedSize + alignment, this); + size_t dwRoomSize = AllocMem_TotalSize(dwRequestedSize + alignment); if (dwRoomSize > GetBytesAvailCommittedRegion()) { if (!GetMoreCommittedPages(dwRoomSize)) @@ -1777,7 +1775,7 @@ void *UnlockedLoaderHeap::UnlockedAllocAlignedMem_NoThrow(size_t dwRequestedSiz RETURN NULL; } - size_t dwSize = AllocMem_TotalSize( cbAllocSize.Value(), this); + size_t dwSize = AllocMem_TotalSize( cbAllocSize.Value()); m_pAllocPtr += dwSize; @@ -2126,7 +2124,7 @@ void LoaderHeapSniffer::ValidateFreeList(UnlockedLoaderHeap *pHeap) } size_t dwSize = pFree->m_dwSize; - if (dwSize < AllocMem_TotalSize(1, pHeap) || + if (dwSize < pHeap->AllocMem_TotalSize(1) || 0 != (dwSize & ALLOC_ALIGN_CONSTANT)) { // Size is not a valid value (out of range or unaligned.) diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 2710d8c5c9ca25..7ee4f99c54c65b 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -2517,7 +2517,11 @@ HeapList* LoaderCodeHeap::CreateCodeHeap(CodeHeapRequestInfo *pInfo, LoaderHeap DWORD dwSizeAcquiredFromInitialBlock = 0; bool fAllocatedFromEmergencyJumpStubReserve = false; - pBaseAddr = (BYTE *)pInfo->m_pAllocator->GetCodeHeapInitialBlock(loAddr, hiAddr, (DWORD)initialRequestSize, &dwSizeAcquiredFromInitialBlock); + size_t allocationSize = pCodeHeap->m_LoaderHeap.AllocMem_TotalSize(initialRequestSize); +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) + allocationSize += pCodeHeap->m_LoaderHeap.AllocMem_TotalSize(JUMP_ALLOCATE_SIZE); +#endif + pBaseAddr = (BYTE *)pInfo->m_pAllocator->GetCodeHeapInitialBlock(loAddr, hiAddr, (DWORD)allocationSize, &dwSizeAcquiredFromInitialBlock); if (pBaseAddr != NULL) { pCodeHeap->m_LoaderHeap.SetReservedRegion(pBaseAddr, dwSizeAcquiredFromInitialBlock, FALSE);