Skip to content

Commit 47c9a09

Browse files
authored
Fix a potential memory leak due to EventPipe buffer allocation (#35924)
* simplest fix * make the buffer size page-aligned * CR feedback * Add comment about memset
1 parent d1e7d7b commit 47c9a09

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

src/coreclr/src/vm/eventpipebuffer.cpp

+15-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,21 @@ EventPipeBuffer::EventPipeBuffer(unsigned int bufferSize, EventPipeThread* pWrit
2323
m_state = EventPipeBufferState::WRITABLE;
2424
m_pWriterThread = pWriterThread;
2525
m_eventSequenceNumber = eventSequenceNumber;
26-
m_pBuffer = new BYTE[bufferSize];
26+
// Use ClrVirtualAlloc instead of malloc to allocate buffer to avoid potential internal fragmentation in the native CRT heap.
27+
// (See https://github.com/dotnet/runtime/pull/35924 and https://github.com/microsoft/ApplicationInsights-dotnet/issues/1678 for more details)
28+
//
29+
// This fix does cause a little bit of performance regression (1-2%) in throughput,
30+
// but within acceptable boundaries, while minimizing the risk of the fix to be backported
31+
// to servicing releases. We may come back in the future to reassess this and potentially improve
32+
// the throughput via more performant solution afterwards.
33+
m_pBuffer = (BYTE*)ClrVirtualAlloc(NULL, bufferSize, MEM_COMMIT, PAGE_READWRITE);
34+
35+
// memset may be unnecessary here because VirtualAlloc with MEM_COMMIT zero-initializes the pages and mmap also zero-initializes
36+
// if MAP_UNINITIALIZED isn't passed (which ClrVirtualAlloc doesn't). If this memset ends up being a perf cost in future investigations
37+
// we may remove this. But for risk mitigation we're leaving it as-is.
38+
// (See https://github.com/dotnet/runtime/pull/35924#discussion_r421282564 for discussion on this)
2739
memset(m_pBuffer, 0, bufferSize);
40+
2841
m_pLimit = m_pBuffer + bufferSize;
2942
m_pCurrent = GetNextAlignedAddress(m_pBuffer);
3043

@@ -47,7 +60,7 @@ EventPipeBuffer::~EventPipeBuffer()
4760
}
4861
CONTRACTL_END;
4962

50-
delete[] m_pBuffer;
63+
ClrVirtualFree(m_pBuffer, 0, MEM_RELEASE);
5164
}
5265

5366
bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeSession &session, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack)

src/coreclr/src/vm/eventpipebuffermanager.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(EventPipeThread
168168
const unsigned int maxBufferSize = 1024 * 1024;
169169
bufferSize = Min(bufferSize, maxBufferSize);
170170

171+
// Make the buffer size fit into with pagesize-aligned block, since ClrVirtualAlloc expects page-aligned sizes to be passed as arguments (see ctor of EventPipeBuffer)
172+
bufferSize = (bufferSize + g_SystemInfo.dwAllocationGranularity - 1) & ~static_cast<unsigned int>(g_SystemInfo.dwAllocationGranularity - 1);
173+
171174
// EX_TRY is used here as opposed to new (nothrow) because
172175
// the constructor also allocates a private buffer, which
173176
// could throw, and cannot be easily checked

0 commit comments

Comments
 (0)