Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Fill freed loader heap chunk with non-zero value #12731

Merged
merged 12 commits into from
Jul 31, 2017
23 changes: 13 additions & 10 deletions src/inc/loaderheap.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ class UnlockedLoaderHeap

size_t * m_pPrivatePerfCounter_LoaderBytes;

DWORD m_flProtect;
DWORD m_Options;

LoaderHeapFreeBlock *m_pFirstFreeBlock;

Expand Down Expand Up @@ -288,7 +288,8 @@ class UnlockedLoaderHeap
SIZE_T dwReservedRegionSize,
size_t *pPrivatePerfCounter_LoaderBytes = NULL,
RangeList *pRangeList = NULL,
BOOL fMakeExecutable = FALSE);
BOOL fMakeExecutable = FALSE,
BOOL fZeroInit = TRUE);

~UnlockedLoaderHeap();
#endif
Expand Down Expand Up @@ -398,10 +399,8 @@ class UnlockedLoaderHeap
return m_dwTotalAlloc;
}

BOOL IsExecutable()
{
return (PAGE_EXECUTE_READWRITE == m_flProtect);
}
BOOL IsExecutable();
BOOL IsZeroInit();


public:
Expand Down Expand Up @@ -447,14 +446,16 @@ class LoaderHeap : public UnlockedLoaderHeap, public ILoaderHeapBackout
DWORD dwCommitBlockSize,
size_t *pPrivatePerfCounter_LoaderBytes = NULL,
RangeList *pRangeList = NULL,
BOOL fMakeExecutable = FALSE
BOOL fMakeExecutable = FALSE,
BOOL fZeroInit = TRUE
)
: UnlockedLoaderHeap(dwReserveBlockSize,
dwCommitBlockSize,
NULL, 0,
pPrivatePerfCounter_LoaderBytes,
pRangeList,
fMakeExecutable)
fMakeExecutable,
fZeroInit)
{
WRAPPER_NO_CONTRACT;
m_CriticalSection = NULL;
Expand All @@ -469,15 +470,17 @@ class LoaderHeap : public UnlockedLoaderHeap, public ILoaderHeapBackout
SIZE_T dwReservedRegionSize,
size_t *pPrivatePerfCounter_LoaderBytes = NULL,
RangeList *pRangeList = NULL,
BOOL fMakeExecutable = FALSE
BOOL fMakeExecutable = FALSE,
BOOL fZeroInit = TRUE
)
: UnlockedLoaderHeap(dwReserveBlockSize,
dwCommitBlockSize,
dwReservedRegionAddress,
dwReservedRegionSize,
pPrivatePerfCounter_LoaderBytes,
pRangeList,
fMakeExecutable)
fMakeExecutable,
fZeroInit)
{
WRAPPER_NO_CONTRACT;
m_CriticalSection = NULL;
Expand Down
35 changes: 26 additions & 9 deletions src/utilcode/loaderheap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#define DONOT_DEFINE_ETW_CALLBACK
#include "eventtracebase.h"

#define LHF_EXECUTABLE 0x1
#define LHF_ZEROINIT 0x2

#ifndef DACCESS_COMPILE

INDEBUG(DWORD UnlockedLoaderHeap::s_dwNumInstancesOfLoaderHeaps = 0;)
Expand Down Expand Up @@ -903,7 +906,8 @@ UnlockedLoaderHeap::UnlockedLoaderHeap(DWORD dwReserveBlockSize,
SIZE_T dwReservedRegionSize,
size_t *pPrivatePerfCounter_LoaderBytes,
RangeList *pRangeList,
BOOL fMakeExecutable)
BOOL fMakeExecutable,
BOOL fZeroInit)
{
CONTRACTL
{
Expand Down Expand Up @@ -939,10 +943,11 @@ UnlockedLoaderHeap::UnlockedLoaderHeap(DWORD dwReserveBlockSize,

m_pPrivatePerfCounter_LoaderBytes = pPrivatePerfCounter_LoaderBytes;

m_Options = 0;
if (fMakeExecutable)
m_flProtect = PAGE_EXECUTE_READWRITE;
else
m_flProtect = PAGE_READWRITE;
m_Options |= LHF_EXECUTABLE;
if (fZeroInit)
m_Options |= LHF_ZEROINIT;

m_pFirstFreeBlock = NULL;

Expand Down Expand Up @@ -1133,7 +1138,7 @@ BOOL UnlockedLoaderHeap::UnlockedReservePages(size_t dwSizeToCommit)
}

// Commit first set of pages, since it will contain the LoaderHeapBlock
void *pTemp = ClrVirtualAlloc(pData, dwSizeToCommit, MEM_COMMIT, m_flProtect);
void *pTemp = ClrVirtualAlloc(pData, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
if (pTemp == NULL)
{
//_ASSERTE(!"Unable to ClrVirtualAlloc commit in a loaderheap");
Expand Down Expand Up @@ -1225,7 +1230,7 @@ BOOL UnlockedLoaderHeap::GetMoreCommittedPages(size_t dwMinSize)
dwSizeToCommit = ALIGN_UP(dwSizeToCommit, GetOsPageSize());

// Yes, so commit the desired number of reserved pages
void *pData = ClrVirtualAlloc(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, MEM_COMMIT, m_flProtect);
void *pData = ClrVirtualAlloc(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
if (pData == NULL)
return FALSE;

Expand Down Expand Up @@ -1351,7 +1356,7 @@ void *UnlockedLoaderHeap::UnlockedAllocMem_NoThrow(size_t dwSize
// Don't fill the memory we allocated - it is assumed to be zeroed - fill the memory after it
memset(pAllocatedBytes + dwRequestedSize, 0xEE, LOADER_HEAP_DEBUG_BOUNDARY);
#endif
if (dwRequestedSize > 0)
if ((dwRequestedSize > 0) && (m_Options & LHF_ZEROINIT))
{
_ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0,
"LoaderHeap must return zero-initialized memory");
Expand Down Expand Up @@ -1529,7 +1534,8 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem,
{
// Cool. This was the last block allocated. We can just undo the allocation instead
// of going to the freelist.
memset(pMem, 0, dwSize); // Must zero init this memory as AllocMem expect it
if (m_Options & LHF_ZEROINIT)
memset(pMem, 0x00, dwSize); // Fill freed region with 0
m_pAllocPtr = (BYTE*)pMem;
}
else
Expand Down Expand Up @@ -1639,14 +1645,15 @@ void *UnlockedLoaderHeap::UnlockedAllocAlignedMem_NoThrow(size_t dwRequestedSiz


((BYTE*&)pResult) += extra;

#ifdef _DEBUG
BYTE *pAllocatedBytes = (BYTE *)pResult;
#if LOADER_HEAP_DEBUG_BOUNDARY > 0
// Don't fill the entire memory - we assume it is all zeroed -just the memory after our alloc
memset(pAllocatedBytes + dwRequestedSize, 0xee, LOADER_HEAP_DEBUG_BOUNDARY);
#endif

if (dwRequestedSize != 0)
if ((dwRequestedSize != 0) && (m_Options & LHF_ZEROINIT))
{
_ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0,
"LoaderHeap must return zero-initialized memory");
Expand Down Expand Up @@ -1766,6 +1773,16 @@ void *UnlockedLoaderHeap::UnlockedAllocMemForCode_NoThrow(size_t dwHeaderSize, s

#endif // #ifndef DACCESS_COMPILE

BOOL UnlockedLoaderHeap::IsExecutable()
{
return (m_Options & LHF_EXECUTABLE);
}

BOOL UnlockedLoaderHeap::IsZeroInit()
{
return (m_Options & LHF_ZEROINIT);
}

#ifdef DACCESS_COMPILE

void UnlockedLoaderHeap::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
Expand Down
13 changes: 13 additions & 0 deletions src/vm/amd64/cgenamd64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,19 @@ void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
_ASSERTE(DbgIsExecutable(&m_movR10[0], &m_jmpRAX[3]-&m_movR10[0]));
}

void UMEntryThunkCode::Poison()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;

m_movR10[0] = X86_INSTR_INT3;
}

UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback)
{
LIMITED_METHOD_CONTRACT;
Expand Down
1 change: 1 addition & 0 deletions src/vm/amd64/cgencpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ struct DECLSPEC_ALIGN(8) UMEntryThunkCode
BYTE m_padding2[5];

void Encode(BYTE* pTargetCode, void* pvSecretParam);
void Poison();

LPCBYTE GetEntryPoint() const
{
Expand Down
1 change: 1 addition & 0 deletions src/vm/arm/cgencpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,7 @@ struct DECLSPEC_ALIGN(4) UMEntryThunkCode
TADDR m_pvSecretParam;

void Encode(BYTE* pTargetCode, void* pvSecretParam);
void Poison();

LPCBYTE GetEntryPoint() const
{
Expand Down
6 changes: 6 additions & 0 deletions src/vm/arm/stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2522,6 +2522,12 @@ void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
FlushInstructionCache(GetCurrentProcess(),&m_code,sizeof(m_code));
}

void UMEntryThunkCode::Poison()
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add a comment with instruction that this translates so

// Insert 'bkpt 0x00be' at the entry point
m_code[0] = 0xbebe;
}

///////////////////////////// UNIMPLEMENTED //////////////////////////////////

#ifndef DACCESS_COMPILE
Expand Down
1 change: 1 addition & 0 deletions src/vm/arm64/cgencpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ struct DECLSPEC_ALIGN(16) UMEntryThunkCode
TADDR m_pvSecretParam;

void Encode(BYTE* pTargetCode, void* pvSecretParam);
void Poison();

LPCBYTE GetEntryPoint() const
{
Expand Down
4 changes: 4 additions & 0 deletions src/vm/arm64/stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,10 @@ void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
FlushInstructionCache(GetCurrentProcess(),&m_code,sizeof(m_code));
}

void UMEntryThunkCode::Poison()
{

}

#ifdef PROFILING_SUPPORTED
#include "proftoeeinterfaceimpl.h"
Expand Down
12 changes: 3 additions & 9 deletions src/vm/dllimportcallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1111,13 +1111,8 @@ UMEntryThunk* UMEntryThunk::CreateUMEntryThunk()

UMEntryThunk * p;

#ifdef FEATURE_WINDOWSPHONE
// On the phone, use loader heap to save memory commit of regular executable heap
p = (UMEntryThunk *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(sizeof(UMEntryThunk)));
#else
p = new (executable) UMEntryThunk;
memset (p, 0, sizeof(*p));
#endif

RETURN p;
}
Expand All @@ -1126,11 +1121,10 @@ void UMEntryThunk::Terminate()
{
WRAPPER_NO_CONTRACT;

#ifdef FEATURE_WINDOWSPHONE
_ASSERTE(!SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->IsZeroInit());
m_code.Poison();

SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->BackoutMem(this, sizeof(UMEntryThunk));
#else
DeleteExecutable(this);
#endif
}

VOID UMEntryThunk::FreeUMEntryThunk(UMEntryThunk* p)
Expand Down
4 changes: 0 additions & 4 deletions src/vm/dllimportcallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,10 +326,6 @@ class UMEntryThunk
{
DestroyLongWeakHandle(GetObjectHandle());
}

#ifdef _DEBUG
FillMemory(this, sizeof(*this), 0xcc);
#endif
}

void Terminate();
Expand Down
1 change: 1 addition & 0 deletions src/vm/i386/cgencpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ struct DECLSPEC_ALIGN(4) UMEntryThunkCode
const BYTE * m_execstub; // pointer to destination code // make sure the backpatched portion is dword aligned.

void Encode(BYTE* pTargetCode, void* pvSecretParam);
void Poison();

LPCBYTE GetEntryPoint() const
{
Expand Down
7 changes: 7 additions & 0 deletions src/vm/i386/cgenx86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,13 @@ void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
FlushInstructionCache(GetCurrentProcess(),GetEntryPoint(),sizeof(UMEntryThunkCode));
}

void UMEntryThunkCode::Poison()
{
LIMITED_METHOD_CONTRACT;

m_movEAX = X86_INSTR_INT3;
}

UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback)
{
LIMITED_METHOD_CONTRACT;
Expand Down
4 changes: 3 additions & 1 deletion src/vm/loaderallocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1005,7 +1005,9 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory)
dwExecutableHeapReserveSize,
LOADERHEAP_PROFILE_COUNTER,
NULL,
TRUE /* Make heap executable */);
TRUE /* Make heap executable */,
FALSE /* Disable zero-initialization (needed by UMEntryThunkCode::Poison) */
);
initReservedMem += dwExecutableHeapReserveSize;
}

Expand Down