summaryrefslogtreecommitdiff
path: root/src/ipcman
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
committerdotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
commitef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch)
treedee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/ipcman
downloadcoreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/ipcman')
-rw-r--r--src/ipcman/.gitmirror1
-rw-r--r--src/ipcman/CMakeLists.txt14
-rw-r--r--src/ipcman/dirs.proj19
-rw-r--r--src/ipcman/ipcenums.h93
-rw-r--r--src/ipcman/ipcfunccallimpl.cpp654
-rw-r--r--src/ipcman/ipcheader.h561
-rw-r--r--src/ipcman/ipcheader.inl91
-rw-r--r--src/ipcman/ipcman-staticcrt/.gitmirror1
-rw-r--r--src/ipcman/ipcman-staticcrt/CMakeLists.txt2
-rw-r--r--src/ipcman/ipcman-staticcrt/dirs.proj17
-rw-r--r--src/ipcman/ipcman-staticcrt/ipcman-staticcrt.props11
-rw-r--r--src/ipcman/ipcmanager.props28
-rw-r--r--src/ipcman/ipcmanagerimpl.h148
-rw-r--r--src/ipcman/ipcmanagerimpl.inl193
-rw-r--r--src/ipcman/ipcreaderimpl.cpp1114
-rw-r--r--src/ipcman/ipcshared.h95
-rw-r--r--src/ipcman/ipcsharedsrc.cpp966
-rw-r--r--src/ipcman/ipcwriterimpl.cpp918
-rw-r--r--src/ipcman/stdafx.cpp11
-rw-r--r--src/ipcman/stdafx.h30
20 files changed, 4967 insertions, 0 deletions
diff --git a/src/ipcman/.gitmirror b/src/ipcman/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ipcman/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ipcman/CMakeLists.txt b/src/ipcman/CMakeLists.txt
new file mode 100644
index 0000000000..6213fc462c
--- /dev/null
+++ b/src/ipcman/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+add_definitions(-DFEATURE_NO_HOST)
+
+set(IPCMAN_SOURCES
+ ipcsharedsrc.cpp
+ ipcreaderimpl.cpp
+ ipcwriterimpl.cpp
+ ipcfunccallimpl.cpp
+)
+
+convert_to_absolute_path(IPCMAN_SOURCES ${IPCMAN_SOURCES})
+
+add_subdirectory(ipcman-staticcrt)
diff --git a/src/ipcman/dirs.proj b/src/ipcman/dirs.proj
new file mode 100644
index 0000000000..04a7daa547
--- /dev/null
+++ b/src/ipcman/dirs.proj
@@ -0,0 +1,19 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Include="ipcman\ipcman.nativeproj" />
+ <ProjectFile Include="ipcman-staticcrt\dirs.proj" />
+ <ProjectFile Include="ipcman_dac\dirs.proj" />
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/ipcman/ipcenums.h b/src/ipcman/ipcenums.h
new file mode 100644
index 0000000000..99bbe341e8
--- /dev/null
+++ b/src/ipcman/ipcenums.h
@@ -0,0 +1,93 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+//*****************************************************************************
+// File: IPCEnums.h
+//
+// Define various enums used by IPCMan
+//
+//*****************************************************************************
+
+#ifndef _IPCEnums_h_
+#define _IPCEnums_h_
+
+
+
+//enum for factoring security descriptor stuff...
+enum EDescriptorType
+{
+ eDescriptor_Private = 0,
+ eDescriptor_Public,
+
+// MAX used for arrays, insert above this.
+ eDescriptor_MAX
+};
+
+
+//-----------------------------------------------------------------------------
+// Each IPC client for an IPC block has one entry.
+// IMPORTANT: Do not remove any entries from this enumeration as
+// the directory indexing cannot change from version to version
+// in order to preserve compatibility. The actual directory
+// entry for an obsolete index can be zeroed (which all users
+// should be written to handle for forward compatibility) but
+// this enumeration can only be appended to.
+//-----------------------------------------------------------------------------
+enum EIPCClient
+{
+ eIPC_PerfCounters = 0,
+
+// MAX used for arrays, insert above this.
+ eIPC_MAX
+};
+
+//-----------------------------------------------------------------------------
+// Each IPC client for a LegacyPrivate block (debugging, perf counters, etc)
+// has one entry.
+// IMPORTANT: Do not remove any entries from this enumeration as
+// the directory indexing cannot change from version to version
+// in order to preserve compatibility. The actual directory
+// entry for an obsolete index can be zeroed (which all users
+// should be written to handle for forward compatibility) but
+// this enumeration can only be appended to.
+//-----------------------------------------------------------------------------
+enum ELegacyPrivateIPCClient
+{
+ eLegacyPrivateIPC_PerfCounters = 0,
+ eLegacyPrivateIPC_Obsolete_Debugger,
+ eLegacyPrivateIPC_AppDomain,
+ eLegacyPrivateIPC_Obsolete_Service,
+ eLegacyPrivateIPC_Obsolete_ClassDump,
+ eLegacyPrivateIPC_Obsolete_MiniDump,
+ eLegacyPrivateIPC_InstancePath,
+
+// MAX used for arrays, insert above this.
+ eLegacyPrivateIPC_MAX
+};
+
+//-----------------------------------------------------------------------------
+// Each IPC client for a LegacyPublic block has one entry.
+// IMPORTANT: Do not remove any entries from this enumeration as
+// the directory indexing cannot change from version to version
+// in order to preserve compatibility. The actual directory
+// entry for an obsolete index can be zeroed (which all users
+// should be written to handle for forward compatibility) but
+// this enumeration can only be appended to.
+//-----------------------------------------------------------------------------
+enum ELegacyPublicIPCClient
+{
+ eLegacyPublicIPC_PerfCounters = 0,
+
+// MAX used for arrays, insert above this.
+ eLegacyPublicIPC_MAX
+};
+
+#endif // _IPCEnums_h_
+
diff --git a/src/ipcman/ipcfunccallimpl.cpp b/src/ipcman/ipcfunccallimpl.cpp
new file mode 100644
index 0000000000..6625386b72
--- /dev/null
+++ b/src/ipcman/ipcfunccallimpl.cpp
@@ -0,0 +1,654 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+//*****************************************************************************
+// File: IPCFuncCallImpl.cpp
+//
+// Implement support for a cross process function call.
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "ipcfunccall.h"
+#include "ipcshared.h"
+
+#if defined(FEATURE_PERFMON) && defined(FEATURE_IPCMAN)
+
+// #define ENABLE_TIMING
+
+#ifdef ENABLE_TIMING
+#include "timer.h"
+CTimer g_time;
+#endif // ENABLE_TIMING
+
+//-----------------------------------------------------------------------------
+// <TODO>@todo: This is very generic. However, If we want to support multiple
+// functions calls, we will have to decorate the event object names.</TODO>
+//-----------------------------------------------------------------------------
+
+#define NamePrexix L"Global\\CLR_"
+
+// Name of sync objects
+#define StartEnumEventName NamePrexix L"PerfMon_StartEnumEvent"
+#define DoneEnumEventName NamePrexix L"PerfMon_DoneEnumEvent"
+#define WrapMutexName NamePrexix L"PerfMon_WrapMutex"
+
+// Time the Source Caller is willing to wait for Handler to finish
+// Note, a nefarious handler can at worst case make caller
+// wait twice the delay below.
+const DWORD START_ENUM_TIMEOUT = 500; // time out in milliseconds
+
+//-----------------------------------------------------------------------------
+// Wrap an unsafe call in a mutex to assure safety
+// Biggest error issues are:
+// 1. Timeout (probably handler doesn't exist)
+// 2. Handler can be destroyed at any time.
+//-----------------------------------------------------------------------------
+IPCFuncCallSource::EError IPCFuncCallSource::DoThreadSafeCall()
+{
+ WRAPPER_NO_CONTRACT;
+
+ DWORD dwDesiredAccess;
+ DWORD dwErr;
+ EError err = Ok;
+
+#if defined(ENABLE_TIMING)
+ g_time.Reset();
+ g_time.Start();
+#endif
+
+ dwDesiredAccess = EVENT_MODIFY_STATE;
+
+ HANDLE hStartEnum = NULL;
+ HANDLE hDoneEnum = NULL;
+ HANDLE hWrapCall = NULL;
+ DWORD dwWaitRet;
+
+ // Check if we have a handler (handler creates the events) and
+ // abort if not. Do this check asap to optimize the most common
+ // case of no handler.
+ hStartEnum = WszOpenEvent(dwDesiredAccess,
+ FALSE,
+ StartEnumEventName);
+ if (hStartEnum == NULL)
+ {
+ dwErr = GetLastError();
+ err = Fail_NoHandler;
+ goto errExit;
+ }
+
+ hDoneEnum = WszOpenEvent(dwDesiredAccess,
+ FALSE,
+ DoneEnumEventName);
+ if (hDoneEnum == NULL)
+ {
+ dwErr = GetLastError();
+ err = Fail_NoHandler;
+ goto errExit;
+ }
+
+ // Need to create the mutex
+ hWrapCall = WszCreateMutex(NULL, FALSE, WrapMutexName);
+ if (hWrapCall == NULL)
+ {
+ dwErr = GetLastError();
+ err = Fail_CreateMutex;
+ goto errExit;
+ }
+
+
+// Wait for our turn
+ dwWaitRet = WaitForSingleObject(hWrapCall, START_ENUM_TIMEOUT);
+ dwErr = GetLastError();
+ switch(dwWaitRet) {
+ case WAIT_OBJECT_0:
+ // Good case. All other cases are errors and goto errExit.
+ break;
+
+ case WAIT_TIMEOUT:
+ err = Fail_Timeout_Lock;
+ goto errExit;
+ break;
+ default:
+ err = Failed;
+ goto errExit;
+ break;
+ }
+
+ // Our turn: Make the function call
+ {
+ BOOL fSetOK = 0;
+
+ // Reset the 'Done event' to make sure that Handler sets it after they start.
+ fSetOK = ResetEvent(hDoneEnum);
+ _ASSERTE(fSetOK);
+ dwErr = GetLastError();
+
+ // Signal Handler to execute callback
+ fSetOK = SetEvent(hStartEnum);
+ _ASSERTE(fSetOK);
+ dwErr = GetLastError();
+
+ // Now wait for handler to finish.
+
+ dwWaitRet = WaitForSingleObject(hDoneEnum, START_ENUM_TIMEOUT);
+ dwErr = GetLastError();
+ switch (dwWaitRet)
+ {
+ case WAIT_OBJECT_0:
+ break;
+ case WAIT_TIMEOUT:
+ err = Fail_Timeout_Call;
+ break;
+ default:
+ err = Failed;
+ break;
+ }
+
+
+ BOOL fMutexOk;
+ fMutexOk = ReleaseMutex(hWrapCall);
+ _ASSERTE(fMutexOk);
+ dwErr = GetLastError();
+
+ } // End function call
+
+
+
+errExit:
+// Close all handles
+ if (hStartEnum != NULL)
+ {
+ CloseHandle(hStartEnum);
+ hStartEnum = NULL;
+
+ }
+ if (hDoneEnum != NULL)
+ {
+ CloseHandle(hDoneEnum);
+ hDoneEnum = NULL;
+ }
+ if (hWrapCall != NULL)
+ {
+ CloseHandle(hWrapCall);
+ hWrapCall = NULL;
+ }
+
+#if defined(ENABLE_TIMING)
+ g_time.End();
+ DWORD dwTime = g_time.GetEllapsedMS();
+#endif
+
+
+ return err;
+
+}
+
+
+// Reset vars so we can be sure that Init was called
+IPCFuncCallHandler::IPCFuncCallHandler()
+{
+ m_hStartEnum = NULL; // event to notify start call
+ m_hDoneEnum = NULL; // event to notify end call
+ m_hAuxThread = NULL; // thread to listen for m_hStartEnum
+ m_pfnCallback = NULL; // Callback handler
+ m_pfnCleanupCallback = NULL; // Cleanup callback handler
+ m_fShutdownAuxThread = FALSE;
+ m_hShutdownThread = NULL;
+ m_hCallbackModule = NULL; // module in which the aux thread's start function lives
+}
+
+IPCFuncCallHandler::~IPCFuncCallHandler()
+{
+ // If Terminate was not called then do so now. This should have been
+ // called from CloseCtrs perf counters API. But in Windows XP this order is
+ // not guaranteed.
+ TerminateFCHandler();
+}
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning (disable: 6320) //We handle ALL exceptions so that the host process doesnt die
+#endif
+
+//-----------------------------------------------------------------------------
+// Thread callback
+//-----------------------------------------------------------------------------
+DWORD WINAPI HandlerAuxThreadProc(
+ LPVOID lpParameter // thread data
+)
+{
+ IPCFuncCallHandler * pHandler = (IPCFuncCallHandler *) lpParameter;
+
+ struct Param
+ {
+ IPCFuncCallHandler * pHandler;
+ } param;
+ param.pHandler = pHandler;
+
+ PAL_TRY(Param *, pParam, &param)
+ {
+ HANDLER_CALLBACK pfnCallback = pParam->pHandler->m_pfnCallback;
+
+ DWORD dwErr = 0;
+ DWORD dwWaitRet;
+
+ HANDLE lpHandles[] = {pParam->pHandler->m_hShutdownThread, pParam->pHandler->m_hStartEnum};
+ DWORD dwHandleCount = 2;
+
+ do {
+ dwWaitRet = WaitForMultipleObjects(dwHandleCount, lpHandles, FALSE /*Wait Any*/, INFINITE);
+ dwErr = GetLastError();
+
+ // If we are in terminate mode then exit this helper thread.
+ if (pParam->pHandler->m_fShutdownAuxThread)
+ break;
+
+ // Keep the 0th index for the terminate thread so that we never miss it
+ // in case of multiple events. note that the ShutdownAuxThread flag above it purely
+ // to protect us against some bug in waitForMultipleObjects.
+ if ((dwWaitRet-WAIT_OBJECT_0) == 0)
+ break;
+
+ // execute callback if wait succeeded
+ if ((dwWaitRet-WAIT_OBJECT_0) == 1)
+ {
+ (*pfnCallback)();
+
+ // reset manual event
+ BOOL fResetOK;
+ fResetOK = ResetEvent(pParam->pHandler->m_hStartEnum);
+ _ASSERTE(fResetOK);
+ dwErr = GetLastError();
+
+ BOOL fSetOK;
+ fSetOK = SetEvent(pParam->pHandler->m_hDoneEnum);
+ _ASSERTE(fSetOK);
+ dwErr = GetLastError();
+ }
+ } while (dwWaitRet != WAIT_FAILED);
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ WCHAR wszMsg[128];
+ swprintf_s(wszMsg, COUNTOF(wszMsg), L"HandlerAuxThreadProc caught exception %x", GetExceptionCode());
+ ClrReportEvent(L".NET Runtime",
+ EVENTLOG_ERROR_TYPE,
+ 0,
+ 0,
+ NULL,
+ wszMsg);
+ }
+ PAL_ENDTRY
+
+
+ pHandler->SafeCleanup();
+
+ HMODULE hCallbackModule = pHandler->m_hCallbackModule;
+ pHandler->m_hCallbackModule = NULL;
+
+ pHandler->m_fShutdownAuxThread = FALSE;
+
+ // Close the thread's handle and clear the shut down flag. Note the order here is very tricky
+ // to avoid a race. We must set the shutdown flag first to ensure that once m_hAuxThread is set
+ // to NULL no further modification happens to pHandler.
+ HANDLE hThread = InterlockedExchangeT(&pHandler->m_hAuxThread, NULL);
+
+ // If hThread was null then WaitForCompletion will close it.
+ if (hThread != NULL)
+ CloseHandle(hThread);
+
+ FreeLibraryAndExitThread (hCallbackModule, 0);
+ // Above call doesn't return
+
+ return 0;
+}
+
+#ifdef _MSC_VER
+#pragma warning (pop) //6320
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Receieves the call. This should be in a different process than the source
+//-----------------------------------------------------------------------------
+HRESULT IPCFuncCallHandler::InitFCHandler(HANDLER_CALLBACK pfnCallback, HANDLER_CALLBACK pfnCleanupCallback)
+{
+ // If the thread is still in the process of shutting down then
+ // we have to fail.
+ if (!IsShutdownComplete())
+ {
+ _ASSERTE(!"shutdown should have completed before calling this function");
+ return E_FAIL;
+ }
+
+ m_pfnCallback = pfnCallback;
+ m_pfnCleanupCallback = pfnCleanupCallback;
+
+ HRESULT hr = NOERROR;
+ DWORD dwThreadId;
+ DWORD dwErr = 0;
+ DWORD dwDesiredAccess;
+ DWORD dwRet = 0;
+ HANDLE hToken = NULL;
+
+ SetLastError(0);
+
+ // Grab the SA
+ DWORD dwPid = 0;
+ SECURITY_ATTRIBUTES *pSA = NULL;
+
+ dwDesiredAccess = EVENT_MODIFY_STATE | SYNCHRONIZE;
+
+ dwPid = GetCurrentProcessId();
+ hr = IPCShared::CreateWinNTDescriptor(dwPid, FALSE, &pSA, Event, eDescriptor_Public);
+
+ if (FAILED(hr))
+ goto errExit;;
+
+ // try to open event first (another process may already have created one)
+ m_hStartEnum = WszOpenEvent(dwDesiredAccess,
+ FALSE,
+ L"Global\\" StartEnumEventName);
+ if (m_hStartEnum == NULL)
+ {
+ // Create the StartEnum Event
+ m_hStartEnum = WszCreateEvent(pSA,
+ TRUE, // manual event for multiple instances of corperfmonext.dll
+ FALSE,
+ StartEnumEventName);
+ }
+
+ if (m_hStartEnum == NULL)
+ {
+ dwErr = GetLastError();
+ hr = HRESULT_FROM_WIN32(dwErr);
+ goto errExit;
+ }
+
+ // try to open event first (another process may already have created one)
+ m_hDoneEnum = WszOpenEvent(dwDesiredAccess,
+ FALSE,
+ L"Global\\" DoneEnumEventName);
+
+ if (m_hDoneEnum == NULL)
+ {
+ // Create the EndEnumEvent
+ m_hDoneEnum = WszCreateEvent(pSA,
+ TRUE, // manual event for multiple instances of corperfmonext.dll
+ FALSE,
+ DoneEnumEventName);
+ }
+ if (m_hDoneEnum == NULL)
+ {
+ dwErr = GetLastError();
+ hr = HRESULT_FROM_WIN32(dwErr);
+ goto errExit;
+ }
+
+ // Create the ShutdownThread Event
+ m_hShutdownThread = WszCreateEvent(pSA,
+ TRUE, /* Manual Reset */
+ FALSE, /* Initial state not signalled */
+ NULL);
+
+ dwErr = GetLastError();
+ if (m_hShutdownThread == NULL)
+ {
+ hr = HRESULT_FROM_WIN32(dwErr);
+ goto errExit;
+ }
+
+ BOOL bSuccess = FALSE;
+
+ // Get current thread token with duplicate and impersonation access
+ // Will use this token for polling thread impersonation if current
+ // thread is impersonating
+ bSuccess = OpenThreadToken(
+ GetCurrentThread(),
+ TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE,
+ TRUE,
+ &hToken
+ );
+
+ dwErr = GetLastError();
+ // token won't exist if running local becase we are not impersonating
+ if (FALSE == bSuccess && ERROR_NO_TOKEN != dwErr)
+ {
+ hr = HRESULT_FROM_WIN32(dwErr);
+ goto errExit;
+ }
+
+ // at this point, we should either have a valid token or we failed
+ // to get the token because one does not exist on this thread
+ _ASSERTE(NULL != hToken || ERROR_NO_TOKEN == dwErr);
+
+ // The thread that we are about to create should always
+ // find the code in memory. So we take a ref on the DLL.
+ // and do a free library at the end of the thread's start function
+ m_hCallbackModule = WszLoadLibrary (L"CorPerfmonExt.dll");
+
+ dwErr = GetLastError();
+ if (m_hCallbackModule == NULL)
+ {
+ hr = HRESULT_FROM_WIN32(dwErr);
+ goto errExit;
+ }
+
+ // Create thread suspended so we can set impersonation token
+ m_hAuxThread = CreateThread(
+ NULL,
+ 0,
+ HandlerAuxThreadProc,
+ this,
+ CREATE_SUSPENDED,
+ &dwThreadId);
+
+ dwErr = GetLastError();
+ if (m_hAuxThread.Load() == NULL)
+ {
+ hr = HRESULT_FROM_WIN32(dwErr);
+
+ // In case of an error free this library here otherwise
+ // the thread's exit would take care of it.
+ if (m_hCallbackModule)
+ FreeLibrary (m_hCallbackModule);
+ goto errExit;
+ }
+
+ // If we got a token for the current thread,
+ // set token on new thread
+ if (NULL != hToken)
+ {
+ bSuccess = SetThreadToken((PHANDLE)m_hAuxThread.GetPointer(), hToken);
+
+ dwErr = GetLastError();
+ if (FALSE == bSuccess)
+ {
+ hr = HRESULT_FROM_WIN32(dwErr);
+ goto errExit;
+ }
+ }
+
+ // Resume the newly created thread
+ dwRet = ResumeThread(m_hAuxThread);
+
+ dwErr = GetLastError();
+ if (dwRet == (DWORD)(-1))
+ {
+ hr = HRESULT_FROM_WIN32(dwErr);
+ goto errExit;
+ }
+
+ _ASSERTE(1 == dwRet);
+
+errExit:
+ if (NULL != hToken)
+ {
+ CloseHandle(hToken);
+ }
+
+ if (!SUCCEEDED(hr))
+ {
+ TerminateFCHandler();
+ }
+
+ if (pSA != NULL)
+ {
+ IPCShared::DestroySecurityAttributes( pSA );
+ }
+ return hr;
+
+}
+
+//-----------------------------------------------------------------------------
+// Close all our handles
+//-----------------------------------------------------------------------------
+void IPCFuncCallHandler::SafeCleanup()
+{
+ // Call the cleanup callback
+
+ if (m_pfnCleanupCallback != NULL)
+ {
+ struct Param
+ {
+ IPCFuncCallHandler * pHandler;
+ } param;
+ param.pHandler = this;
+
+ PAL_TRY(Param *, pParam, &param)
+ {
+ HANDLER_CALLBACK pfnCleanupCallback = pParam->pHandler->m_pfnCleanupCallback;
+
+ (*pfnCleanupCallback)();
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ WCHAR wszMsg[128];
+ swprintf_s(wszMsg, COUNTOF(wszMsg), L"HandlerAuxThreadProc caught exception %x", GetExceptionCode());
+ ClrReportEvent(L".NET Runtime",
+ EVENTLOG_ERROR_TYPE,
+ 0,
+ 0,
+ NULL,
+ wszMsg);
+ }
+ PAL_ENDTRY
+ }
+
+
+ // Release all the handles
+
+ if (m_hStartEnum != NULL)
+ {
+ CloseHandle(m_hStartEnum);
+ m_hStartEnum = NULL;
+ }
+
+ if (m_hDoneEnum != NULL)
+ {
+ CloseHandle(m_hDoneEnum);
+ m_hDoneEnum = NULL;
+ }
+
+ if (m_hShutdownThread != NULL)
+ {
+ CloseHandle(m_hShutdownThread);
+ m_hShutdownThread = NULL;
+ }
+
+ m_pfnCallback = NULL;
+ m_pfnCleanupCallback = NULL;
+}
+
+void IPCFuncCallHandler::TerminateFCHandler()
+{
+ // If the thread is in the process of shutting down then
+ // there is nothing to do
+ if (m_fShutdownAuxThread)
+ {
+ return;
+ }
+
+ // If this IPCFuncCallHandler has not been initialized yet
+ // then there is nothing to do
+ if ((m_hStartEnum == NULL) &&
+ (m_hDoneEnum == NULL) &&
+ (m_hAuxThread.Load() == NULL) &&
+ (m_pfnCallback == NULL))
+ {
+ return;
+ }
+
+ if(m_hAuxThread.Load() != NULL)
+ {
+ // Always resume the thread to make sure it is not suspended
+ if (ResumeThread(m_hAuxThread) == (DWORD)(-1))
+ {
+ _ASSERTE (!"TerminateFCHandler: ResumeThread(m_hAuxThread) failed");
+ }
+
+ // First make sure that we make the aux thread gracefully exit
+ m_fShutdownAuxThread = TRUE;
+
+ // Hope that this set event makes the thread quit.
+ if (!SetEvent (m_hShutdownThread))
+ {
+ _ASSERTE (!"TerminateFCHandler: SetEvent(m_hShutdownThread) failed");
+ }
+ }
+ else
+ {
+ // We failed during InitFCHandler before creating the auxilliary thread
+ SafeCleanup();
+
+ }
+
+ // The aux thread is responsible for cleanup. When it is finished cleaning
+ // up it will set m_fShutdownAuxThread to FALSE.
+}
+
+BOOL IPCFuncCallHandler::IsShutdownComplete()
+{
+ return m_hAuxThread.Load() == NULL;
+}
+
+void IPCFuncCallHandler::WaitForShutdown()
+{
+ // Check to see if the thread handle is null. If it is then the thread has shut down,
+ // otherwise we will wait for the thread to exit.
+ HANDLE hThread = InterlockedExchangeT(&m_hAuxThread, NULL);
+
+ if (hThread != NULL)
+ {
+ // Otherwise wait for the thread to complete and we close its handle.
+ DWORD result = WaitForSingleObject(hThread, INFINITE);
+ _ASSERTE(result == WAIT_OBJECT_0);
+
+ CloseHandle(hThread);
+ }
+}
+#else // !FEATURE_PERFMON || !FEATURE_IPCMAN
+
+
+// Telesto stubs
+
+//-----------------------------------------------------------------------------
+// Wrap an unsafe call in a mutex to assure safety
+// Biggest error issues are:
+// 1. Timeout (probably handler doesn't exist)
+// 2. Handler can be destroyed at any time.
+//-----------------------------------------------------------------------------
+IPCFuncCallSource::EError IPCFuncCallSource::DoThreadSafeCall()
+{
+ return Ok;
+}
+
+#endif // FEATURE_PERFMON && FEATURE_IPCMAN
diff --git a/src/ipcman/ipcheader.h b/src/ipcman/ipcheader.h
new file mode 100644
index 0000000000..98e940699e
--- /dev/null
+++ b/src/ipcman/ipcheader.h
@@ -0,0 +1,561 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+//*****************************************************************************
+// File: IPCHeader.h
+//
+// Define the LegacyPrivate header format for COM+ memory mapped files. Everyone
+// outside of IPCMan.lib will use the public header, IPCManagerInterface.h
+//
+//*****************************************************************************
+
+#ifndef _IPCManagerPriv_h_
+#define _IPCManagerPriv_h_
+
+
+//-----------------------------------------------------------------------------
+// We must pull in the headers of all client blocks
+// <TODO>@todo - resolve these directory links</TODO>
+//-----------------------------------------------------------------------------
+#include "../debug/inc/dbgappdomain.h"
+#include "perfcounterdefs.h"
+
+#include "ndpversion.h"
+#include "ipcenums.h"
+
+//-----------------------------------------------------------------------------
+// Entry in the IPC Directory. Ensure binary compatibility across versions
+// if we add (or remove) entries. If we remove an block, the entry should
+// be EMPTY_ENTRY_OFFSET
+//-----------------------------------------------------------------------------
+
+const DWORD EMPTY_ENTRY_OFFSET = 0xFFFFFFFF;
+const DWORD EMPTY_ENTRY_SIZE = 0;
+
+struct IPCEntry
+{
+ DWORD m_Offset; // offset of the IPC Block from the end of the Full IPC Header
+ DWORD m_Size; // size (in bytes) of the block
+};
+
+// Newer versions of the CLR use m_Flags field
+const USHORT IPC_FLAG_USES_FLAGS = 0x1;
+const USHORT IPC_FLAG_INITIALIZED = 0x2;
+const USHORT IPC_FLAG_X86 = 0x4;
+
+/******************************************************************************
+ * The CLR opens memory mapped files to expose perfcounter values and other
+ * information to other processes. Historically there have been three memory
+ * mapped files: the BlockTable, the LegacyPrivateBlock, and the
+ * LegacyPublicBlock.
+ *
+ * BlockTable - The block table was designed to work with multiple runtimes
+ * running side by side in the same process (SxS in-proc). We have defined
+ * semantics using interlocked operations that allow a runtime to allocate
+ * a block from the block table in a thread-safe manner.
+ *
+ * LegacyPrivateBlock - The legacy private block was used by older versions
+ * of the runtime to expose various information to the debugger. The
+ * legacy private block is not compatible with in-proc SxS, and thus it
+ * must be removed in the near future. Currently it is being used to expose
+ * information about AppDomains to the debugger. We will need to keep the
+ * code that knows how to read the legacy private block as long as we
+ * continue to support .NET 3.5 SP1.
+ *
+ * LegacyPublicBlock - The legacy public block was used by older versions
+ * of the runtime to expose perfcounter values. The legacy public block is
+ * not compatible with in-proc SxS, and thus it has been removed. We will
+ * need to keep the code that knows how to read the legacy public block as
+ * long as we continue to support .NET 3.5 SP1.
+ ******************************************************************************/
+
+/**************************************** BLOCK TABLE ****************************************/
+
+const DWORD IPC_BLOCK_TABLE_SIZE = 65536;
+const DWORD IPC_BLOCK_SIZE = 2048;
+const DWORD IPC_NUM_BLOCKS_IN_TABLE = 32;
+
+static_assert_no_msg(IPC_BLOCK_TABLE_SIZE == IPC_NUM_BLOCKS_IN_TABLE * IPC_BLOCK_SIZE);
+
+#if defined(TIA64)
+#define IA64MemoryBarrier() MemoryBarrier()
+#else
+#define IA64MemoryBarrier()
+#endif
+
+struct IPCHeader
+{
+ // Chunk header
+ Volatile<LONG> m_Counter; // value of 0 is special; means that this block has never been touched before by a writer
+ DWORD m_RuntimeId; // value of 0 is special; means that chunk is currently free (runtime ids are always greater than 0)
+ DWORD m_Reserved1;
+ DWORD m_Reserved2;
+
+ // Standard header (m_hInstance was removed)
+ USHORT m_Version; // version of the IPC Block
+ USHORT m_Flags; // flags field
+ DWORD m_blockSize; // Size of the entire shared memory block
+ USHORT m_BuildYear; // stamp for year built
+ USHORT m_BuildNumber; // stamp for Month/Day built
+ DWORD m_numEntries; // Number of entries in the table
+
+ // Directory
+ IPCEntry m_table[eIPC_MAX]; // entry describing each client's block
+};
+
+extern BOOL __SwitchToThread(DWORD dwSleepMSec, DWORD dwSwitchCount);
+
+class IPCHeaderLockHolder
+{
+ LONG m_Counter;
+ BOOL m_HaveLock;
+ IPCHeader & m_Header;
+
+ public:
+
+ IPCHeaderLockHolder(IPCHeader & header) : m_HaveLock(FALSE), m_Header(header) {}
+
+ BOOL TryGetLock()
+ {
+ _ASSERTE(!m_HaveLock);
+ LONG oldCounter = m_Header.m_Counter;
+ if ((oldCounter & 1) != 0)
+ return FALSE;
+ m_Counter = oldCounter + 1;
+ if (InterlockedCompareExchange((LONG *)(&(m_Header.m_Counter)), m_Counter, oldCounter) != oldCounter)
+ return FALSE;
+ m_HaveLock = TRUE;
+
+ return TRUE;
+ }
+
+ BOOL TryGetLock(DWORD numRetries)
+ {
+ DWORD dwSwitchCount = 0;
+
+ for (;;)
+ {
+ if (TryGetLock())
+ return TRUE;
+
+ if (numRetries == 0)
+ return FALSE;
+
+ --numRetries;
+ __SwitchToThread(0, ++dwSwitchCount);
+ }
+ }
+
+ void FreeLock()
+ {
+ _ASSERTE(m_HaveLock);
+ _ASSERTE(m_Header.m_Counter == m_Counter);
+ ++m_Counter;
+ m_Counter = (m_Counter == 0) ? 2 : m_Counter;
+ m_Header.m_Counter = m_Counter;
+ m_HaveLock = FALSE;
+ }
+
+ ~IPCHeaderLockHolder()
+ {
+ if (m_HaveLock)
+ FreeLock();
+ }
+};
+
+class IPCHeaderReadHelper
+{
+ IPCHeader m_CachedHeader;
+ IPCHeader * m_pUnreliableHeader;
+ BOOL m_IsOpen;
+
+ public:
+
+ IPCHeaderReadHelper() : m_pUnreliableHeader(NULL), m_IsOpen(FALSE) {}
+
+ BOOL TryOpenHeader(IPCHeader * header)
+ {
+ _ASSERTE(!m_IsOpen);
+
+ m_pUnreliableHeader = header;
+
+ // Read the counter and the runtime ID from the header
+ m_CachedHeader.m_Counter = m_pUnreliableHeader->m_Counter;
+ if ((m_CachedHeader.m_Counter & 1) != 0)
+ return FALSE;
+ m_CachedHeader.m_RuntimeId = m_pUnreliableHeader->m_RuntimeId;
+
+ // If runtime ID is 0, then this block is not allocated by
+ // a runtime, and thus there is no further work to do
+ if (m_CachedHeader.m_RuntimeId == 0)
+ {
+ m_IsOpen = TRUE;
+ return TRUE;
+ }
+
+ // Read the rest of the values from the header
+ m_CachedHeader.m_Reserved1 = m_pUnreliableHeader->m_Reserved1;
+ m_CachedHeader.m_Reserved2 = m_pUnreliableHeader->m_Reserved2;
+ m_CachedHeader.m_Version = m_pUnreliableHeader->m_Version;
+ m_CachedHeader.m_Flags = m_pUnreliableHeader->m_Flags;
+ m_CachedHeader.m_blockSize = m_pUnreliableHeader->m_blockSize;
+ m_CachedHeader.m_BuildYear = m_pUnreliableHeader->m_BuildYear;
+ m_CachedHeader.m_BuildNumber = m_pUnreliableHeader->m_BuildNumber;
+ m_CachedHeader.m_numEntries = m_pUnreliableHeader->m_numEntries;
+
+ // Verify that the header did not change during the read
+ LONG counter = m_pUnreliableHeader->m_Counter;
+ if (m_CachedHeader.m_Counter != counter)
+ return FALSE;
+
+ // Since we know we got a clean read of numEntries, we
+ // should be able to assert this with confidence
+ if (m_CachedHeader.m_numEntries == 0)
+ {
+ _ASSERTE(!"numEntries from IPCBlock is zero");
+ return FALSE;
+ }
+ else if (m_CachedHeader.m_numEntries > eIPC_MAX)
+ {
+ _ASSERTE(!"numEntries from IPCBlock is too big");
+ return FALSE;
+ }
+
+ if (m_CachedHeader.m_blockSize == 0)
+ {
+ _ASSERTE(!"blockSize from IPCBlock is zero");
+ return FALSE;
+ }
+ else if (m_CachedHeader.m_blockSize > IPC_BLOCK_SIZE)
+ {
+ _ASSERTE(!"blockSize from IPCBlock is too big");
+ return FALSE;
+ }
+
+ // Copy the table
+ for (DWORD i = 0; i < m_CachedHeader.m_numEntries; ++i)
+ {
+ m_CachedHeader.m_table[i].m_Offset = m_pUnreliableHeader->m_table[i].m_Offset;
+ m_CachedHeader.m_table[i].m_Size = m_pUnreliableHeader->m_table[i].m_Size;
+ if (i == eIPC_PerfCounters)
+ {
+ if(!((SIZE_T)m_CachedHeader.m_table[i].m_Offset < IPC_BLOCK_SIZE) && ((SIZE_T)m_CachedHeader.m_table[i].m_Offset + m_CachedHeader.m_table[i].m_Size <= IPC_BLOCK_SIZE))
+ {
+ _ASSERTE(!"PerfCounter section offset + size is too large");
+ return FALSE;
+ }
+ }
+ }
+
+ // If eIPC_MAX > numEntries, then mark the left over
+ // slots in m_table as "empty".
+ for (DWORD i = m_CachedHeader.m_numEntries; i < eIPC_MAX; ++i)
+ {
+ m_CachedHeader.m_table[i].m_Offset = EMPTY_ENTRY_OFFSET;
+ m_CachedHeader.m_table[i].m_Size = EMPTY_ENTRY_SIZE;
+ }
+
+ // Verify again that the header did not change during the read
+ counter = m_pUnreliableHeader->m_Counter;
+ if (m_CachedHeader.m_Counter != counter)
+ return FALSE;
+
+ m_IsOpen = TRUE;
+ return TRUE;
+ }
+
+ void CloseHeader()
+ {
+ m_IsOpen = FALSE;
+ m_pUnreliableHeader = NULL;
+ }
+
+ BOOL HeaderHasChanged()
+ {
+ _ASSERTE(m_IsOpen);
+ LONG counter = m_pUnreliableHeader->m_Counter;
+ return (m_CachedHeader.m_Counter != counter) ? TRUE : FALSE;
+ }
+
+ BOOL IsSentinal()
+ {
+ _ASSERTE(m_IsOpen);
+ return (m_CachedHeader.m_Counter == 0);
+ }
+
+ DWORD GetRuntimeId()
+ {
+ _ASSERTE(m_IsOpen);
+ return m_CachedHeader.m_RuntimeId;
+ }
+
+ USHORT GetIPCVersion()
+ {
+ _ASSERTE(m_IsOpen);
+ return m_CachedHeader.m_Version;
+ }
+
+ BOOL UseWow64Structs()
+ {
+ _ASSERTE(m_IsOpen);
+#if !defined(_TARGET_X86_)
+ return ((m_CachedHeader.m_Flags & IPC_FLAG_X86) != 0) ? TRUE : FALSE;
+#else
+ return FALSE;
+#endif
+ }
+
+ IPCHeader * GetCachedCopyOfHeader()
+ {
+ _ASSERTE(m_IsOpen);
+ return &m_CachedHeader;
+ }
+
+ IPCHeader * GetUnreliableHeader()
+ {
+ _ASSERTE(m_IsOpen);
+ return m_pUnreliableHeader;
+ }
+
+ private:
+
+ void * GetUnreliableSection(EIPCClient eClient)
+ {
+ if (!m_IsOpen)
+ {
+ _ASSERTE(!"IPCHeaderReadHelper is not open");
+ return NULL;
+ }
+
+ if (eClient < 0 || eClient >= eIPC_MAX)
+ {
+ _ASSERTE(!"eClient is out of bounds");
+ return NULL;
+ }
+
+ if (m_CachedHeader.m_table[eClient].m_Offset == EMPTY_ENTRY_OFFSET)
+ {
+ _ASSERTE(!"Section is empty");
+ return NULL;
+ }
+
+ return (BYTE*)m_pUnreliableHeader + (SIZE_T)m_CachedHeader.m_table[eClient].m_Offset;
+ }
+
+ public:
+
+ // We opted to return void* instead of PerfCounterIPCControlBlock* because this
+ // forces the caller to do an explicit cast. If UseWow64Structs() returns FALSE,
+ // then the caller should cast to PerfCounterIPCControlBlock*. If UseWow64Structs()
+ // return TRUE, then the caller should cast to PerfCounterWow64IPCControlBlock*
+
+ void * GetUnreliablePerfBlock()
+ {
+ return GetUnreliableSection(eIPC_PerfCounters);
+ }
+};
+
+const DWORD SXSPUBLIC_IPC_SIZE_NO_PADDING = sizeof(IPCHeader) + sizeof(struct PerfCounterIPCControlBlock);
+const DWORD SXSPUBLIC_WOW64_IPC_SIZE_NO_PADDING = sizeof(IPCHeader) + sizeof(struct PerfCounterWow64IPCControlBlock);
+
+const DWORD SXSPUBLIC_IPC_PAD_SIZE = IPC_BLOCK_SIZE - SXSPUBLIC_IPC_SIZE_NO_PADDING;
+const DWORD SXSPUBLIC_WOW64_IPC_PAD_SIZE = IPC_BLOCK_SIZE - SXSPUBLIC_WOW64_IPC_SIZE_NO_PADDING;
+
+struct IPCControlBlock
+{
+// Header
+ IPCHeader m_Header;
+
+// Client blocks
+ struct PerfCounterIPCControlBlock m_perf;
+
+// Padding
+ BYTE m_Padding[SXSPUBLIC_IPC_PAD_SIZE];
+};
+
+#pragma pack(push, 4)
+struct IPCControlBlockWow64
+{
+// Header
+ IPCHeader m_Header;
+
+// Client blocks
+ PerfCounterWow64IPCControlBlock m_perf;
+
+// Padding
+ BYTE m_Padding[SXSPUBLIC_WOW64_IPC_PAD_SIZE];
+};
+#pragma pack(pop)
+
+static_assert_no_msg(sizeof(IPCControlBlock) == IPC_BLOCK_SIZE);
+static_assert_no_msg(sizeof(IPCControlBlockWow64) == IPC_BLOCK_SIZE);
+
+struct IPCControlBlockTable
+{
+ IPCControlBlock m_blocks[IPC_NUM_BLOCKS_IN_TABLE];
+
+ IPCControlBlock * GetBlock(DWORD index)
+ { return &(m_blocks[index]); }
+};
+
+static_assert_no_msg(sizeof(IPCControlBlockTable) == IPC_BLOCK_TABLE_SIZE);
+
+typedef DPTR(IPCControlBlockTable) PTR_IPCControlBlockTable;
+typedef DPTR(IPCControlBlock) PTR_IPCControlBlock;
+
+
+/**************************************** LEGACY ****************************************/
+
+//-----------------------------------------------------------------------------
+// LegacyPrivate header - put in its own structure so we can easily get the
+// size of the header. It will compile to the same thing either way.
+// This header must remain completely binary compatible w/ older versions.
+// Notes:
+// This header contains a "module handle" field which is platform dependent in size.
+// That means for a 64 bit process to read IPC header of a 32 bit process, we cannot
+// use the same class definition. The class would be different on the two platforms.
+// Hence LegacyPrivateIPCHeaderTemplate is templatized on the type of the module handle. The
+// IPC writers always use HINSTANCE as the parameter. The IPC reader has to know
+// whether it is reading IPC block of a 32 bit process. If so it uses DWORD as the
+// parameter so that the resulting LegacyPrivateIPCHeader is similar in format to the one
+// written by the 32 bit process at the other end.
+// The DWORD 'm_dwVersion' was split into two two USHORTS named 'm_Version' and
+// 'm_Flags'. The upper bits of 'm_dwVersion' were never used, nor was 'm_dwVersion'
+// used to determine whether an IPC block was valid or compatible. Thus, splitting
+// the 'm_dwVersion' into two USHORTs should not introduce any compatibility issues.
+//-----------------------------------------------------------------------------
+template <class TModuleHandle>
+struct LegacyPrivateIPCHeaderTemplate
+{
+// header
+ USHORT m_Version; // version of the IPC Block
+ USHORT m_Flags; // flags field
+ DWORD m_blockSize; // Size of the entire shared memory block
+ TModuleHandle m_hInstance; // instance of module that created this header
+ USHORT m_BuildYear; // stamp for year built
+ USHORT m_BuildNumber; // stamp for Month/Day built
+ DWORD m_numEntries; // Number of entries in the table
+};
+
+typedef LegacyPrivateIPCHeaderTemplate<HINSTANCE> LegacyPrivateIPCHeader;
+
+//-----------------------------------------------------------------------------
+// This fixes alignment & packing issues.
+// This header must remain completely binary compatible w/ older versions.
+//-----------------------------------------------------------------------------
+struct FullIPCHeaderLegacyPrivate
+{
+// Header
+ LegacyPrivateIPCHeader m_header;
+
+// Directory
+ IPCEntry m_table[eLegacyPrivateIPC_MAX]; // entry describing each client's block
+
+};
+
+//-----------------------------------------------------------------------------
+// This fixes alignment & packing issues.
+// This header must remain completely binary compatible w/ older versions.
+//-----------------------------------------------------------------------------
+template <class TModuleHandle>
+struct FullIPCHeaderLegacyPublicTemplate
+{
+// Header
+ struct LegacyPrivateIPCHeaderTemplate<TModuleHandle> m_header;
+
+// Directory
+ IPCEntry m_table[eLegacyPublicIPC_MAX]; // entry describing each client's block
+
+};
+
+// In hindsight, we should have made the offsets be absolute, but we made them
+// relative to the end of the FullIPCHeader.
+// The problem is that as future versions added new Entries to the directory,
+// the header size grew.
+// Thus we make IPCEntry::m_Offset is relative to LEGACY_IPC_ENTRY_OFFSET_BASE, which
+// corresponds to sizeof(LegacyPrivateIPCHeader) for an v1.0 /v1.1 build.
+#ifdef _TARGET_X86_
+ const DWORD LEGACY_IPC_ENTRY_OFFSET_BASE = 0x14;
+#else
+ // On non-x86 platforms, we don't need to worry about backwards compat.
+ // But we do need to worry about alignment, so just pretend that everett was 0,
+ // and solve both problems.
+ const DWORD LEGACY_IPC_ENTRY_OFFSET_BASE = 0x0;
+#endif
+
+// When a 64 bit process reads IPC block of a 32 bit process, we need to know the
+// LEGACY_IPC_ENTRY_OFFSET_BASE of the latter from the former. So this constant is defined
+const DWORD LEGACY_IPC_ENTRY_OFFSET_BASE_WOW64 = 0x14;
+
+// Size of LegacyPublicIPCControlBlock for Everett and Whidbey
+const DWORD LEGACYPUBLIC_IPC_BLOCK_SIZE_32 = 0x134;
+const DWORD LEGACYPUBLIC_IPC_BLOCK_SIZE_64 = 0x1a0;
+
+//-----------------------------------------------------------------------------
+// LegacyPrivate (per process) IPC Block for COM+ apps
+//-----------------------------------------------------------------------------
+struct LegacyPrivateIPCControlBlock
+{
+ FullIPCHeaderLegacyPrivate m_FullIPCHeader;
+
+
+// Client blocks
+ struct PerfCounterIPCControlBlock m_perf; // no longer used but kept for compat
+ struct AppDomainEnumerationIPCBlock m_appdomain;
+ WCHAR m_instancePath[MAX_PATH];
+};
+
+typedef DPTR(LegacyPrivateIPCControlBlock) PTR_LegacyPrivateIPCControlBlock;
+
+#if defined(_TARGET_X86_)
+// For perf reasons, we'd like to keep the IPC block small enough to fit on
+// a single page. This assert ensures it won't silently grow past the page boundary
+// w/o us knowing about it. If this assert fires, then either:
+// - consciously adjust it to let the IPC block be 2 pages.
+// - shrink the IPC blocks.
+static_assert_no_msg(sizeof(LegacyPrivateIPCControlBlock) <= 4096);
+#endif
+
+//-----------------------------------------------------------------------------
+// LegacyPublic (per process) IPC Block for CLR apps
+//-----------------------------------------------------------------------------
+struct LegacyPublicIPCControlBlock
+{
+ FullIPCHeaderLegacyPublicTemplate<HINSTANCE> m_FullIPCHeaderLegacyPublic;
+
+// Client blocks
+ struct PerfCounterIPCControlBlock m_perf;
+};
+
+//-----------------------------------------------------------------------------
+// LegacyPublicWow64IPCControlBlock is used by a 64 bit process to read the IPC block
+// of a 32 bit process. This struct is similar to LegacyPublicIPCControlBlock, except
+// that all pointer (ie platform dependent) sized fields are substituted with
+// DWORDs, so as to match the exact layout of LegacyPublicIPCControlBlock in a 32 bit process
+//-----------------------------------------------------------------------------
+#pragma pack(push, 4)
+struct LegacyPublicWow64IPCControlBlock
+{
+ FullIPCHeaderLegacyPublicTemplate<DWORD> m_FullIPCHeaderLegacyPublic;
+
+// Client blocks
+ PerfCounterWow64IPCControlBlock m_perf;
+};
+#pragma pack(pop)
+
+typedef DPTR(LegacyPublicIPCControlBlock) PTR_LegacyPublicIPCControlBlock;
+
+//-----------------------------------------------------------------------------
+// Inline definitions
+//-----------------------------------------------------------------------------
+
+#include "ipcheader.inl"
+
+#endif // _IPCManagerPriv_h_
diff --git a/src/ipcman/ipcheader.inl b/src/ipcman/ipcheader.inl
new file mode 100644
index 0000000000..85aed282c4
--- /dev/null
+++ b/src/ipcman/ipcheader.inl
@@ -0,0 +1,91 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+//*****************************************************************************
+// File: IPCHeader.inl
+//
+// Define the LegacyPrivate header format for COM+ memory mapped files. Everyone
+// outside of IPCMan.lib will use the public header, IPCManagerInterface.h
+//
+//*****************************************************************************
+
+#ifndef _IPCManagerPriv_inl_
+#define _IPCManagerPriv_inl_
+
+#include "ipcheader.h"
+
+//=============================================================================
+// Internal Helpers: Encapsulate any error-prone math / comparisons.
+// The helpers are very streamlined and don't handle error conditions.
+// Also, Table access functions use DWORD instead of typesafe Enums
+// so they can be more flexible (not just for LegacyPrivate blocks).
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Returns true if the entry is empty and false if the entry is usable.
+// This is an internal helper that Enforces a formal definition for an
+// "empty" entry
+//
+// Arguments:
+// block - IPC block of interest
+// Id - index of the desired entry in the IPC block's table
+//-----------------------------------------------------------------------------
+inline bool Internal_CheckEntryEmptyLegacyPrivate(
+ const LegacyPrivateIPCControlBlock & block,
+ DWORD Id
+)
+{
+// Directory has offset in bytes of block
+ const DWORD offset = block.m_FullIPCHeader.m_table[Id].m_Offset;
+
+ return (EMPTY_ENTRY_OFFSET == offset);
+}
+
+//-----------------------------------------------------------------------------
+// Returns the base that entry offsets for the specified IPC block
+// are relative to.
+//
+// Arguments:
+// block - IPC block of interest
+//-----------------------------------------------------------------------------
+inline SIZE_T Internal_GetOffsetBaseLegacyPrivate(const LegacyPrivateIPCControlBlock & block)
+{
+ return LEGACY_IPC_ENTRY_OFFSET_BASE +
+ block.m_FullIPCHeader.m_header.m_numEntries
+ * sizeof(IPCEntry); // skip over directory (variable size)
+}
+
+//-----------------------------------------------------------------------------
+// Returns a BYTE* to a block within a header. This is an internal
+// helper that encapsulates error-prone math.
+//
+// Arguments:
+// block - IPC block of interest
+// Id - index of the desired entry in the IPC block's table
+//-----------------------------------------------------------------------------
+inline BYTE* Internal_GetBlockLegacyPrivate(
+ const LegacyPrivateIPCControlBlock & block,
+ DWORD Id
+)
+{
+
+// Directory has offset in bytes of block
+ const DWORD offset = block.m_FullIPCHeader.m_table[Id].m_Offset;
+
+
+// This block has been removed. Callee should have caught that and not called us.
+ _ASSERTE(!Internal_CheckEntryEmptyLegacyPrivate(block, Id));
+ return
+ ((BYTE*) &block) // base pointer to start of block
+ + Internal_GetOffsetBaseLegacyPrivate(block)
+ +offset; // jump to block
+}
+
+#endif // _IPCManagerPriv_inl_
diff --git a/src/ipcman/ipcman-staticcrt/.gitmirror b/src/ipcman/ipcman-staticcrt/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/ipcman/ipcman-staticcrt/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/ipcman/ipcman-staticcrt/CMakeLists.txt b/src/ipcman/ipcman-staticcrt/CMakeLists.txt
new file mode 100644
index 0000000000..01c155c7ad
--- /dev/null
+++ b/src/ipcman/ipcman-staticcrt/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_definitions(-D_CRTIMP=) #static link of crt
+add_library(ipcmanager-staticcrt STATIC ${IPCMAN_SOURCES}) \ No newline at end of file
diff --git a/src/ipcman/ipcman-staticcrt/dirs.proj b/src/ipcman/ipcman-staticcrt/dirs.proj
new file mode 100644
index 0000000000..d6abafb7e2
--- /dev/null
+++ b/src/ipcman/ipcman-staticcrt/dirs.proj
@@ -0,0 +1,17 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Condition="'$(FeatureDbiDebugging)'=='true'" Include="HostLocal\ipcman-staticcrt.nativeproj" />
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/ipcman/ipcman-staticcrt/ipcman-staticcrt.props b/src/ipcman/ipcman-staticcrt/ipcman-staticcrt.props
new file mode 100644
index 0000000000..f4cbade081
--- /dev/null
+++ b/src/ipcman/ipcman-staticcrt/ipcman-staticcrt.props
@@ -0,0 +1,11 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\IPCMan\ipcmanager.props" />
+
+ <PropertyGroup>
+ <LinkNoLibraries>true</LinkNoLibraries>
+ <LinkUseCMT>true</LinkUseCMT>
+ <UseMsvcrt>false</UseMsvcrt>
+ </PropertyGroup>
+
+</Project>
diff --git a/src/ipcman/ipcmanager.props b/src/ipcman/ipcmanager.props
new file mode 100644
index 0000000000..f1f4018d4e
--- /dev/null
+++ b/src/ipcman/ipcmanager.props
@@ -0,0 +1,28 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <UserIncludes>$(UserIncludes);
+ $(Clrbase)\src\IPCMan;
+ $(Clrbase)\src\vm</UserIncludes>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE -DFEATURE_NO_HOST</ClAdditionalOptions>
+ <OutputName>ipcmanager</OutputName>
+ <OutputPath>$(ClrLibDest)</OutputPath>
+ <TargetType>LIBRARY</TargetType>
+ <PCHHeader>stdafx.h</PCHHeader>
+ <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders>
+ <PCHCompile>$(Clrbase)\src\IPCMan\stdafx.cpp</PCHCompile>
+ </PropertyGroup>
+ <!--Leaf Project Items-->
+ <ItemGroup>
+ <CppCompile Include="$(Clrbase)\src\IPCMan\IPCWriterImpl.cpp" />
+ <CppCompile Include="$(Clrbase)\src\IPCMan\IPCReaderImpl.cpp" />
+ <CppCompile Include="$(Clrbase)\src\IPCMan\IPCSharedSrc.cpp" />
+ <CppCompile Include="$(Clrbase)\src\IPCMan\IPCFuncCallImpl.cpp" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/ipcman/ipcmanagerimpl.h b/src/ipcman/ipcmanagerimpl.h
new file mode 100644
index 0000000000..500b9d6c71
--- /dev/null
+++ b/src/ipcman/ipcmanagerimpl.h
@@ -0,0 +1,148 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+//*****************************************************************************
+// File: IPCManagerImpl.h
+//
+// Defines Classes to implement InterProcess Communication Manager for a COM+
+//
+//*****************************************************************************
+
+#ifndef _IPCManagerImpl_H_
+#define _IPCManagerImpl_H_
+
+#include <aclapi.h>
+
+#include "contract.h"
+#include "ipcenums.h"
+
+
+// Version of the IPC Block that this lib was compiled for
+const USHORT VER_IPC_BLOCK = 4;
+
+// Versions for the legacy IPC Blocks
+const USHORT VER_LEGACYPRIVATE_IPC_BLOCK = 2;
+const USHORT VER_LEGACYPUBLIC_IPC_BLOCK = 3;
+
+struct LegacyPrivateIPCControlBlock;
+
+
+//-----------------------------------------------------------------------------
+// Implementation for the IPCManager for COM+.
+//-----------------------------------------------------------------------------
+class IPCWriterImpl
+{
+public:
+
+ IPCWriterImpl();
+ ~IPCWriterImpl();
+
+ BOOL IsLegacyPrivateBlockOpen() const;
+ BOOL IsBlockTableOpen() const;
+
+ HRESULT CreateWinNTDescriptor(DWORD pid, SECURITY_ATTRIBUTES **ppSA, EDescriptorType descType);
+
+protected:
+
+#ifndef DACCESS_COMPILE
+
+ void CloseMemoryMappedFile(HANDLE & hMemFile, void * & pBlock);
+
+ HRESULT CreateNewIPCBlock();
+ void CreateIPCHeader();
+ void WriteEntryHelper(EIPCClient eClient, DWORD offs, DWORD size);
+
+#endif
+
+ // Cache pointers to each section
+ struct PerfCounterIPCControlBlock *m_pPerf;
+ struct AppDomainEnumerationIPCBlock *m_pAppDomain;
+ PCWSTR m_pInstancePath;
+
+ // Info on the Block Table
+ HANDLE m_handleBlockTable;
+ HANDLE m_handleBoundaryDesc;
+ HANDLE m_handlePrivateNamespace;
+ PSID m_pSID;
+ PTR_IPCControlBlockTable m_pBlockTable;
+ PTR_IPCControlBlock m_pBlock;
+ PTR_IPCControlBlockTable m_pBackupBlock;
+
+#ifndef DACCESS_COMPILE
+
+ HRESULT CreateNewLegacyPrivateIPCBlock();
+ void CreateLegacyPrivateIPCHeader();
+ void WriteEntryHelper(ELegacyPrivateIPCClient eClient, DWORD offs, DWORD size);
+
+#endif
+
+ // Stats on MemoryMapped file for the given pid
+ HANDLE m_handleLegacyPrivateBlock;
+ PTR_LegacyPrivateIPCControlBlock m_ptrLegacyPrivateBlock;
+ PTR_LegacyPrivateIPCControlBlock m_pIPCBackupBlockLegacyPrivate;
+
+ // Security attributes cached for the current process.
+ SECURITY_ATTRIBUTES *m_cachedPrivateDescriptor;
+
+};
+
+//-----------------------------------------------------------------------------
+// IPCReader class connects to a COM+ IPC block and reads from it
+// <TODO>@todo - make global & private readers</TODO>
+//-----------------------------------------------------------------------------
+class IPCReaderImpl
+{
+public:
+ IPCReaderImpl();
+ ~IPCReaderImpl();
+
+ BOOL TryOpenBlock(IPCHeaderReadHelper & readHelper, DWORD blockIndex);
+
+ BOOL UseWow64StructsLegacy();
+ BOOL Internal_CheckEntryEmptyLegacyPublic(DWORD Id);
+ BYTE * Internal_GetBlockLegacyPublic(DWORD Id);
+ DWORD GetNumEntriesLegacy(void * pBlock);
+ IPCEntry * GetDirectoryLegacy(void * pBlock);
+ DWORD GetOffsetBaseLegacy();
+ DWORD GetFirstExpectedOffsetLegacy();
+ USHORT GetFlagsLegacy(void * pBlock);
+ DWORD GetBlockSizeLegacy(void * pBlock);
+
+protected:
+
+ HANDLE m_handleBlockTable;
+ HANDLE m_handleBoundaryDesc;
+ HANDLE m_handlePrivateNamespace;
+ PSID m_pSID;
+ IPCControlBlockTable * m_pBlockTable;
+
+ BOOL m_fIsTarget32Bit;
+#ifdef _DEBUG
+ BOOL m_fInitialized;
+#endif
+
+ HANDLE m_handleLegacyPrivateBlock;
+ LegacyPrivateIPCControlBlock * m_ptrLegacyPrivateBlock;
+ HANDLE m_handleLegacyPublicBlock;
+
+ union
+ {
+ LegacyPublicIPCControlBlock * m_ptrLegacyPublicBlock;
+ LegacyPublicWow64IPCControlBlock * m_ptrWow64LegacyPublicBlock;
+ };
+};
+
+//-----------------------------------------------------------------------------
+// Inline definitions
+//-----------------------------------------------------------------------------
+
+#include "ipcmanagerimpl.inl"
+
+#endif // _IPCManagerImpl_H_
diff --git a/src/ipcman/ipcmanagerimpl.inl b/src/ipcman/ipcmanagerimpl.inl
new file mode 100644
index 0000000000..1428b3a0dc
--- /dev/null
+++ b/src/ipcman/ipcmanagerimpl.inl
@@ -0,0 +1,193 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+//*****************************************************************************
+// File: IPCManagerImpl.inl
+//
+// Defines Classes to implement InterProcess Communication Manager for a COM+
+//
+//*****************************************************************************
+
+#ifndef _IPCManagerImpl_INL_
+#define _IPCManagerImpl_INL_
+
+#include "ipcmanagerimpl.h"
+
+//-----------------------------------------------------------------------------
+// Return true if the IPCBlockTable is available.
+//-----------------------------------------------------------------------------
+inline BOOL IPCWriterImpl::IsBlockTableOpen() const
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+ return (m_pBlockTable != NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Return true if our LegacyPrivate block is available.
+//-----------------------------------------------------------------------------
+inline BOOL IPCWriterImpl::IsLegacyPrivateBlockOpen() const
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+ return (m_ptrLegacyPrivateBlock != NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Returns a BOOL indicating whether the Wow64 structs should be used instead
+// of the normal structs.
+//
+// It is NOT safe to call this function before m_fIsTarget32Bit has been
+// initialized.
+//-----------------------------------------------------------------------------
+inline BOOL IPCReaderImpl::UseWow64StructsLegacy()
+{
+#if !defined(_X86_)
+ _ASSERTE(m_fInitialized);
+ return m_fIsTarget32Bit;
+#else
+ return FALSE;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Returns the value of the flags field of the specified IPC block. This is
+// safe to call before m_fIsTarget32Bit has been initialized since the flags
+// field is in the same position for 32-bit and 64-bit targets.
+//
+// It is safe to call this function before m_fIsTarget32Bit has been
+// initialized.
+//-----------------------------------------------------------------------------
+inline USHORT IPCReaderImpl::GetFlagsLegacy(void * pBlock)
+{
+ return ((LegacyPrivateIPCHeader*)pBlock)->m_Flags;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the value of the block size field of the specified IPC block.
+//
+// It is safe to call this function before m_fIsTarget32Bit has been
+// initialized.
+//-----------------------------------------------------------------------------
+inline DWORD IPCReaderImpl::GetBlockSizeLegacy(void * pBlock)
+{
+ return ((LegacyPrivateIPCHeader*)pBlock)->m_blockSize;
+}
+
+//-----------------------------------------------------------------------------
+// Returns true if the specified entry is empty and false if the entry is
+// usable. This is an internal helper that enforces the formal definition
+// for an "empty" entry
+//
+// Arguments:
+// Id - index of the entry to check
+//
+// It is NOT safe to call this function before m_fIsTarget32Bit has been
+// initialized.
+//-----------------------------------------------------------------------------
+inline BOOL IPCReaderImpl::Internal_CheckEntryEmptyLegacyPublic(DWORD Id)
+{
+ // Directory has offset in bytes of block
+ return (GetDirectoryLegacy(m_ptrLegacyPublicBlock)[Id].m_Offset == EMPTY_ENTRY_OFFSET);
+}
+
+//-----------------------------------------------------------------------------
+// Returns a BYTE* to a block within a header. This is an internal
+// helper that encapsulates error-prone math.
+//
+// Arguments:
+// Id - index of the entry containing the desired LegacyPublic block
+//
+// It is NOT safe to call this function before m_fIsTarget32Bit has been
+// initialized.
+//-----------------------------------------------------------------------------
+inline BYTE * IPCReaderImpl::Internal_GetBlockLegacyPublic(DWORD Id)
+{
+ // This block has been removed. Callee should have caught that and not called us.
+ _ASSERTE(!Internal_CheckEntryEmptyLegacyPublic(Id));
+
+ return ((BYTE*) m_ptrLegacyPublicBlock) + (SIZE_T)GetOffsetBaseLegacy() +
+ (SIZE_T)GetNumEntriesLegacy(m_ptrLegacyPublicBlock) * sizeof(IPCEntry) +
+ (SIZE_T)GetDirectoryLegacy(m_ptrLegacyPublicBlock)[Id].m_Offset;
+}
+
+//-----------------------------------------------------------------------------
+// Returns a value that is used to calculate the actual offset of an entry in
+// an IPC block. Internal_GetBlockLegacyPublic() shows how to use GetOffsetBaseLegacy()
+// to calculate the actual offset of an entry.
+//
+// It is NOT safe to call this funciton before m_fIsTarget32Bit has been
+// initialized.
+//-----------------------------------------------------------------------------
+inline DWORD IPCReaderImpl::GetOffsetBaseLegacy()
+{
+ if (UseWow64StructsLegacy())
+ return LEGACY_IPC_ENTRY_OFFSET_BASE_WOW64;
+
+ return LEGACY_IPC_ENTRY_OFFSET_BASE;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the expected value for the specified offset of the first entry in
+// an IPC block.
+//
+// It is NOT safe to call this funciton before m_fIsTarget32Bit has been
+// initialized.
+//-----------------------------------------------------------------------------
+inline DWORD IPCReaderImpl::GetFirstExpectedOffsetLegacy()
+{
+ if (GetOffsetBaseLegacy() == 0)
+ return sizeof(LegacyPrivateIPCHeader);
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the number of entries in the specified IPC block.
+//
+// It is NOT safe to call this funciton before m_fIsTarget32Bit has been
+// initialized.
+//-----------------------------------------------------------------------------
+inline DWORD IPCReaderImpl::GetNumEntriesLegacy(void * pBlock)
+{
+ if (UseWow64StructsLegacy())
+ return ((LegacyPrivateIPCHeaderTemplate<DWORD>*)pBlock)->m_numEntries;
+
+ return ((LegacyPrivateIPCHeader*)pBlock)->m_numEntries;
+}
+
+//-----------------------------------------------------------------------------
+// Returns a pointer to the directory ('m_table') in the specified IPC block.
+//
+// It is NOT safe to call this funciton before m_fIsTarget32Bit has been
+// initialized.
+//-----------------------------------------------------------------------------
+inline IPCEntry * IPCReaderImpl::GetDirectoryLegacy(void * pBlock)
+{
+ if (UseWow64StructsLegacy())
+ return ((FullIPCHeaderLegacyPublicTemplate<DWORD>*)pBlock)->m_table;
+
+ return ((FullIPCHeaderLegacyPublicTemplate<HINSTANCE>*)pBlock)->m_table;
+}
+
+//-----------------------------------------------------------------------------
+// Compile-time asserts that check the values of LEGACY_IPC_ENTRY_OFFSET_BASE and
+// LEGACY_IPC_ENTRY_OFFSET_BASE_WOW64
+//-----------------------------------------------------------------------------
+
+#ifdef _TARGET_X86_
+ C_ASSERT(sizeof(LegacyPrivateIPCHeaderTemplate<HINSTANCE>) == LEGACY_IPC_ENTRY_OFFSET_BASE);
+#endif
+
+C_ASSERT(sizeof(LegacyPrivateIPCHeaderTemplate<DWORD>) == LEGACY_IPC_ENTRY_OFFSET_BASE_WOW64);
+
+
+#endif // _IPCManagerImpl_INL_
+
diff --git a/src/ipcman/ipcreaderimpl.cpp b/src/ipcman/ipcreaderimpl.cpp
new file mode 100644
index 0000000000..0a3b46aa4a
--- /dev/null
+++ b/src/ipcman/ipcreaderimpl.cpp
@@ -0,0 +1,1114 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+//*****************************************************************************
+// File: IPCReaderImpl.cpp
+//
+// Read a COM+ memory mapped file
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+
+#include "ipcmanagerinterface.h"
+#include "ipcheader.h"
+#include "ipcshared.h"
+#include <safewrap.h>
+
+#include <securitywrapper.h>
+
+#if defined(FEATURE_IPCMAN)
+//-----------------------------------------------------------------------------
+// Ctor sets members
+//-----------------------------------------------------------------------------
+IPCReaderImpl::IPCReaderImpl()
+{
+ LIMITED_METHOD_CONTRACT;
+#ifdef _DEBUG
+ m_fInitialized = FALSE;
+#endif
+ m_fIsTarget32Bit = FALSE;
+ m_handleLegacyPrivateBlock = NULL;
+ m_ptrLegacyPrivateBlock = NULL;
+ m_handleLegacyPublicBlock = NULL;
+ m_ptrLegacyPublicBlock = NULL;
+ m_handleBlockTable = NULL;
+ m_pBlockTable = NULL;
+ m_handleBoundaryDesc = NULL;
+ m_handlePrivateNamespace = NULL;
+ m_pSID = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// dtor
+//-----------------------------------------------------------------------------
+IPCReaderImpl::~IPCReaderImpl()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::IPCReaderImpl::~IPCReaderImpl 0x%08x (LegacyPrivate)\n", m_handleLegacyPrivateBlock));
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::IPCReaderImpl::~IPCReaderImpl 0x%08x (LegacyPublic)\n", m_handleLegacyPublicBlock));
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::IPCReaderImpl::~IPCReaderImpl 0x%08x (SxSPublic)\n", m_handleBlockTable));
+
+ _ASSERTE(m_handleLegacyPrivateBlock == NULL);
+ _ASSERTE(m_ptrLegacyPrivateBlock == NULL);
+
+ _ASSERTE(m_handleLegacyPublicBlock == NULL);
+ _ASSERTE(m_ptrLegacyPublicBlock == NULL);
+
+ _ASSERTE(m_handleBlockTable == NULL);
+ _ASSERTE(m_pBlockTable == NULL);
+ _ASSERTE(m_handleBoundaryDesc == NULL);
+ _ASSERTE(m_pSID == NULL);
+ _ASSERTE(m_handlePrivateNamespace == NULL);
+
+
+}
+
+//-----------------------------------------------------------------------------
+// dtor
+//-----------------------------------------------------------------------------
+IPCReaderInterface::~IPCReaderInterface()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::IPCReaderInterface::~IPCReaderInterface 0x%08x (BlockTable)\n", m_handleBlockTable));
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::IPCReaderInterface::~IPCReaderInterface 0x%08x (LegacyPrivate)\n", m_handleLegacyPrivateBlock));
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::IPCReaderInterface::~IPCReaderInterface 0x%08x (LegacyPublic)\n", m_handleLegacyPublicBlock));
+
+ if (m_handleLegacyPrivateBlock)
+ {
+ CloseLegacyPrivateBlock();
+ }
+ _ASSERTE(m_handleLegacyPrivateBlock == NULL);
+ _ASSERTE(m_ptrLegacyPrivateBlock == NULL);
+
+#ifndef DACCESS_COMPILE
+ if (m_handleLegacyPublicBlock)
+ {
+ CloseLegacyPublicBlock();
+ }
+ _ASSERTE(m_handleLegacyPublicBlock == NULL);
+ _ASSERTE(m_ptrLegacyPublicBlock == NULL);
+
+ if (m_handleBlockTable)
+ {
+ CloseBlockTable();
+ }
+ _ASSERTE(m_handleBlockTable == NULL);
+ _ASSERTE(m_pBlockTable == NULL);
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Close whatever block we opened
+//-----------------------------------------------------------------------------
+void IPCReaderInterface::CloseLegacyPrivateBlock()
+{
+ WRAPPER_NO_CONTRACT;
+
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::CloseLegacyPrivateBlock 0x%08x\n", m_handleLegacyPrivateBlock));
+
+ IPCShared::CloseMemoryMappedFile(
+ m_handleLegacyPrivateBlock,
+ (void * &) m_ptrLegacyPrivateBlock
+ );
+ _ASSERTE(m_handleLegacyPrivateBlock == NULL);
+ _ASSERTE(m_ptrLegacyPrivateBlock == NULL);
+}
+
+#ifndef DACCESS_COMPILE
+
+//-----------------------------------------------------------------------------
+// Close whatever block we opened
+//-----------------------------------------------------------------------------
+void IPCReaderInterface::CloseLegacyPublicBlock()
+{
+ WRAPPER_NO_CONTRACT;
+
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::CloseLegacyPublicBlock 0x%08x\n", m_handleLegacyPublicBlock));
+
+ IPCShared::CloseMemoryMappedFile(
+ m_handleLegacyPublicBlock,
+ (void * &) m_ptrLegacyPublicBlock
+ );
+ _ASSERTE(m_handleLegacyPublicBlock == NULL);
+ _ASSERTE(m_ptrLegacyPublicBlock == NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Close whatever block we opened
+//-----------------------------------------------------------------------------
+void IPCReaderInterface::CloseBlockTable()
+{
+ WRAPPER_NO_CONTRACT;
+
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::CloseBlockTable 0x%08x\n", m_handleBlockTable));
+
+ IPCShared::CloseMemoryMappedFile(
+ m_handleBlockTable,
+ (void * &) m_pBlockTable
+ );
+ _ASSERTE(m_handleBlockTable == NULL);
+ _ASSERTE(m_pBlockTable == NULL);
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Check to see if Debugger and Debuggee are on compatible platform
+// Currently, we only consider incompatible if one process is on WOW64 box and the other is not.
+// If so, return false.
+// For all other cases, return true for now.
+//-----------------------------------------------------------------------------
+HRESULT IPCReaderInterface::IsCompatablePlatformForDebuggerAndDebuggee(
+ DWORD pid,
+ BOOL * pfCompatible)
+{
+ if (pfCompatible == NULL)
+ return E_INVALIDARG;
+
+ // assume compatible unless otherwise
+ *pfCompatible = TRUE;
+
+ // assume that the target has the same bitness as
+ // this process unless otherwise
+#ifdef _TARGET_X86_
+ m_fIsTarget32Bit = TRUE;
+#else
+ m_fIsTarget32Bit = FALSE;
+#endif
+#ifdef _DEBUG
+ m_fInitialized = TRUE;
+#endif
+
+ BOOL fThisProcessIsWow64 = FALSE;
+ BOOL fSuccess = FALSE;
+ HANDLE hThisProcess = GetCurrentProcess();
+ fSuccess = IsWow64Process(hThisProcess, &fThisProcessIsWow64);
+ CloseHandle(hThisProcess);
+ hThisProcess = NULL;
+
+ if (!fSuccess)
+ return HRESULT_FROM_GetLastError();
+
+ HANDLE hTargetProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
+ if (hTargetProcess == NULL)
+ return HRESULT_FROM_GetLastError();
+
+ BOOL fTargetProcessIsWow64 = FALSE;
+ fSuccess = IsWow64Process(hTargetProcess, &fTargetProcessIsWow64);
+ CloseHandle(hTargetProcess);
+ hTargetProcess = NULL;
+
+ if (!fSuccess)
+ return HRESULT_FROM_GetLastError();
+
+ // We don't want to expose the IPC block if one process is x86 and
+ // the other is ia64 or amd64
+ if (fTargetProcessIsWow64 != fThisProcessIsWow64)
+ {
+ *pfCompatible = FALSE;
+ m_fIsTarget32Bit = !m_fIsTarget32Bit;
+ }
+
+ return S_OK;
+}
+
+
+void IPCReaderInterface::MakeInstanceName(const WCHAR * szProcessName, DWORD pid, DWORD runtimeId, SString & sName)
+{
+ WRAPPER_NO_CONTRACT;
+
+ const WCHAR * szFormat = CorSxSPublicInstanceName;
+
+ sName.Printf(szFormat, szProcessName, pid, runtimeId);
+}
+
+
+void IPCReaderInterface::MakeInstanceNameWhidbey(const WCHAR * szProcessName, DWORD pid, SString & sName)
+{
+ WRAPPER_NO_CONTRACT;
+
+ const WCHAR * szFormat = CorSxSPublicInstanceNameWhidbey;
+
+ sName.Printf(szFormat, szProcessName, pid);
+}
+
+
+//-----------------------------------------------------------------------------
+// Open our LegacyPrivate block
+//-----------------------------------------------------------------------------
+HRESULT IPCReaderInterface::OpenLegacyPrivateBlockOnPid(DWORD pid, DWORD dwDesiredAccess)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if 0
+// Note, PID != GetCurrentProcessId(), b/c we're expected to be opening
+// someone else's IPCBlock, not our own. If this isn't the case, just remove
+// this assert
+
+// exception: if we're enumerating provesses, we'll hit our own
+// _ASSERTE(pid != GetCurrentProcessId());
+#endif
+
+ // Note: if our LegacyPrivate block is open, we shouldn't be attaching to a new one.
+ _ASSERTE(!IsLegacyPrivateBlockOpen());
+ if (IsLegacyPrivateBlockOpen())
+ {
+ // if we goto errExit, it will close the file. We don't want that.
+ return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
+ }
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ // We should not be trying to open the IPC block of an x86 process from
+ // within an ia64 process, or vice versa. This can only happen on
+ // Server2003 and later.
+
+ BOOL fCompatible = FALSE;
+ hr = IsCompatablePlatformForDebuggerAndDebuggee(pid, &fCompatible);
+ if (FAILED(hr))
+ {
+ goto end;
+ }
+ if (fCompatible == FALSE)
+ {
+ hr = CORDBG_E_UNCOMPATIBLE_PLATFORMS;
+ goto end;
+ }
+
+ // In order to verify handle's owner, we need READ_CONTROL.
+ dwDesiredAccess |= READ_CONTROL;
+
+ {
+ SString szMemFileName;
+ IPCShared::GenerateName(pid, szMemFileName);
+ m_handleLegacyPrivateBlock = WszOpenFileMapping(dwDesiredAccess,
+ FALSE,
+ szMemFileName);
+ if (m_handleLegacyPrivateBlock == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ }
+
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::OPBOP: CreateFileMapping of %S, handle = 0x%08x, pid = 0x%8.8x GetLastError=%d\n",
+ szMemFileName.GetUnicode(), m_handleLegacyPrivateBlock, pid, GetLastError()));
+ if (m_handleLegacyPrivateBlock == NULL)
+ {
+ goto end;
+ }
+ }
+
+ // Verify that the owner of the handle is the same as the user of that pid.
+ // This protects us against a 3rd user pre-creating the IPC block underneath us
+ // and tricking us into attaching to that.
+ // Even if a 3rd-party is able to spoof the IPC block, they themselves won't
+ // have access to it and so the IPC block will remain all zeros.
+ // That radically limits potential attacks that a 3rd-party could do.
+ //
+ if (IsHandleSpoofed(m_handleLegacyPrivateBlock, pid))
+ {
+ hr = E_ACCESSDENIED;
+ goto end;
+ }
+
+ m_ptrLegacyPrivateBlock = (LegacyPrivateIPCControlBlock*) MapViewOfFile(
+ m_handleLegacyPrivateBlock,
+ dwDesiredAccess,
+ 0, 0, 0);
+
+ if (m_ptrLegacyPrivateBlock== NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto end;
+ }
+
+ // Check if LegacyPrivate block is valid; if it is not valid,
+ // report the block as "not compatible"
+ if (!IsValidLegacy(FALSE))
+ hr = CORDBG_E_UNCOMPATIBLE_PLATFORMS;
+
+ end:;
+ }
+ EX_CATCH
+ {
+ Exception *e = GET_EXCEPTION();
+ hr = e->GetHR();
+ if (hr == S_OK)
+ {
+ hr = E_FAIL;
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (!SUCCEEDED(hr))
+ {
+ CloseLegacyPrivateBlock();
+ }
+
+ return hr;
+}
+
+//-----------------------------------------------------------------------------
+// Open our LegacyPrivate block
+//-----------------------------------------------------------------------------
+HRESULT IPCReaderInterface::OpenLegacyPrivateBlockTempV4OnPid(DWORD pid, DWORD dwDesiredAccess)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if 0
+// Note, PID != GetCurrentProcessId(), b/c we're expected to be opening
+// someone else's IPCBlock, not our own. If this isn't the case, just remove
+// this assert
+
+// exception: if we're enumerating provesses, we'll hit our own
+// _ASSERTE(pid != GetCurrentProcessId());
+#endif
+
+ // Note: if our LegacyPrivate block is open, we shouldn't be attaching to a new one.
+ _ASSERTE(!IsLegacyPrivateBlockOpen());
+ if (IsLegacyPrivateBlockOpen())
+ {
+ // if we goto errExit, it will close the file. We don't want that.
+ return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
+ }
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ // We should not be trying to open the IPC block of an x86 process from
+ // within an ia64 process, or vice versa. This can only happen on
+ // Server2003 and later.
+
+ BOOL fCompatible = FALSE;
+ hr = IsCompatablePlatformForDebuggerAndDebuggee(pid, &fCompatible);
+ if (FAILED(hr))
+ {
+ goto end;
+ }
+ if (fCompatible == FALSE)
+ {
+ hr = CORDBG_E_UNCOMPATIBLE_PLATFORMS;
+ goto end;
+ }
+
+ // In order to verify handle's owner, we need READ_CONTROL.
+ dwDesiredAccess |= READ_CONTROL;
+
+ {
+ SString szMemFileName;
+ IPCShared::GenerateNameLegacyTempV4(pid, szMemFileName);
+ m_handleLegacyPrivateBlock = WszOpenFileMapping(dwDesiredAccess,
+ FALSE,
+ szMemFileName);
+ if (m_handleLegacyPrivateBlock == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ }
+
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::OLPBTV4OP: CreateFileMapping of %S, handle = 0x%08x, pid = 0x%8.8x GetLastError=%d\n",
+ szMemFileName.GetUnicode(), m_handleLegacyPrivateBlock, pid, GetLastError()));
+ if (m_handleLegacyPrivateBlock == NULL)
+ {
+ goto end;
+ }
+ }
+
+ // Verify that the owner of the handle is the same as the user of that pid.
+ // This protects us against a 3rd user pre-creating the IPC block underneath us
+ // and tricking us into attaching to that.
+ // Even if a 3rd-party is able to spoof the IPC block, they themselves won't
+ // have access to it and so the IPC block will remain all zeros.
+ // That radically limits potential attacks that a 3rd-party could do.
+ //
+ if (IsHandleSpoofed(m_handleLegacyPrivateBlock, pid))
+ {
+ hr = E_ACCESSDENIED;
+ goto end;
+ }
+
+ m_ptrLegacyPrivateBlock = (LegacyPrivateIPCControlBlock*) MapViewOfFile(
+ m_handleLegacyPrivateBlock,
+ dwDesiredAccess,
+ 0, 0, 0);
+
+ if (m_ptrLegacyPrivateBlock== NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto end;
+ }
+
+ // Check if LegacyPrivate block is valid; if it is not valid,
+ // report the block as "not compatible"
+ if (!IsValidLegacy(FALSE))
+ hr = CORDBG_E_UNCOMPATIBLE_PLATFORMS;
+
+ end:;
+ }
+ EX_CATCH
+ {
+ Exception *e = GET_EXCEPTION();
+ hr = e->GetHR();
+ if (hr == S_OK)
+ {
+ hr = E_FAIL;
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (!SUCCEEDED(hr))
+ {
+ CloseLegacyPrivateBlock();
+ }
+
+ return hr;
+}
+
+#ifndef DACCESS_COMPILE
+
+//-----------------------------------------------------------------------------
+// Open our LegacyPublic block
+//-----------------------------------------------------------------------------
+HRESULT IPCReaderInterface::OpenLegacyPublicBlockOnPid(DWORD pid, DWORD dwDesiredAccess)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Note: if our LegacyPublic block is open, we shouldn't be attaching to a new one.
+ _ASSERTE(!IsLegacyPublicBlockOpen());
+ if (IsLegacyPublicBlockOpen())
+ {
+ // if we goto errExit, it will close the file. We don't want that.
+ return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
+ }
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ {
+ SString szMemFileName;
+ IPCShared::GenerateLegacyPublicName(pid, szMemFileName);
+ m_handleLegacyPublicBlock = WszOpenFileMapping(dwDesiredAccess,
+ FALSE,
+ szMemFileName);
+ if (m_handleLegacyPublicBlock == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ }
+
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::OPBOP: CreateFileMapping of %S, handle = 0x%08x, pid = 0x%8.8x GetLastError=%d\n",
+ szMemFileName.GetUnicode(), m_handleLegacyPublicBlock, pid, GetLastError()));
+ if (m_handleLegacyPublicBlock == NULL)
+ {
+ goto end;
+ }
+ }
+
+ m_ptrLegacyPublicBlock = (LegacyPublicIPCControlBlock*) MapViewOfFile(
+ m_handleLegacyPublicBlock,
+ dwDesiredAccess,
+ 0, 0, 0);
+ if (m_ptrLegacyPublicBlock == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto end;
+ }
+
+ // Check if the target is valid and compatible
+ if (!IsValidLegacy(TRUE))
+ {
+ hr = CORDBG_E_UNCOMPATIBLE_PLATFORMS;
+ goto end;
+ }
+
+ end:;
+ }
+ EX_CATCH
+ {
+ Exception *e = GET_EXCEPTION();
+ hr = e->GetHR();
+ if (hr == S_OK)
+ {
+ hr = E_FAIL;
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (!SUCCEEDED(hr))
+ {
+ if (m_handleLegacyPrivateBlock != NULL)
+ CloseLegacyPrivateBlock();
+
+ if (m_handleLegacyPublicBlock != NULL)
+ CloseLegacyPublicBlock();
+ }
+
+ return hr;
+}
+
+
+
+
+
+//-----------------------------------------------------------------------------
+// Open our IPCBlockTable
+//-----------------------------------------------------------------------------
+HRESULT IPCReaderInterface::OpenBlockTableOnPid(DWORD pid, DWORD dwDesiredAccess)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Note: if our IPCBlockTable is open, we shouldn't be attaching to a new one.
+ _ASSERTE(!IsBlockTableOpen());
+ if (IsBlockTableOpen())
+ {
+ // if we goto errExit, it will close the file. We don't want that.
+ return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
+ }
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ {
+ SString szMemFileName;
+
+ hr = IPCShared::GenerateBlockTableName(pid, szMemFileName, m_handleBoundaryDesc,m_handlePrivateNamespace, &m_pSID, FALSE);
+ if (FAILED(hr))
+ {
+ goto end;
+ }
+
+ m_handleBlockTable = WszOpenFileMapping(dwDesiredAccess,
+ FALSE,
+ szMemFileName);
+ if (m_handleBlockTable == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ }
+
+ LOG((LF_CORDB, LL_INFO10, "IPCRI::OPBOP: CreateFileMapping of %S, handle = 0x%08x, pid = 0x%8.8x GetLastError=%d\n",
+ szMemFileName.GetUnicode(), m_handleBlockTable, pid, GetLastError()));
+ if (m_handleBlockTable == NULL)
+ {
+ goto end;
+ }
+ }
+
+ m_pBlockTable = (IPCControlBlockTable*) MapViewOfFile(
+ m_handleBlockTable,
+ dwDesiredAccess,
+ 0, 0, 0);
+
+ if (m_pBlockTable == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto end;
+ }
+#if defined(_TARGET_X86_)
+ //get the flags of the first available block.
+ BOOL isInitialized = FALSE;
+ for (DWORD i = 0; i < IPC_NUM_BLOCKS_IN_TABLE; ++i) {
+ USHORT flags = m_pBlockTable->GetBlock(i)->m_Header.m_Flags;
+ if ((flags & IPC_FLAG_INITIALIZED) == 0) {
+ continue;
+ }
+ m_fIsTarget32Bit = ((flags & IPC_FLAG_X86) != 0);
+ isInitialized = TRUE;
+ // If this process is 32 bit and the target is 64 bit,
+ // then the target is incompatible
+ if (!m_fIsTarget32Bit){
+ hr = E_FAIL;
+ goto end;
+ }
+ break;
+ }
+ if (!isInitialized) {
+ //none of the blocks are initialized
+ hr = E_FAIL;
+ goto end;
+ }
+#endif // defined(_TARGET_X86_)
+
+ end:;
+ }
+ EX_CATCH
+ {
+ Exception *e = GET_EXCEPTION();
+ hr = e->GetHR();
+ if (hr == S_OK)
+ {
+ hr = E_FAIL;
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (!SUCCEEDED(hr))
+ {
+ if (m_handleBlockTable != NULL)
+ CloseBlockTable();
+ }
+ if(!SUCCEEDED(IPCShared::FreeHandles(m_handleBoundaryDesc,m_pSID,m_handlePrivateNamespace)))
+ {
+ hr = E_FAIL;
+ }
+ return hr;
+}
+
+HRESULT IPCReaderInterface::OpenBlockTableOnPid(DWORD pid)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (OpenBlockTableOnPid(pid, FILE_MAP_ALL_ACCESS));
+}
+
+HRESULT IPCReaderInterface::OpenBlockTableOnPidReadOnly(DWORD pid)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (OpenBlockTableOnPid(pid, FILE_MAP_READ));
+}
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Open our LegacyPrivate block for all access
+//-----------------------------------------------------------------------------
+HRESULT IPCReaderInterface::OpenLegacyPrivateBlockOnPid(DWORD pid)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (OpenLegacyPrivateBlockOnPid(pid, FILE_MAP_ALL_ACCESS));
+}
+
+//-----------------------------------------------------------------------------
+// Open our LegacyPrivate block for all access
+//-----------------------------------------------------------------------------
+HRESULT IPCReaderInterface::OpenLegacyPrivateBlockTempV4OnPid(DWORD pid)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (OpenLegacyPrivateBlockTempV4OnPid(pid, FILE_MAP_ALL_ACCESS));
+}
+
+#ifndef DACCESS_COMPILE
+
+//-----------------------------------------------------------------------------
+// Open our LegacyPublic block for all access
+//-----------------------------------------------------------------------------
+HRESULT IPCReaderInterface::OpenLegacyPublicBlockOnPid(DWORD pid)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (OpenLegacyPublicBlockOnPid(pid, FILE_MAP_ALL_ACCESS));
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Open our LegacyPrivate block for read/write access
+//-----------------------------------------------------------------------------
+HRESULT IPCReaderInterface::OpenLegacyPrivateBlockOnPidReadWrite(DWORD pid)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (OpenLegacyPrivateBlockOnPid(pid, FILE_MAP_READ | FILE_MAP_WRITE));
+}
+
+//-----------------------------------------------------------------------------
+// Open our LegacyPrivate block for read only access
+//-----------------------------------------------------------------------------
+HRESULT IPCReaderInterface::OpenLegacyPrivateBlockOnPidReadOnly(DWORD pid)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (OpenLegacyPrivateBlockOnPid(pid, FILE_MAP_READ));
+}
+
+#ifndef DACCESS_COMPILE
+
+//-----------------------------------------------------------------------------
+// Open our LegacyPublic block for read only access
+//-----------------------------------------------------------------------------
+HRESULT IPCReaderInterface::OpenLegacyPublicBlockOnPidReadOnly(DWORD pid)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (OpenLegacyPublicBlockOnPid(pid, FILE_MAP_READ));
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Get a client's LegacyPrivate block based on enum
+// This is a robust function.
+// It will return NULL if:
+// * the IPC block is closed (also ASSERT),
+// * the eClient is out of range (From version mismatch)
+// * the request block is removed (probably version mismatch)
+// Else it will return a pointer to the requested block
+//-----------------------------------------------------------------------------
+void * IPCReaderInterface::GetLegacyPrivateBlock(ELegacyPrivateIPCClient eClient)
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(IsLegacyPrivateBlockOpen());
+
+ // This block doesn't exist if we're closed or out of the table's range
+ if (!IsLegacyPrivateBlockOpen() || (DWORD) eClient >= m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_numEntries)
+ {
+ return NULL;
+ }
+
+ if (Internal_CheckEntryEmptyLegacyPrivate(*m_ptrLegacyPrivateBlock,eClient))
+ {
+ return NULL;
+ }
+
+ return Internal_GetBlockLegacyPrivate(*m_ptrLegacyPrivateBlock,eClient);
+}
+
+#ifndef DACCESS_COMPILE
+
+//-----------------------------------------------------------------------------
+// Get a client's LegacyPublic block based on enum
+// This is a robust function.
+// It will return NULL if:
+// * the IPC block is closed (also ASSERT),
+// * the eClient is out of range (From version mismatch)
+// * the requested block is removed (probably version mismatch)
+// Else it will return a pointer to the requested block
+//-----------------------------------------------------------------------------
+void * IPCReaderInterface::GetLegacyPublicBlock(ELegacyPublicIPCClient eClient)
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(IsLegacyPublicBlockOpen());
+
+ // This block doesn't exist if we're closed or out of the table's range
+ if (!IsLegacyPublicBlockOpen())
+ return NULL;
+
+ DWORD dwNumEntries = GetNumEntriesLegacy(m_ptrLegacyPublicBlock);
+
+ if ((DWORD) eClient >= dwNumEntries)
+ return NULL;
+
+ if (Internal_CheckEntryEmptyLegacyPublic(eClient))
+ return NULL;
+
+ return Internal_GetBlockLegacyPublic(eClient);
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Is our LegacyPrivate block open?
+//-----------------------------------------------------------------------------
+bool IPCReaderInterface::IsLegacyPrivateBlockOpen() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_ptrLegacyPrivateBlock != NULL;
+}
+
+#ifndef DACCESS_COMPILE
+
+bool IPCReaderInterface::IsLegacyPublicBlockOpen() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_ptrLegacyPublicBlock != NULL;
+}
+
+bool IPCReaderInterface::IsBlockTableOpen() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pBlockTable != NULL;
+}
+
+void * IPCReaderInterface::GetPerfBlockLegacyPublic()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (PerfCounterIPCControlBlock*) GetLegacyPublicBlock(eLegacyPublicIPC_PerfCounters);
+}
+
+#endif
+
+void * IPCReaderInterface::GetPerfBlockLegacyPrivate()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (PerfCounterIPCControlBlock*) GetLegacyPrivateBlock(eLegacyPrivateIPC_PerfCounters);
+}
+
+AppDomainEnumerationIPCBlock * IPCReaderInterface::GetAppDomainBlock()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (AppDomainEnumerationIPCBlock*) GetLegacyPrivateBlock(eLegacyPrivateIPC_AppDomain);
+}
+
+//-----------------------------------------------------------------------------
+// Check if the block is valid. Current checks include:
+// * Check Flags
+// * Check Directory structure
+// * Check Bitness (LegacyPublic block only)
+//-----------------------------------------------------------------------------
+BOOL IPCReaderInterface::IsValidLegacy(BOOL fIsLegacyPublicBlock)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Initialize the pBlock pointer to point to the specified block;
+ // specified block must be open
+
+ void * pBlock = (fIsLegacyPublicBlock ? (void*)m_ptrLegacyPublicBlock : (void*)m_ptrLegacyPrivateBlock);
+ _ASSERTE(pBlock != NULL);
+
+ // Check if block size has been initialized
+ DWORD dwBlockSize = GetBlockSizeLegacy(pBlock);
+ if (dwBlockSize == 0)
+ return FALSE;
+
+ // If this IPC block uses the flags field and the initialized flag isn't set,
+ // then the block has not been initialized
+ USHORT flags = GetFlagsLegacy(pBlock);
+ BOOL fUsesFlags = (flags & IPC_FLAG_USES_FLAGS);
+ if (fUsesFlags && (flags & IPC_FLAG_INITIALIZED) == 0)
+ return FALSE;
+
+ // If this is the LegacyPublic block, then we need to check bitness; if it
+ // turns out that the bitness is incompatible, return FALSE
+ if (fIsLegacyPublicBlock)
+ {
+ // If this IPC block uses the flags field, then use the flags to
+ // determine the bitness of the target block
+ if (fUsesFlags)
+ {
+ m_fIsTarget32Bit = ((flags & IPC_FLAG_X86) != 0);
+ }
+
+ // Otherwise, this IPC block does not use the flags field
+ else
+ {
+ // Use block size to determine the bitness of the target block
+ m_fIsTarget32Bit = (dwBlockSize == LEGACYPUBLIC_IPC_BLOCK_SIZE_32);
+
+ // If block size is not equal known values from used by older
+ // versions of the CLR, then assume this block is not compatible
+ _ASSERTE(m_fIsTarget32Bit || dwBlockSize == LEGACYPUBLIC_IPC_BLOCK_SIZE_64);
+ }
+
+#if defined(_DEBUG)
+ m_fInitialized = TRUE;
+#endif //_DEBUG
+
+#if defined(_TARGET_X86_)
+ // If this process is 32 bit and the target is 64 bit,
+ // then the target is incompatible
+ if (!m_fIsTarget32Bit)
+ return FALSE;
+#endif //_TARGET_X86_
+ }
+
+ // If this IPC block uses the flags field and this is not a
+ // debug build, then no further checks are necessary.
+#if !defined(_DEBUG)
+ if (fUsesFlags)
+ return TRUE;
+#endif //_DEBUG
+
+ // Make sure numEntries has been initialized
+ DWORD dwNumEntries = GetNumEntriesLegacy(pBlock);
+ if (dwNumEntries == 0)
+ {
+ // This assert will fail only if the IPC block uses flags and
+ // 'm_numEntries' has not been initialized
+ _ASSERTE(!fUsesFlags && "m_numEntries is not initialized");
+ return FALSE;
+ }
+
+ // Make sure that block size is not too small
+ SIZE_T cbOffsetBase = (SIZE_T)GetOffsetBaseLegacy() + dwNumEntries * sizeof(IPCEntry);
+ if (dwBlockSize < cbOffsetBase)
+ {
+ _ASSERTE(!"m_blockSize is too small or m_numEntries is too big (1)");
+ return FALSE;
+ }
+
+ // Check to make sure that the expected offset for the end of
+ // m_table does not go past the end of the block
+ SIZE_T offsetExpected = GetFirstExpectedOffsetLegacy();
+ SIZE_T offsetLast = dwBlockSize - cbOffsetBase;
+ if (offsetExpected > offsetLast)
+ {
+ _ASSERTE(!"m_blockSize is too small or m_numEntries is too big (2)");
+ return FALSE;
+ }
+
+ // Check each entries offset and size to make sure they are correct
+ IPCEntry * table = GetDirectoryLegacy(pBlock);
+ for(DWORD i = 0; i < dwNumEntries; ++i)
+ {
+ SIZE_T entryOffset = table[i].m_Offset;
+ SIZE_T entrySize = table[i].m_Size;
+
+ if (entryOffset == EMPTY_ENTRY_OFFSET)
+ {
+ // Verify that this entry has size of EMPTY_ENTRY_SIZE
+ if (entrySize != EMPTY_ENTRY_SIZE)
+ {
+ _ASSERTE(!"Empty entry has size that does not equal EMPTY_ENTRY_SIZE");
+ return FALSE;
+ }
+ }
+ else
+ {
+ // Verify that this entry has non-zero size
+ if (entrySize == 0)
+ {
+ // This assert will fail only if the IPC block uses flags and
+ // 'm_Size' has not been initialized
+ _ASSERTE(!fUsesFlags && "m_Size is not initialized");
+ return FALSE;
+ }
+
+ // Verify that the actual offset equals the expected offset
+ if (entryOffset != offsetExpected)
+ {
+ if (entryOffset == 0)
+ {
+ // This assert will only fail if the IPC block uses flags and
+ // 'm_Offset' has not been initialized
+ _ASSERTE(!fUsesFlags && "m_Offset is not initialized");
+ }
+ else
+ {
+ // This assert will fail if 'm_Offset' has been initialized
+ // but does not equal the expected value
+ _ASSERTE(!"Actual offset does not equal to expected offset");
+ }
+
+ return FALSE;
+ }
+
+ // Compute the next expected offset
+ offsetExpected += entrySize;
+ }
+ }
+
+ // Verify that the end of the last entry is equal to the
+ // end of the IPC block
+ if (offsetExpected != offsetLast)
+ {
+ _ASSERTE(!"End of last entry does not equal end of IPC block");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL IPCReaderInterface::TryOpenBlock(IPCHeaderReadHelper & readHelper, DWORD blockIndex)
+{
+ _ASSERTE(blockIndex < IPC_NUM_BLOCKS_IN_TABLE);
+ readHelper.CloseHeader();
+ return readHelper.TryOpenHeader(&m_pBlockTable->m_blocks[blockIndex].m_Header);
+}
+
+USHORT IPCReaderInterface::GetBlockVersion()
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(IsLegacyPrivateBlockOpen());
+ return m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_Version;
+}
+
+#ifndef DACCESS_COMPILE
+
+USHORT IPCReaderInterface::GetLegacyPublicBlockVersion()
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(IsLegacyPublicBlockOpen());
+ return m_ptrLegacyPublicBlock->m_FullIPCHeaderLegacyPublic.m_header.m_Version;
+}
+
+#endif
+
+HINSTANCE IPCReaderInterface::GetInstance()
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(IsLegacyPrivateBlockOpen());
+ return m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_hInstance;
+}
+
+USHORT IPCReaderInterface::GetBuildYear()
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(IsLegacyPrivateBlockOpen());
+ return m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_BuildYear;
+}
+
+USHORT IPCReaderInterface::GetBuildNumber()
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(IsLegacyPrivateBlockOpen());
+ return m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_BuildNumber;
+}
+
+PVOID IPCReaderInterface::GetBlockStart()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (PVOID) m_ptrLegacyPrivateBlock;
+}
+
+PCWSTR IPCReaderInterface::GetInstancePath()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (PCWSTR) GetLegacyPrivateBlock(eLegacyPrivateIPC_InstancePath);
+}
+
+#endif // FEATURE_IPCMAN
diff --git a/src/ipcman/ipcshared.h b/src/ipcman/ipcshared.h
new file mode 100644
index 0000000000..659f14972a
--- /dev/null
+++ b/src/ipcman/ipcshared.h
@@ -0,0 +1,95 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+//*****************************************************************************
+// File: IPCShared.h
+//
+// Shared LegacyPrivate utility functions for COM+ IPC operations
+//
+//*****************************************************************************
+
+#ifndef _IPCSHARED_H_
+#define _IPCSHARED_H_
+
+
+#include "ipcenums.h"
+
+
+class SString;
+
+// This is the name of the file backed session's name on the LS (debuggee)
+// Name of the LegacyPrivate (per-process) block. %d resolved to a PID
+#define CorLegacyPrivateIPCBlock L"Cor_Private_IPCBlock_%d"
+#define CorLegacyPrivateIPCBlockTempV4 L"Cor_Private_IPCBlock_v4_%d"
+#define CorLegacyPublicIPCBlock L"Cor_Public_IPCBlock_%d"
+#define CorSxSPublicIPCBlock L"Cor_SxSPublic_IPCBlock_%d"
+#define CorSxSBoundaryDescriptor L"Cor_CLR_IPCBlock_%d"
+#define CorSxSWriterPrivateNamespacePrefix L"Cor_CLR_WRITER"
+#define CorSxSReaderPrivateNamespacePrefix L"Cor_CLR_READER"
+#define CorSxSVistaPublicIPCBlock L"Cor_SxSPublic_IPCBlock"
+
+#define CorLegacyPrivateIPCBlock_RS L"CLR_PRIVATE_RS_IPCBlock_%d"
+#define CorLegacyPrivateIPCBlock_RSTempV4 L"CLR_PRIVATE_RS_IPCBlock_v4_%d"
+#define CorLegacyPublicIPCBlock_RS L"CLR_PUBLIC_IPCBlock_%d"
+#define CorSxSPublicIPCBlock_RS L"CLR_SXSPUBLIC_IPCBlock_%d"
+
+#define CorSxSPublicInstanceName L"%s_p%d_r%d"
+#define CorSxSPublicInstanceNameWhidbey L"%s_p%d"
+
+// NOTE: we cannot just remove this otherwise 'FeatureCoreClr' build breaks
+// since this is not defined in old SDK header
+#ifndef CREATE_BOUNDARY_DESCRIPTOR_ADD_APPCONTAINER_SID
+#define CREATE_BOUNDARY_DESCRIPTOR_ADD_APPCONTAINER_SID 0x1
+#endif
+// ENDNOTE
+
+enum KernelObject
+{
+ Section,
+ Event,
+ PrivateNamespace,
+ TotalKernelObjects
+};
+
+
+class IPCShared
+{
+public:
+// Close a handle and pointer to any memory mapped file
+ static void CloseMemoryMappedFile(HANDLE & hMemFile, void * & pBlock);
+
+// Based on the pid, write a unique name for a memory mapped file
+ static void GenerateName(DWORD pid, SString & sName);
+ static void GenerateNameLegacyTempV4(DWORD pid, SString & sName);
+ static void GenerateLegacyPublicName(DWORD pid, SString & sName);
+
+ static HRESULT GenerateBlockTableName(DWORD pid,
+ SString & sName,
+ HANDLE & pBoundaryDesc,
+ HANDLE & pPrivateNamespace,
+ PSID* pSID,
+ BOOL bCreate);
+ static HRESULT IPCShared::FreeHandles(HANDLE & hDescriptor, PSID & pSID);
+ static HRESULT IPCShared::FreeHandles(HANDLE & hBoundaryDescriptor, PSID & pSID, HANDLE & hPrivateNamespace);
+ static HRESULT CreateWinNTDescriptor(DWORD pid, BOOL bRestrictiveACL, SECURITY_ATTRIBUTES **ppSA, KernelObject whatObject);
+ static HRESULT CreateWinNTDescriptor(DWORD pid, BOOL bRestrictiveACL, SECURITY_ATTRIBUTES **ppSA, KernelObject whatObject, EDescriptorType descType);
+ static void DestroySecurityAttributes(SECURITY_ATTRIBUTES *pSA);
+
+private:
+ static const int MaxNumberACEs = 5;
+ static BOOL InitializeGenericIPCAcl(DWORD pid, BOOL bRestrictiveACL, PACL *ppACL, KernelObject whatObject, EDescriptorType descType);
+ static DWORD GetAccessFlagsForObject(KernelObject whatObject, BOOL bRestrictiveACL);
+ static HRESULT GetSidForProcess(HINSTANCE hDll,
+ DWORD pid,
+ PSID *ppSID,
+ __deref_out_opt char **ppBufferToFreeByCaller);
+};
+
+#endif
diff --git a/src/ipcman/ipcsharedsrc.cpp b/src/ipcman/ipcsharedsrc.cpp
new file mode 100644
index 0000000000..95fd9d5649
--- /dev/null
+++ b/src/ipcman/ipcsharedsrc.cpp
@@ -0,0 +1,966 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+//*****************************************************************************
+// File: IPCSharedSrc.cpp
+//
+// Shared source for COM+ IPC Reader & Writer classes
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "ipcshared.h"
+#include "ipcmanagerinterface.h"
+
+#ifndef FEATURE_CORECLR
+#include "AppXUtil.h"
+#endif
+
+#if defined(FEATURE_IPCMAN)
+
+//-----------------------------------------------------------------------------
+// Close a handle and pointer to any memory mapped file
+//-----------------------------------------------------------------------------
+void IPCShared::CloseMemoryMappedFile(HANDLE & hMemFile, void * & pBlock)
+{
+ WRAPPER_NO_CONTRACT;
+
+ LOG((LF_CORDB, LL_INFO10, "IPCS::CloseMemoryMappedFile: closing 0x%08x\n", hMemFile));
+
+ if (pBlock != NULL) {
+ if (!UnmapViewOfFile(pBlock))
+ _ASSERTE(!"UnmapViewOfFile failed");
+ pBlock = NULL;
+ }
+
+ if (hMemFile != NULL) {
+ CloseHandle(hMemFile);
+ hMemFile = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Based on the pid, write a unique name for a memory mapped file
+//-----------------------------------------------------------------------------
+void IPCShared::GenerateName(DWORD pid, SString & sName)
+{
+ WRAPPER_NO_CONTRACT;
+
+ const WCHAR * szFormat = CorLegacyPrivateIPCBlock;
+ szFormat = L"Global\\" CorLegacyPrivateIPCBlock;
+
+ sName.Printf(szFormat, pid);
+}
+
+//-----------------------------------------------------------------------------
+// Based on the pid, write a unique name for a memory mapped file
+//-----------------------------------------------------------------------------
+void IPCShared::GenerateNameLegacyTempV4(DWORD pid, SString & sName)
+{
+ WRAPPER_NO_CONTRACT;
+
+ const WCHAR * szFormat = CorLegacyPrivateIPCBlockTempV4;
+ szFormat = L"Global\\" CorLegacyPrivateIPCBlockTempV4;
+
+ sName.Printf(szFormat, pid);
+}
+
+//-----------------------------------------------------------------------------
+// Based on the pid, write a unique name for a memory mapped file
+//-----------------------------------------------------------------------------
+void IPCShared::GenerateLegacyPublicName(DWORD pid, SString & sName)
+{
+ WRAPPER_NO_CONTRACT;
+
+ const WCHAR * szFormat = CorLegacyPublicIPCBlock;
+ szFormat = L"Global\\" CorLegacyPublicIPCBlock;
+
+ sName.Printf(szFormat, pid);
+}
+
+#ifndef DACCESS_COMPILE
+
+//-----------------------------------------------------------------------------
+// Based on the pid, write a unique name for the IPCBlockTable on Vista and Higher
+//-----------------------------------------------------------------------------
+HRESULT IPCShared::GenerateBlockTableName(DWORD pid, SString & sName, HANDLE & pBoundaryDesc, HANDLE & pPrivateNamespace, PSID* pSID, BOOL bCreate)
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr = E_FAIL;
+
+#define SIZE 100
+ const WCHAR * szFormat = CorSxSPublicIPCBlock;
+ static HMODULE hKernel32 = NULL;
+ if(hKernel32 == NULL)
+ hKernel32 = WszGetModuleHandle(L"kernel32.dll");
+ if(hKernel32 == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+ //We are using static function pointers so that we dont call GetProcAddress every time
+ //We know that the Writer will call this function only once and the reader (perfmon) is a single
+ //threaded App. Therefore its safe to assign static local variables in this case.
+ typedef WINBASEAPI BOOL (WINAPI ADD_SID_TO_BOUNDARY_DESCRIPTOR)(HANDLE*, PSID);
+ static ADD_SID_TO_BOUNDARY_DESCRIPTOR * pAddSIDToBoundaryDescriptor = NULL;
+
+ typedef WINBASEAPI HANDLE (WINAPI CREATE_BOUNDARY_DESCRIPTOR)(LPCWSTR,ULONG);
+ static CREATE_BOUNDARY_DESCRIPTOR * pCreateBoundaryDescriptor = NULL;
+
+ typedef WINBASEAPI HANDLE (WINAPI CREATE_PRIVATE_NAMESPACE )(LPSECURITY_ATTRIBUTES, LPVOID, LPCWSTR);
+ static CREATE_PRIVATE_NAMESPACE * pCreatePrivateNamespace = NULL;
+
+ typedef WINBASEAPI HANDLE (WINAPI OPEN_PRIVATE_NAMESPACE)(LPVOID,LPCWSTR);
+ static OPEN_PRIVATE_NAMESPACE * pOpenPrivateNamespace = NULL;
+
+ if(pAddSIDToBoundaryDescriptor == NULL)
+ pAddSIDToBoundaryDescriptor = (ADD_SID_TO_BOUNDARY_DESCRIPTOR *)GetProcAddress(hKernel32, "AddSIDToBoundaryDescriptor");
+ if(pCreateBoundaryDescriptor == NULL)
+ pCreateBoundaryDescriptor = (CREATE_BOUNDARY_DESCRIPTOR *)GetProcAddress(hKernel32, "CreateBoundaryDescriptorW");
+ if(pCreatePrivateNamespace == NULL)
+ pCreatePrivateNamespace = (CREATE_PRIVATE_NAMESPACE *)GetProcAddress(hKernel32, "CreatePrivateNamespaceW");
+ if(pOpenPrivateNamespace==NULL)
+ pOpenPrivateNamespace = (OPEN_PRIVATE_NAMESPACE *)GetProcAddress(hKernel32, "OpenPrivateNamespaceW");
+ _ASSERTE((pAddSIDToBoundaryDescriptor != NULL) &&
+ (pCreateBoundaryDescriptor != NULL) &&
+ (pCreatePrivateNamespace != NULL) &&
+ (pOpenPrivateNamespace != NULL));
+
+ if ((pAddSIDToBoundaryDescriptor == NULL) ||
+ (pCreateBoundaryDescriptor == NULL) ||
+ (pCreatePrivateNamespace == NULL) ||
+ (pOpenPrivateNamespace == NULL))
+ {
+ return ERROR_PROC_NOT_FOUND;
+ }
+
+ WCHAR wsz[SIZE];
+ swprintf_s(wsz,SIZE, CorSxSBoundaryDescriptor, pid);
+
+ ULONG flags = 0;
+ if (RunningOnWin8())
+ {
+ // on win8 we specify this flag regardless if the process is inside an appcontainer, the kernel will do the right thing.
+ // note that for appcontainers this flag is necessary regardless of producer or consumer, ie you can't create a boundary
+ // descriptor in an appcontainer process without adding the appcontainer SID (the API call will fail).
+ flags |= CREATE_BOUNDARY_DESCRIPTOR_ADD_APPCONTAINER_SID;
+ }
+
+ pBoundaryDesc = (*pCreateBoundaryDescriptor)((LPCWSTR)&wsz, flags);
+ if(!pBoundaryDesc)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+ SID_IDENTIFIER_AUTHORITY SIDWorldAuth = SECURITY_WORLD_SID_AUTHORITY;
+ if(!AllocateAndInitializeSid( &SIDWorldAuth, 1,SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, pSID))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+ if(!(*pAddSIDToBoundaryDescriptor) (&pBoundaryDesc,*pSID))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+
+#ifndef FEATURE_CORECLR
+ // when pid != GetCurrentProcessId() it means we're the consumer opening other process perf counter data
+ if (pid != GetCurrentProcessId())
+ {
+ // if the target process is inside an appcontainer we need to add the appcontainer SID to the boundary descriptor.
+ NewArrayHolder<BYTE> pbTokenMem;
+ hr = AppX::GetAppContainerTokenInfoForProcess(pid, pbTokenMem);
+
+ if (FAILED(hr))
+ {
+ // failed to open the target's process, continue on
+ // assuming that the process isn't in an AppContainer.
+ _ASSERTE(pbTokenMem == NULL);
+ }
+ else
+ {
+ if (hr == S_FALSE)
+ {
+ // not an appcontainer
+ _ASSERTE(pbTokenMem == NULL);
+ }
+ else
+ {
+ // process is an appcontainer so add the SID
+ PTOKEN_APPCONTAINER_INFORMATION pAppContainerTokenInfo =
+ reinterpret_cast<PTOKEN_APPCONTAINER_INFORMATION>(pbTokenMem.GetValue());
+ _ASSERTE(pAppContainerTokenInfo);
+ _ASSERTE(pAppContainerTokenInfo->TokenAppContainer);
+
+ if (!(*pAddSIDToBoundaryDescriptor)(&pBoundaryDesc, pAppContainerTokenInfo->TokenAppContainer))
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+ }
+#endif // FEATURE_CORECLR
+
+ if(bCreate)
+ {
+ SECURITY_ATTRIBUTES *pSA = NULL;
+ IPCShared::CreateWinNTDescriptor(pid, FALSE, &pSA, PrivateNamespace, eDescriptor_Public);
+ pPrivateNamespace = (*pCreatePrivateNamespace)(pSA, (VOID *)(pBoundaryDesc),
+ (LPCWSTR)CorSxSWriterPrivateNamespacePrefix);
+ if(!pPrivateNamespace)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ }
+ IPCShared::DestroySecurityAttributes(pSA);
+
+ if(!pPrivateNamespace)
+ {
+ //if already created by a different version of the runtime we return OK.
+ if(hr ==HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
+ {
+ hr = S_OK;
+ }
+ else
+ {
+ return hr;
+ }
+ }
+ }
+ else
+ {
+ pPrivateNamespace = (*pOpenPrivateNamespace)((VOID *)(pBoundaryDesc), (LPCWSTR)CorSxSReaderPrivateNamespacePrefix);
+ if(!pPrivateNamespace)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+ }
+ szFormat = (bCreate ? CorSxSWriterPrivateNamespacePrefix L"\\" CorSxSVistaPublicIPCBlock : CorSxSReaderPrivateNamespacePrefix L"\\" CorSxSVistaPublicIPCBlock);
+ sName.Printf(szFormat);
+ hr=S_OK;
+
+ return hr;
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Free's the handle to a boundary descriptor and a SID
+//-----------------------------------------------------------------------------
+HRESULT IPCShared::FreeHandles(HANDLE & hBoundaryDescriptor, PSID & pSID)
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr = S_OK;
+ if(hBoundaryDescriptor != NULL)
+ {
+ static HMODULE hKernel32 = NULL;
+ if(hKernel32 == NULL)
+ hKernel32 = WszGetModuleHandle(L"kernel32.dll");
+ if(hKernel32 == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+ typedef WINBASEAPI VOID (WINAPI DELETE_BOUNDARY_DESCRIPTOR)(HANDLE);
+ static DELETE_BOUNDARY_DESCRIPTOR * pDeleteBoundaryDescriptor = NULL;
+ if(pDeleteBoundaryDescriptor == NULL)
+ pDeleteBoundaryDescriptor = (DELETE_BOUNDARY_DESCRIPTOR *)GetProcAddress(hKernel32, "DeleteBoundaryDescriptor");
+ _ASSERTE(pDeleteBoundaryDescriptor != NULL);
+ if (pDeleteBoundaryDescriptor == NULL)
+ {
+ hr = ERROR_PROC_NOT_FOUND;
+ }
+ else
+ {
+ (*pDeleteBoundaryDescriptor)(hBoundaryDescriptor);
+ hBoundaryDescriptor = NULL;
+
+ }
+ }
+ if(pSID != NULL)
+ {
+ FreeSid(pSID);
+ pSID = NULL;
+ }
+
+ return hr;
+}
+
+//--------------------------------------------------------------------------------------
+// Free's the handle to a boundary descriptor, a SID and a handle to a privatenamespace
+//--------------------------------------------------------------------------------------
+HRESULT IPCShared::FreeHandles(HANDLE & hBoundaryDescriptor, PSID & pSID, HANDLE & hPrivateNamespace)
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr = S_OK;
+
+ hr = IPCShared::FreeHandles(hBoundaryDescriptor,pSID);
+ if(!SUCCEEDED(hr))
+ return hr;
+ if(hPrivateNamespace != NULL)
+ {
+ static HMODULE hKernel32 = NULL;
+ if(hKernel32 == NULL)
+ hKernel32 = WszGetModuleHandle(L"kernel32.dll");
+ if(hKernel32 == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+ typedef WINBASEAPI BOOL (WINAPI CLOSE_PRIVATE_NAMESPACE)(HANDLE, ULONG);
+ static CLOSE_PRIVATE_NAMESPACE * pClosePrivateNamespace;
+ if(pClosePrivateNamespace == NULL)
+ pClosePrivateNamespace = (CLOSE_PRIVATE_NAMESPACE *)GetProcAddress(hKernel32, "ClosePrivateNamespace");
+ _ASSERTE(pClosePrivateNamespace != NULL);
+ if (pClosePrivateNamespace == NULL)
+ {
+ hr = ERROR_PROC_NOT_FOUND;
+ }
+ else
+ {
+ BOOL isClosed = (*pClosePrivateNamespace)(hPrivateNamespace,0);
+ hPrivateNamespace = NULL;
+ if(!isClosed)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ }
+
+ }
+ }
+
+ return hr;
+}
+
+HRESULT IPCShared::CreateWinNTDescriptor(DWORD pid, BOOL bRestrictiveACL, SECURITY_ATTRIBUTES **ppSA, KernelObject whatObject)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return IPCShared::CreateWinNTDescriptor(pid, bRestrictiveACL, ppSA, whatObject, eDescriptor_Private);
+}
+
+//-----------------------------------------------------------------------------
+// Setup a security descriptor for the named kernel objects if we're on NT.
+//-----------------------------------------------------------------------------
+
+HRESULT IPCShared::CreateWinNTDescriptor(DWORD pid, BOOL bRestrictiveACL, SECURITY_ATTRIBUTES **ppSA, KernelObject whatObject, EDescriptorType descType)
+{
+ WRAPPER_NO_CONTRACT;
+
+ HRESULT hr = NO_ERROR;
+
+ // Gotta have a place to stick the new SA...
+ if (ppSA == NULL)
+ {
+ _ASSERTE(!"Caller must supply ppSA");
+ return E_INVALIDARG;
+ }
+
+ *ppSA = NULL;
+
+ ACL *pACL = NULL;
+ SECURITY_DESCRIPTOR *pSD = NULL;
+ SECURITY_ATTRIBUTES *pSA = NULL;
+
+ // Allocate a SD.
+ _ASSERTE (SECURITY_DESCRIPTOR_MIN_LENGTH == sizeof(SECURITY_DESCRIPTOR));
+ pSD = new (nothrow) SECURITY_DESCRIPTOR;
+
+ if (pSD == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto errExit;
+ }
+
+ // Do basic SD initialization
+ if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto errExit;
+ }
+
+ // Grab the ACL for the IPC block for the given process
+ if (!InitializeGenericIPCAcl(pid, bRestrictiveACL, &pACL, whatObject, descType))
+ {
+ hr = E_FAIL;
+ goto errExit;
+ }
+
+ // Add the ACL as the DACL for the SD.
+ if (!SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto errExit;
+ }
+
+ // Allocate a SA.
+ pSA = new (nothrow) SECURITY_ATTRIBUTES;
+
+ if (pSA == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto errExit;
+ }
+
+ // Pass out the new SA.
+ *ppSA = pSA;
+
+ pSA->nLength = sizeof(SECURITY_ATTRIBUTES);
+ pSA->lpSecurityDescriptor = pSD;
+ pSA->bInheritHandle = FALSE;
+
+ // uncomment this line if you want to see the DACL being generated.
+ //DumpSD(pSD);
+
+errExit:
+ if (FAILED(hr))
+ {
+ if (pACL != NULL)
+ {
+ for(int i = 0; i < pACL->AceCount; i++)
+ DeleteAce(pACL, i);
+
+ delete [] pACL;
+ }
+
+ if (pSD != NULL)
+ delete pSD;
+ }
+
+ return hr;
+}
+
+//-----------------------------------------------------------------------------
+// Helper to destroy the security attributes for the shared memory for a given
+// process.
+//-----------------------------------------------------------------------------
+void IPCShared::DestroySecurityAttributes(SECURITY_ATTRIBUTES *pSA)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // We'll take a NULL param just to be nice.
+ if (pSA == NULL)
+ return;
+
+ // Cleanup the DACL in the SD.
+ SECURITY_DESCRIPTOR *pSD = (SECURITY_DESCRIPTOR*) pSA->lpSecurityDescriptor;
+
+ if (pSD != NULL)
+ {
+ // Grab the DACL
+ BOOL isDACLPresent = FALSE;
+ BOOL isDefaultDACL = FALSE;
+ ACL *pACL = NULL;
+
+ BOOL res = GetSecurityDescriptorDacl(pSD, &isDACLPresent, &pACL, &isDefaultDACL);
+
+ // If we got the DACL, then free the stuff inside of it.
+ if (res && isDACLPresent && (pACL != NULL) && !isDefaultDACL)
+ {
+ for(int i = 0; i < pACL->AceCount; i++)
+ DeleteAce(pACL, i);
+
+ delete [] pACL;
+ }
+
+ // Free the SD from within the SA.
+ delete pSD;
+ }
+
+ // Finally, free the SA.
+ delete pSA;
+}
+
+//-----------------------------------------------------------------------------
+// Given a PID, grab the SID for the owner of the process.
+//
+// NOTE:: Caller has to free *ppBufferToFreeByCaller.
+// This buffer is allocated to hold the PSID return by GetPrcoessTokenInformation.
+// The tkOwner field may contain a poniter into this allocated buffer. So we cannot free
+// the buffer in GetSidForProcess.
+//
+//-----------------------------------------------------------------------------
+HRESULT IPCShared::GetSidForProcess(HINSTANCE hDll,
+ DWORD pid,
+ PSID *ppSID,
+ __deref_out_opt char **ppBufferToFreeByCaller)
+{
+ WRAPPER_NO_CONTRACT;
+
+ HRESULT hr = S_OK;
+ HANDLE hProc = NULL;
+ HANDLE hToken = NULL;
+ PSID_IDENTIFIER_AUTHORITY pSID = NULL;
+ TOKEN_OWNER *ptkOwner = NULL;
+ DWORD dwRetLength;
+
+ LOG((LF_CORDB, LL_INFO10, "IPCWI::GSFP: GetSidForProcess 0x%x (%d)", pid, pid));
+
+ // Grab a handle to the target process.
+ hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
+
+ *ppBufferToFreeByCaller = NULL;
+
+ if (hProc == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::GSFP: Unable to get SID for process. "
+ "OpenProcess(%d) failed: 0x%08x\n", pid, hr));
+
+ goto ErrorExit;
+ }
+
+ // Get the pointer to the requested function
+ FARPROC pProcAddr = GetProcAddress(hDll, "OpenProcessToken");
+
+ // If the proc address was not found, return error
+ if (pProcAddr == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::GSFP: Unable to get SID for process. "
+ "GetProcAddr (OpenProcessToken) failed: 0x%08x\n", hr));
+
+ goto ErrorExit;
+ }
+
+ typedef BOOL WINAPI OPENPROCESSTOKEN(HANDLE, DWORD, PHANDLE);
+
+ // Retrieve a handle of the access token
+ if (!((OPENPROCESSTOKEN *)pProcAddr)(hProc, TOKEN_QUERY, &hToken))
+ {
+ hr = HRESULT_FROM_GetLastError();
+
+ LOG((LF_CORDB, LL_INFO100,
+ "IPCWI::GSFP: OpenProcessToken() failed: 0x%08x\n", hr));
+
+ goto ErrorExit;
+ }
+
+ // Get the pointer to the requested function
+ pProcAddr = GetProcAddress(hDll, "GetTokenInformation");
+
+ // If the proc address was not found, return error
+ if (pProcAddr == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::GSFP: Unable to get SID for process. "
+ "GetProcAddr (GetTokenInformation) failed: 0x%08x\n", hr));
+
+ goto ErrorExit;
+ }
+
+ typedef BOOL GETTOKENINFORMATION(HANDLE, TOKEN_INFORMATION_CLASS, LPVOID,
+ DWORD, PDWORD);
+
+ // get the required size of buffer
+ ((GETTOKENINFORMATION *)pProcAddr) (hToken, TokenOwner, NULL,
+ 0, &dwRetLength);
+ _ASSERTE (dwRetLength);
+
+ *ppBufferToFreeByCaller = new (nothrow) char [dwRetLength];
+ if ((ptkOwner = (TOKEN_OWNER *) *ppBufferToFreeByCaller) == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::GSFP: OutOfMemory... "
+ "GetTokenInformation() failed.\n"));
+
+ goto ErrorExit;
+ }
+
+ if (!((GETTOKENINFORMATION *)pProcAddr) (hToken, TokenOwner, (LPVOID)ptkOwner,
+ dwRetLength, &dwRetLength))
+ {
+ hr = HRESULT_FROM_GetLastError();
+
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::GSFP: Unable to get SID for process. "
+ "GetTokenInformation() failed: 0x%08x\n", hr));
+
+ goto ErrorExit;
+ }
+
+ *ppSID = ptkOwner->Owner;
+
+ErrorExit:
+ if (hProc != NULL)
+ CloseHandle(hProc);
+
+ if (hToken != NULL)
+ CloseHandle(hToken);
+
+ return hr;
+}
+
+/* static */
+DWORD IPCShared::GetAccessFlagsForObject(KernelObject whatObject, BOOL bFullControlACL)
+{
+ _ASSERTE(whatObject >= 0 && whatObject < TotalKernelObjects);
+
+ DWORD dwAccessFlags = 0;
+
+ if (!bFullControlACL)
+ {
+ if (whatObject == Section)
+ dwAccessFlags = (STANDARD_RIGHTS_ALL | SECTION_MAP_READ) & ~WRITE_DAC & ~WRITE_OWNER & ~DELETE;
+ else if (whatObject == Event)
+ dwAccessFlags = (EVENT_ALL_ACCESS) & ~WRITE_DAC & ~WRITE_OWNER & ~DELETE;
+ else if (whatObject == PrivateNamespace)
+ dwAccessFlags = FILE_MAP_READ;
+ }
+ else
+ {
+ _ASSERTE(whatObject != PrivateNamespace);
+ if (whatObject == Section)
+ dwAccessFlags = CLR_IPC_GENERIC_RIGHT;
+ else if (whatObject == Event)
+ dwAccessFlags = EVENT_ALL_ACCESS;
+ }
+
+ _ASSERTE(dwAccessFlags != 0);
+ return dwAccessFlags;
+}
+
+
+//-----------------------------------------------------------------------------
+// This function will initialize the Access Control List with three
+// Access Control Entries:
+// The first ACE entry grants all permissions to "Administrators".
+// The second ACE grants all permissions to the monitoring users (for perfcounters).
+// The third ACE grants all permissions to "Owner" of the target process.
+//-----------------------------------------------------------------------------
+BOOL IPCShared::InitializeGenericIPCAcl(DWORD pid, BOOL bRestrictiveACL, PACL *ppACL, KernelObject whatObject, EDescriptorType descType)
+{
+ WRAPPER_NO_CONTRACT;
+
+ struct PermissionStruct
+ {
+ PSID rgPSID;
+ DWORD rgAccessFlags;
+ } PermStruct[MaxNumberACEs];
+
+ SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
+ HRESULT hr = S_OK;
+ DWORD dwAclSize;
+ BOOL returnCode = false;
+ *ppACL = NULL;
+ DWORD i;
+ DWORD cActualACECount = 0;
+ char *pBufferToFreeByCaller = NULL;
+ int iSIDforAdmin = -1;
+ int iSIDforUsers = -1;
+ int iSIDforLoggingUsers = -1;
+#if !defined (DACCESS_COMPILE) && !defined(FEATURE_CORECLR)
+ NewArrayHolder<BYTE> pbTokenMem;
+ PTOKEN_APPCONTAINER_INFORMATION pAppContainerTokenInfo = NULL;
+#endif
+
+ PermStruct[0].rgPSID = NULL;
+
+ HINSTANCE hDll = WszGetModuleHandle(L"advapi32");
+
+ if (hDll == NULL)
+ {
+ LOG((LF_CORDB, LL_INFO10, "IPCWI::IGIPCA: Unable to generate ACL for IPC. LoadLibrary (advapi32) failed.\n"));
+ return false;
+ }
+ _ASSERTE(hDll != NULL);
+
+ // Get the pointer to the requested function
+ FARPROC pProcAddr = GetProcAddress(hDll, "AllocateAndInitializeSid");
+
+ // If the proc address was not found, return error
+ if (pProcAddr == NULL)
+ {
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::IGIPCA: Unable to generate ACL for IPC. "
+ "GetProcAddr (AllocateAndInitializeSid) failed.\n"));
+ goto ErrorExit;
+ }
+
+ typedef BOOL ALLOCATEANDINITIALIZESID(PSID_IDENTIFIER_AUTHORITY,
+ BYTE, DWORD, DWORD, DWORD, DWORD,
+ DWORD, DWORD, DWORD, DWORD, PSID *);
+
+
+ BOOL bGrantAllAccess = ((descType == eDescriptor_Private) ? TRUE : FALSE);
+ // Create a SID for the BUILTIN\Administrators group.
+ // SECURITY_BUILTIN_DOMAIN_RID + DOMAIN_ALIAS_RID_ADMINS = all Administrators. This translates to (A;;GA;;;BA).
+ if (!((ALLOCATEANDINITIALIZESID *) pProcAddr)(&SIDAuthNT,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &PermStruct[0].rgPSID))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ _ASSERTE(SUCCEEDED(hr));
+
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::IGIPCA: failed to allocate AdminSid: 0x%08x\n", hr));
+
+ goto ErrorExit;
+ }
+ // GENERIC_ALL access for Administrators
+ PermStruct[cActualACECount].rgAccessFlags = GetAccessFlagsForObject(whatObject, bGrantAllAccess);
+
+ iSIDforAdmin = cActualACECount;
+ cActualACECount++;
+
+ // Next, we get the SID for the owner of the current process.
+ hr = GetSidForProcess(hDll, GetCurrentProcessId(), &(PermStruct[cActualACECount].rgPSID), &pBufferToFreeByCaller);
+ DWORD accessFlags = 0;
+ if (whatObject == Section) {
+ //special case, grant SECTION_MAP_WRITE for current owner just to support inProc SxS.
+ accessFlags = GetAccessFlagsForObject(whatObject, bGrantAllAccess) | SECTION_MAP_WRITE;
+ }
+ else {
+ accessFlags = GetAccessFlagsForObject(whatObject, bGrantAllAccess);
+ }
+ PermStruct[cActualACECount].rgAccessFlags = accessFlags;
+
+ // Don't fail out if we cannot get the SID for the owner of the current process. In this case, the
+ // share memory block will be created with only Admin (and optionall "Users") permissions.
+ // Currently we discovered the anonymous user doesn't have privilege to call OpenProcess. Without OpenProcess,
+ // we cannot get the SID...
+ //
+ if (SUCCEEDED(hr))
+ {
+ cActualACECount++;
+ }
+#if _DEBUG
+ else
+ LOG((LF_CORDB, LL_INFO100, "IPCWI::IGIPCA: GetSidForProcess() failed: 0x%08x\n", hr));
+#endif // _DEBUG
+
+
+ if (descType == eDescriptor_Public)
+ {
+ DWORD dwRet = ((ALLOCATEANDINITIALIZESID *) pProcAddr)(&SIDAuthNT,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_MONITORING_USERS,
+ 0, 0, 0, 0, 0, 0,
+ &PermStruct[cActualACECount].rgPSID);
+
+ if (dwRet)
+ {
+ // "Users" shouldn't be able to write to block, delete object, change DACLs, or change ownership
+ PermStruct[cActualACECount].rgAccessFlags = GetAccessFlagsForObject(whatObject, FALSE);
+
+ iSIDforUsers = cActualACECount;
+ cActualACECount++;
+ }
+ else
+ {
+ hr = HRESULT_FROM_GetLastError();
+ _ASSERTE(SUCCEEDED(hr));
+
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::IGIPCA: failed to allocate Users Sid: 0x%08x\n", hr));
+
+ // non-fatal error, so don't goto errorexit
+ }
+
+ dwRet = ((ALLOCATEANDINITIALIZESID *) pProcAddr)(&SIDAuthNT,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_LOGGING_USERS,
+ 0, 0, 0, 0, 0, 0,
+ &PermStruct[cActualACECount].rgPSID);
+ if (dwRet)
+ {
+ PermStruct[cActualACECount].rgAccessFlags = GetAccessFlagsForObject(whatObject, FALSE);
+
+ iSIDforLoggingUsers = cActualACECount;
+ cActualACECount++;
+ }
+ else
+ {
+ hr = HRESULT_FROM_GetLastError();
+ _ASSERTE(SUCCEEDED(hr));
+
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::IGIPCA: failed to allocate Domain Logging Users Sid: 0x%08x\n", hr));
+
+ // non-fatal error, so don't goto errorexit
+ }
+
+#if !defined(DACCESS_COMPILE) && !defined(FEATURE_CORECLR)
+ // when running on win8 if the process is an appcontainer then add the appcontainer SID to the ACL
+ // going down this code path means we're creating the descriptor for our current PID.
+ _ASSERTE(pid == GetCurrentProcessId());
+ hr = AppX::GetAppContainerTokenInfoForProcess(pid, pbTokenMem);
+
+ if (FAILED(hr))
+ {
+ // failed to open the target's process, continue on
+ // assuming that the process isn't in an AppContainer.
+ _ASSERTE(pbTokenMem == NULL);
+ }
+ else
+ {
+ if (hr == S_FALSE)
+ {
+ // not an appcontainer
+ _ASSERTE(pbTokenMem == NULL);
+ }
+ else
+ {
+ // process is an appcontainer so add the SID
+ pAppContainerTokenInfo =
+ reinterpret_cast<PTOKEN_APPCONTAINER_INFORMATION>(pbTokenMem.GetValue());
+ _ASSERTE(pAppContainerTokenInfo);
+ _ASSERTE(pAppContainerTokenInfo->TokenAppContainer);
+
+ PermStruct[cActualACECount].rgPSID = pAppContainerTokenInfo->TokenAppContainer;
+ PermStruct[cActualACECount].rgAccessFlags = GetAccessFlagsForObject(whatObject, FALSE);
+ ++cActualACECount;
+ }
+ }
+#endif // !defined(DACCESS_COMPILE) && !defined(FEATURE_CORECLR)
+ }
+
+ _ASSERTE(cActualACECount <= MaxNumberACEs);
+
+ // Now, create an Initialize an ACL and add the ACE entries to it. NOTE: We're not using "SetEntriesInAcl" because
+ // it loads a bunch of other dlls which can be avoided by using this roundabout way!!
+
+ // Get the pointer to the requested function
+ pProcAddr = GetProcAddress(hDll, "InitializeAcl");
+
+ // If the proc address was not found, return error
+ if (pProcAddr == NULL)
+ {
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::IGIPCA: Unable to generate ACL for IPC. "
+ "GetProcAddr (InitializeAcl) failed.\n"));
+ goto ErrorExit;
+ }
+
+ // Also calculate the memory required for ACE entries in the ACL using the
+ // following method:
+ // "sizeof (ACCESS_ALLOWED_ACE) - sizeof (ACCESS_ALLOWED_ACE.SidStart) + GetLengthSid (pAceSid);"
+
+ dwAclSize = sizeof (ACL) + (sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD)) * cActualACECount;
+
+ for (i = 0; i < cActualACECount; i++)
+ {
+ dwAclSize += GetLengthSid(PermStruct[i].rgPSID);
+ }
+
+ // now allocate memory
+ if ((*ppACL = (PACL) new (nothrow) char[dwAclSize]) == NULL)
+ {
+ LOG((LF_CORDB, LL_INFO10, "IPCWI::IGIPCA: OutOfMemory... 'new Acl' failed.\n"));
+
+ goto ErrorExit;
+ }
+
+ typedef BOOL INITIALIZEACL(PACL, DWORD, DWORD);
+
+ if (!((INITIALIZEACL *)pProcAddr)(*ppACL, dwAclSize, ACL_REVISION))
+ {
+ hr = HRESULT_FROM_GetLastError();
+
+ LOG((LF_CORDB, LL_INFO100,
+ "IPCWI::IGIPCA: InitializeACL() failed: 0x%08x\n", hr));
+
+ goto ErrorExit;
+ }
+
+ // Get the pointer to the requested function
+ pProcAddr = GetProcAddress(hDll, "AddAccessAllowedAce");
+
+ // If the proc address was not found, return error
+ if (pProcAddr == NULL)
+ {
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::IGIPCA: Unable to generate ACL for IPC. "
+ "GetProcAddr (AddAccessAllowedAce) failed.\n"));
+ goto ErrorExit;
+ }
+
+ typedef BOOL ADDACCESSALLOWEDACE(PACL, DWORD, DWORD, PSID);
+
+ for (i=0; i < cActualACECount; i++)
+ {
+ if (!((ADDACCESSALLOWEDACE *)pProcAddr)(*ppACL,
+ ACL_REVISION,
+ PermStruct[i].rgAccessFlags,
+ PermStruct[i].rgPSID))
+
+ {
+ hr = HRESULT_FROM_GetLastError();
+
+ LOG((LF_CORDB, LL_INFO100,
+ "IPCWI::IGIPCA: AddAccessAllowedAce() failed: 0x%08x\n", hr));
+ goto ErrorExit;
+ }
+ }
+
+ returnCode = true;
+ goto NormalExit;
+
+
+ErrorExit:
+ returnCode = FALSE;
+
+ if (*ppACL)
+ {
+ delete [] (*ppACL);
+ *ppACL = NULL;
+ }
+
+NormalExit:
+
+ if (pBufferToFreeByCaller != NULL)
+ delete [] pBufferToFreeByCaller;
+
+ // Get the pointer to the requested function
+ pProcAddr = GetProcAddress(hDll, "FreeSid");
+
+ // If the proc address was not found, return error
+ if (pProcAddr == NULL)
+ {
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::IGIPCA: Unable to generate ACL for IPC. "
+ "GetProcAddr (FreeSid) failed.\n"));
+ return false;
+ }
+
+ typedef BOOL FREESID(PSID);
+
+ // Free the SID created earlier. Function does not return a value.
+ if( iSIDforAdmin != -1 )
+ ((FREESID *) pProcAddr)(PermStruct[iSIDforAdmin].rgPSID);
+
+ // free the SID for "Users"
+ if (iSIDforUsers != -1)
+ ((FREESID *) pProcAddr)(PermStruct[iSIDforUsers].rgPSID);
+
+ // free the SID for "Performance Logging Users"
+ if (iSIDforLoggingUsers != -1)
+ ((FREESID *) pProcAddr)(PermStruct[iSIDforLoggingUsers].rgPSID);
+
+ return returnCode;
+}
+
+#endif // FEATURE_IPCMAN
diff --git a/src/ipcman/ipcwriterimpl.cpp b/src/ipcman/ipcwriterimpl.cpp
new file mode 100644
index 0000000000..bcb7e76224
--- /dev/null
+++ b/src/ipcman/ipcwriterimpl.cpp
@@ -0,0 +1,918 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+//*****************************************************************************
+// File: IPCWriterImpl.cpp
+//
+// Implementation for COM+ memory mapped file writing
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+
+#include "ipcmanagerinterface.h"
+#include "ipcheader.h"
+#include "ipcshared.h"
+#include "ipcmanagerimpl.h"
+
+// Declared in threads.h, but including that file seems to cause problems
+DWORD GetRuntimeId();
+
+#include <sddl.h>
+
+#if defined(TIA64)
+#define IA64MemoryBarrier() MemoryBarrier()
+#else
+#define IA64MemoryBarrier()
+#endif
+
+#if defined(FEATURE_IPCMAN)
+
+const USHORT BuildYear = VER_ASSEMBLYMAJORVERSION;
+const USHORT BuildNumber = VER_ASSEMBLYBUILD;
+
+// Import from mscorwks.obj
+HINSTANCE GetModuleInst();
+
+#if defined(_DEBUG)
+static void DumpSD(PSECURITY_DESCRIPTOR sd)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HINSTANCE hDll = WszGetModuleHandle(L"advapi32");
+
+ // Get the pointer to the requested function
+ FARPROC pProcAddr = GetProcAddress(hDll, "ConvertSecurityDescriptorToStringSecurityDescriptorW");
+
+ // If the proc address was not found, return error
+ if (pProcAddr == NULL)
+ {
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::DumpSD: GetProcAddr (ConvertSecurityDescriptorToStringSecurityDescriptorW) failed.\n"));
+ goto ErrorExit;
+ }
+
+ typedef BOOL WINAPI SDTOSTR(PSECURITY_DESCRIPTOR, DWORD, SECURITY_INFORMATION, LPSTR *, PULONG);
+
+ LPSTR str = NULL;
+
+ if (!((SDTOSTR*)pProcAddr)(sd, SDDL_REVISION_1, 0xF, &str, NULL))
+ {
+ LOG((LF_CORDB, LL_INFO10,
+ "IPCWI::DumpSD: ConvertSecurityDescriptorToStringSecurityDescriptorW failed %d\n",
+ GetLastError()));
+ goto ErrorExit;
+ }
+
+ fprintf(stderr, "SD for IPC: %S\n", str);
+ LOG((LF_CORDB, LL_INFO10, "IPCWI::DumpSD: SD for IPC: %s\n", str));
+
+ (LocalFree)(str);
+
+ErrorExit:
+ return;
+}
+#endif // _DEBUG
+
+//-----------------------------------------------------------------------------
+// Generic init
+//-----------------------------------------------------------------------------
+HRESULT IPCWriterInterface::Init()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Nothing to do anymore in here...
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Generic publish
+//-----------------------------------------------------------------------------
+void IPCWriterInterface::Publish()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ IA64MemoryBarrier();
+
+ // Set the appropriate bit to mark the LegacyPrivate IPC block as initialized
+ if (m_ptrLegacyPrivateBlock != NULL)
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_Flags |= IPC_FLAG_INITIALIZED;
+
+ // Set the appropriate bit to mark the SxS Public IPC block as initialized
+ if (m_pBlock != NULL)
+ m_pBlock->m_Header.m_Flags |= IPC_FLAG_INITIALIZED;
+}
+
+
+#ifndef DACCESS_COMPILE
+
+//-----------------------------------------------------------------------------
+// Generic terminate
+//-----------------------------------------------------------------------------
+void IPCWriterInterface::Terminate()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ LOG((LF_CORDB, LL_INFO10, "IPCWI::Terminate: Writer: closing 0x%08x and 0x%08x\n", m_handleLegacyPrivateBlock, m_handleBlockTable));
+
+ if (m_ptrLegacyPrivateBlock == m_pIPCBackupBlockLegacyPrivate)
+ {
+ // This is the case that we allocate a block of memory and pretending it is the map file view,
+ // so we don't need to unmap the file view on m_ptrLegacyPrivateBlock
+ m_ptrLegacyPrivateBlock = NULL;
+ }
+
+ IPCShared::CloseMemoryMappedFile(m_handleLegacyPrivateBlock, (void*&) m_ptrLegacyPrivateBlock);
+
+ if (m_pBlockTable == m_pBackupBlock)
+ {
+ // This is the case that we allocate a block of memory and pretending it is the map file view,
+ // so we don't need to unmap the file view on m_pBlock
+ m_pBlockTable = NULL;
+ m_pBlock = NULL;
+ }
+ else
+ {
+ BOOL fFreedChunk = TryFreeBlock();
+
+ // Release our handle to the shared memory region
+ IPCShared::CloseMemoryMappedFile(m_handleBlockTable, (void*&) m_pBlockTable);
+
+ m_pBlockTable = NULL;
+ m_pBlock = NULL;
+ }
+
+ // If we have a cached SA for this process, go ahead and clean it up.
+ if (m_cachedPrivateDescriptor != NULL)
+ {
+ // DestroySecurityAttributes won't destroy our cached SA, so save the ptr to the SA and clear the cached value
+ // before calling it.
+ SECURITY_ATTRIBUTES *pSA = m_cachedPrivateDescriptor;
+ m_cachedPrivateDescriptor = NULL;
+ DestroySecurityAttributes(pSA);
+ }
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Have ctor zero everything out
+//-----------------------------------------------------------------------------
+IPCWriterImpl::IPCWriterImpl()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Cache pointers to sections
+ m_pPerf = NULL;
+ m_pAppDomain = NULL;
+ m_pInstancePath = NULL;
+
+ // Mem-Mapped file for LegacyPrivate Block
+ m_handleLegacyPrivateBlock = NULL;
+ m_ptrLegacyPrivateBlock = NULL;
+
+ // Mem-Mapped file for SxS Public Block
+ m_handleBlockTable = NULL;
+ m_pBlock = NULL;
+ m_pBlockTable = NULL;
+ m_handleBoundaryDesc = NULL;
+ m_handlePrivateNamespace = NULL;
+ m_pSID = NULL;
+
+ // Security
+ m_cachedPrivateDescriptor = NULL;
+
+ m_pIPCBackupBlockLegacyPrivate = NULL;
+ m_pBackupBlock = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Assert that everything was already shutdown by a call to terminate.
+// Shouldn't be anything left to do in the dtor
+//-----------------------------------------------------------------------------
+IPCWriterImpl::~IPCWriterImpl()
+{
+#ifndef DACCESS_COMPILE
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(!IsLegacyPrivateBlockOpen());
+ if (m_pIPCBackupBlockLegacyPrivate)
+ {
+ delete [] ((BYTE *)m_pIPCBackupBlockLegacyPrivate);
+ }
+
+ _ASSERTE(!IsBlockTableOpen());
+ //Note: m_handlePrivateNamespace is not NULL. This is because we do not Close the handle to PNS and instead
+ //let the OS close it for us. This is because if we close the PNS, then the reader(perfmon) cannot open this PNS.
+ _ASSERTE(!m_handleBoundaryDesc);
+ _ASSERTE(!m_pSID);
+ if (m_pBackupBlock)
+ {
+ delete [] ((BYTE *)m_pBackupBlock);
+ }
+#endif // DACCESS_COMPILE
+}
+
+//-----------------------------------------------------------------------------
+// Accessors to get each clients' blocks
+//-----------------------------------------------------------------------------
+struct PerfCounterIPCControlBlock * IPCWriterInterface::GetPerfBlock()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_pPerf;
+}
+
+struct AppDomainEnumerationIPCBlock * IPCWriterInterface::GetAppDomainBlock()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_pAppDomain;
+}
+
+//-----------------------------------------------------------------------------
+// Helper to destroy the security attributes for the shared memory for a given
+// process.
+//-----------------------------------------------------------------------------
+void IPCWriterInterface::DestroySecurityAttributes(SECURITY_ATTRIBUTES *pSA)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Don't destroy our cached SA!
+ if (pSA == m_cachedPrivateDescriptor)
+ return;
+
+ IPCShared::DestroySecurityAttributes(pSA);
+}
+
+/************************************ IPC BLOCK TABLE ************************************/
+
+#ifndef DACCESS_COMPILE
+
+BOOL IPCWriterInterface::TryAllocBlock(DWORD numRetries)
+{
+ _ASSERTE(m_pBlock == NULL);
+
+ for (DWORD i = 0; i < IPC_NUM_BLOCKS_IN_TABLE; ++i)
+ {
+ m_pBlock = m_pBlockTable->GetBlock(i);
+
+ IPCHeaderLockHolder lockHolder(m_pBlock->m_Header);
+ if (lockHolder.TryGetLock(numRetries) == FALSE)
+ continue;
+
+ DWORD runtimeId = m_pBlock->m_Header.m_RuntimeId;
+ if (runtimeId == 0)
+ {
+ // Set the runtime ID
+ m_pBlock->m_Header.m_RuntimeId = GetRuntimeId();
+
+ // Set up the IPC header while we
+ // still hold the lock
+ CreateIPCHeader();
+
+ return TRUE;
+ }
+ }
+
+ m_pBlock = NULL;
+ return FALSE;
+}
+
+BOOL IPCWriterInterface::TryFreeBlock()
+{
+ _ASSERTE(m_pBlock != NULL);
+
+ DWORD retriesLeft = 100;
+ DWORD dwSwitchCount = 0;
+
+ IPCHeaderLockHolder lockHolder(m_pBlock->m_Header);
+
+ // Try getting the lock, and retry up to 100 times.
+ // If lock cannot be acquired, give up and return FALSE
+
+ if (lockHolder.TryGetLock(100) == FALSE)
+ return FALSE;
+
+ // If the lock was acquired successfully, mark this
+ // block as free, release the lock, and return TRUE
+
+ m_pBlock->m_Header.m_RuntimeId = 0;
+ m_pBlock = NULL;
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// Open our SxS Public IPC block on the given pid.
+//-----------------------------------------------------------------------------
+HRESULT IPCWriterInterface::CreateSxSPublicBlockOnPid(DWORD pid)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Note: if our SxS Public block is open, we shouldn't be creating it again.
+ _ASSERTE(!IsBlockTableOpen());
+
+ if (IsBlockTableOpen())
+ {
+ // if we goto errExit, it will close the file. We don't want that.
+ return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
+ }
+
+
+ // Note: if PID != GetCurrentProcessId(), we're expected to be opening
+ // someone else's IPCBlock, so if it doesn't exist, we should assert.
+ HRESULT hr = S_OK;
+
+ SECURITY_ATTRIBUTES *pSA = NULL;
+
+ EX_TRY
+ {
+ // Grab the SA
+ SString szMemFileName;
+ hr = GetSxSPublicSecurityAttributes(pid, &pSA);
+ if (FAILED(hr))
+ goto failedToGetBlock;
+
+ hr = IPCShared::GenerateBlockTableName(pid, szMemFileName, m_handleBoundaryDesc, m_handlePrivateNamespace, &m_pSID, TRUE);
+ if (FAILED(hr))
+ goto failedToGetBlock;
+
+ BOOL openedExistingBlock = FALSE;
+
+#if defined(FEATURE_PERFMON)
+ DWORD dwFileMapErr = 0;
+
+ // Retry several times to mitigate a potential race with another
+ // runtime shutting down
+ for (int tries = 0; tries < 3; ++tries)
+ {
+ // Connect the handle
+ m_handleBlockTable = WszCreateFileMapping(INVALID_HANDLE_VALUE,
+ pSA,
+ PAGE_READWRITE | SEC_COMMIT,
+ 0,
+ sizeof(IPCControlBlockTable),
+ szMemFileName);
+
+ dwFileMapErr = GetLastError();
+
+ LOG((LF_CORDB, LL_INFO10, "IPCWI::CPBOP: CreateFileMapping of %S, handle = 0x%08x, pid = 0x%8.8x GetLastError=%d\n",
+ szMemFileName.GetUnicode(), m_handleBlockTable, pid, GetLastError()));
+
+ // If GetLastError() returns ERROR_ALREADY_EXISTS, then we need to do some extra checks
+ if (m_handleBlockTable != NULL && dwFileMapErr == ERROR_ALREADY_EXISTS)
+ {
+ openedExistingBlock = TRUE;
+ }
+
+ // If CreateFileMapping() failed with ERROR_ACCESS_DENIED, try calling OpenFileMapping()
+ else if (m_handleBlockTable == NULL && dwFileMapErr == ERROR_ACCESS_DENIED)
+ {
+ // If we could not create the IPCBlockTable due to ERROR_ACCESS_DENIED,
+ // then the IPCBlockTable already exists. Next we will try opening the
+ // IPCBlockTable using OpenFileMapping().
+ m_handleBlockTable = WszOpenFileMapping(FILE_MAP_WRITE,
+ FALSE,
+ szMemFileName);
+ dwFileMapErr = GetLastError();
+
+ // There is a chance for a race where another runtime might close the
+ // IPCBlockTable before we can open it. Thus, if we get ERROR_FILE_NOT_FOUND
+ // we retry calling CreateFileMapping().
+ if (m_handleBlockTable == NULL && dwFileMapErr == ERROR_FILE_NOT_FOUND)
+ {
+ Sleep(1);
+ continue;
+ }
+
+ if (m_handleBlockTable != NULL)
+ {
+ openedExistingBlock = TRUE;
+ }
+ }
+
+ break;
+ }
+#else // !FEATURE_PERFMON
+ m_handleBlockTable = NULL;
+#endif // FEATURE_PERFMON
+
+ // If unsuccessful, don't ever bail out.
+ if (m_handleBlockTable != NULL)
+ {
+ // Get the pointer - must get it even if ERROR_ALREADY_EXISTS,
+ // since the IPC block is allowed to already exist if there is
+ // another runtime in the process.
+ m_pBlockTable = (IPCControlBlockTable *) MapViewOfFile(m_handleBlockTable,
+ FILE_MAP_ALL_ACCESS,
+ 0, 0, 0);
+ // If the IPC Block already exists, then we need to check its size and other
+ // properties. This is needed because a low privledged user may have spoofed
+ // a block with the same name before the CLR started.
+
+ if (m_pBlockTable != NULL && openedExistingBlock)
+ {
+ // If the BlockTable does not fit in this memory region,
+ // then it is not safe to use
+ //The following is a security check to ensure that incase the BlockTable was opened by a
+ //malicious user we simply commit the block table to ensure that its of the required size4
+ PTR_IPCControlBlockTable pBlockTable = (PTR_IPCControlBlockTable) ClrVirtualAlloc(m_pBlockTable,sizeof(IPCControlBlockTable),MEM_COMMIT, PAGE_READWRITE);
+ if(pBlockTable == NULL || pBlockTable != m_pBlockTable)
+ {
+ goto failedToGetBlock;
+ }
+ }
+ }
+
+ BOOL fGotBlock = FALSE;
+
+ // If opening the shared memory block failed, then we need to go down an error path
+ if (m_pBlockTable == NULL)
+ goto failedToGetBlock;
+
+ // Try allocating a chunk by iterating over the chunks;
+ // if a chunk is locked, don't spin waiting on the lock
+ fGotBlock = TryAllocBlock(0);
+
+ // If we failed to allocate a chunk, try iterating over the chunks again,
+ // but this time if a chunk is locked, spin for a while to wait on the lock
+ if (!fGotBlock)
+ fGotBlock = TryAllocBlock(100);
+
+ // If we succeeded in allocating a chunk, we're done
+ if (fGotBlock)
+ {
+ _ASSERTE(m_pBlock != NULL);
+ goto done;
+ }
+
+ // If we failed to allocate a chunk, so we need to do some
+ // cleanup and set up a "backup" block. When we go into this
+ // code path, our perf counters won't work. But our code will
+ // continue to run.
+
+
+failedToGetBlock:
+
+ // Release our handle to the shared memory region
+ if(m_pBlockTable != NULL)
+ IPCShared::CloseMemoryMappedFile(m_handleBlockTable, (void*&) m_pBlockTable);
+
+ // Set all out SxSPublic pointers to NULL
+ m_pBlockTable = NULL;
+ m_pBlock = NULL;
+
+ // Allocate a "backup" block
+ DWORD arraySize = sizeof(IPCControlBlockTable);
+ m_pBackupBlock = (IPCControlBlockTable *) new BYTE[arraySize];
+
+ // Assert that allocation succeeded
+ _ASSERTE(m_pBackupBlock != NULL);
+
+ // Zero out the backup block
+ ZeroMemory(m_pBackupBlock, arraySize);
+ m_pBlockTable = m_pBackupBlock;
+
+ // Since we are allocating a chunk from the backup block, there
+ // should be no contention, and we should always succeed
+ fGotBlock = TryAllocBlock(0);
+ _ASSERTE(fGotBlock);
+ _ASSERTE(m_pBlock != NULL);
+
+done:
+
+ ;
+ }
+ EX_CATCH
+ {
+ Exception *e = GET_EXCEPTION();
+ hr = e->GetHR();
+ if (hr == S_OK)
+ {
+ hr = E_FAIL;
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (!SUCCEEDED(hr))
+ {
+ IPCShared::CloseMemoryMappedFile(m_handleBlockTable, (void*&)m_pBlock);
+ }
+ DestroySecurityAttributes(pSA);
+ if(!SUCCEEDED(IPCShared::FreeHandles(m_handleBoundaryDesc,m_pSID)))
+ {
+ hr = E_FAIL;
+ }
+
+ return hr;
+}
+
+//-----------------------------------------------------------------------------
+// Return the security attributes for the shared memory for a given process.
+//-----------------------------------------------------------------------------
+HRESULT IPCWriterInterface::GetSxSPublicSecurityAttributes(DWORD pid, SECURITY_ATTRIBUTES **ppSA)
+{
+ WRAPPER_NO_CONTRACT;
+ return CreateWinNTDescriptor(pid, ppSA, eDescriptor_Public);
+}
+
+
+//-----------------------------------------------------------------------------
+// Setup a security descriptor for the named kernel objects if we're on NT.
+//-----------------------------------------------------------------------------
+HRESULT IPCWriterImpl::CreateWinNTDescriptor(DWORD pid, SECURITY_ATTRIBUTES **ppSA, EDescriptorType descType)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = NO_ERROR;
+ *ppSA = NULL;
+
+ // If the caller wants the private descriptor for the current process
+ // and a cached copy exists, return the cached copy
+ if (descType == eDescriptor_Private && m_cachedPrivateDescriptor != NULL && pid == GetCurrentProcessId())
+ {
+ *ppSA = m_cachedPrivateDescriptor;
+ return hr;
+ }
+
+ hr = IPCShared::CreateWinNTDescriptor(pid, (descType == eDescriptor_Private ? TRUE : FALSE), ppSA, Section, descType);
+
+ // Cache the private descriptor for the current process.
+ // We do not cache the public descriptor because it isn't
+ // used frequently.
+ if (descType == eDescriptor_Private && pid == GetCurrentProcessId())
+ m_cachedPrivateDescriptor = *ppSA;
+
+ return hr;
+}
+
+//-----------------------------------------------------------------------------
+// Helper: Fill out a directory entry.
+//-----------------------------------------------------------------------------
+void IPCWriterImpl::WriteEntryHelper(EIPCClient eClient,
+ DWORD offs,
+ DWORD size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_pBlock->m_Header.m_table[eClient].m_Offset = offs;
+ m_pBlock->m_Header.m_table[eClient].m_Size = size;
+}
+
+//-----------------------------------------------------------------------------
+// Initialize the header for our SxS public IPC block
+//-----------------------------------------------------------------------------
+void IPCWriterImpl::CreateIPCHeader()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if defined(_TARGET_X86_)
+ m_pBlock->m_Header.m_Flags = IPC_FLAG_USES_FLAGS | IPC_FLAG_X86;
+#else
+ m_pBlock->m_Header.m_Flags = IPC_FLAG_USES_FLAGS;
+#endif
+
+ // Stamp the IPC block with the version
+ m_pBlock->m_Header.m_Version = VER_IPC_BLOCK;
+ m_pBlock->m_Header.m_blockSize = SXSPUBLIC_IPC_SIZE_NO_PADDING;
+
+ m_pBlock->m_Header.m_BuildYear = BuildYear;
+ m_pBlock->m_Header.m_BuildNumber = BuildNumber;
+
+ m_pBlock->m_Header.m_numEntries = eIPC_MAX;
+
+ //
+ // Fill out directory (offset and size of each block).
+ // First fill in the used entries.
+ //
+
+ WriteEntryHelper(eIPC_PerfCounters,
+ offsetof(IPCControlBlock, m_perf),
+ sizeof(PerfCounterIPCControlBlock));
+
+ // Cache our client pointers
+ m_pPerf = &(m_pBlock->m_perf);
+}
+
+#endif
+
+/*********************************** LEGACY FUNCTIONS ***********************************
+ *
+ * We plan to remove the LegacyPrivate block in the near future. However, the debugger
+ * still currently relies on the LegacyPrivate block for AppDomain enumeration, and we
+ * cannot rip out the LegacyPrivate block until the debugger is changed accordingly.
+ *
+ ****************************************************************************************/
+
+#ifndef DACCESS_COMPILE
+
+
+//-----------------------------------------------------------------------------
+// Open our LegacyPrivate IPC block on the given pid.
+//-----------------------------------------------------------------------------
+HRESULT IPCWriterInterface::CreateLegacyPrivateBlockTempV4OnPid(DWORD pid, BOOL inService, HINSTANCE *phInstIPCBlockOwner)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Init the IPC block owner HINSTANCE to 0.
+ *phInstIPCBlockOwner = 0;
+
+ // Note: if our LegacyPrivate block is open, we shouldn't be creating it again.
+ _ASSERTE(!IsLegacyPrivateBlockOpen());
+
+ if (IsLegacyPrivateBlockOpen())
+ {
+ // if we goto errExit, it will close the file. We don't want that.
+ return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
+ }
+
+ // Note: if PID != GetCurrentProcessId(), we're expected to be opening
+ // someone else's IPCBlock, so if it doesn't exist, we should assert.
+ HRESULT hr = S_OK;
+
+ SECURITY_ATTRIBUTES *pSA = NULL;
+
+ EX_TRY
+ {
+ // Grab the SA
+ hr = CreateWinNTDescriptor(pid, &pSA, eDescriptor_Private);
+ if (FAILED(hr))
+ ThrowHR(hr);
+
+ SString szMemFileName;
+
+ IPCShared::GenerateNameLegacyTempV4(pid, szMemFileName);
+
+ // Connect the handle
+ m_handleLegacyPrivateBlock = WszCreateFileMapping(INVALID_HANDLE_VALUE,
+ pSA,
+ PAGE_READWRITE,
+ 0,
+ sizeof(LegacyPrivateIPCControlBlock),
+ szMemFileName);
+
+ DWORD dwFileMapErr = GetLastError();
+
+ LOG((LF_CORDB, LL_INFO10, "IPCWI::CPBOP: CreateFileMapping of %S, handle = 0x%08x, pid = 0x%8.8x GetLastError=%d\n",
+ szMemFileName.GetUnicode(), m_handleLegacyPrivateBlock, pid, GetLastError()));
+
+ // If unsuccessful, don't ever bail out.
+ if (m_handleLegacyPrivateBlock != NULL && dwFileMapErr != ERROR_ALREADY_EXISTS)
+ {
+ m_ptrLegacyPrivateBlock = (LegacyPrivateIPCControlBlock *) MapViewOfFile(m_handleLegacyPrivateBlock,
+ FILE_MAP_ALL_ACCESS,
+ 0, 0, 0);
+ }
+
+ if (m_ptrLegacyPrivateBlock == NULL)
+ {
+ // when we go into this code path, our debugging and perf counter won't work. But
+ // our managed code will continue to run.
+ SIZE_T cbLen = sizeof(LegacyPrivateIPCControlBlock);
+ m_pIPCBackupBlockLegacyPrivate = (LegacyPrivateIPCControlBlock *) new BYTE[cbLen];
+ _ASSERTE(m_pIPCBackupBlockLegacyPrivate != NULL); // throws on OOM.
+
+ ZeroMemory(m_pIPCBackupBlockLegacyPrivate, cbLen); // simulate that OS zeros out memory
+ m_ptrLegacyPrivateBlock = m_pIPCBackupBlockLegacyPrivate;
+ }
+
+ // Hook up each sections' pointers
+ CreateLegacyPrivateIPCHeader();
+ }
+ EX_CATCH
+ {
+ Exception *e = GET_EXCEPTION();
+ hr = e->GetHR();
+ if (hr == S_OK)
+ {
+ hr = E_FAIL;
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (!SUCCEEDED(hr))
+ {
+ IPCShared::CloseMemoryMappedFile(m_handleLegacyPrivateBlock, (void*&)m_ptrLegacyPrivateBlock);
+ }
+ DestroySecurityAttributes(pSA);
+
+ return hr;
+}
+
+//-----------------------------------------------------------------------------
+// ReDacl our LegacyPrivate block after it has been created.
+//-----------------------------------------------------------------------------
+HRESULT IPCWriterInterface::ReDaclLegacyPrivateBlock(PSECURITY_DESCRIPTOR pSecurityDescriptor)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (!IsLegacyPrivateBlockOpen())
+ {
+ // nothing to reDACL.
+ return S_OK;
+ }
+
+ // note that this call will succeed only if we are the owner of this LegacyPrivate block.
+ // That is this call will fail if you call from debugger RS. If this is needed in the
+ // future, you can add WRITE_DAC access when we open LegacyPrivate block on the debugger RS.
+ //
+ if (SetKernelObjectSecurity(m_handleLegacyPrivateBlock, DACL_SECURITY_INFORMATION, pSecurityDescriptor) == 0)
+ {
+ // failed!
+ return HRESULT_FROM_GetLastError();
+ }
+
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Helper: Fill out a directory entry.
+//-----------------------------------------------------------------------------
+void IPCWriterImpl::WriteEntryHelper(ELegacyPrivateIPCClient eClient,
+ DWORD offs,
+ DWORD size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (offs != EMPTY_ENTRY_OFFSET)
+ {
+ // The incoming offset is the actual data structure offset
+ // but the directory is relative to the end of the full header
+ // (on v1.2) so subtract that out.
+
+ DWORD offsetBase = (DWORD)Internal_GetOffsetBaseLegacyPrivate(*m_ptrLegacyPrivateBlock);
+ _ASSERTE(offs >= offsetBase);
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_table[eClient].m_Offset = (offs - offsetBase);
+ }
+ else
+ {
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_table[eClient].m_Offset = offs;
+ }
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_table[eClient].m_Size = size;
+}
+
+//-----------------------------------------------------------------------------
+// Initialize the header for our LegacyPrivate IPC block
+//-----------------------------------------------------------------------------
+void IPCWriterImpl::CreateLegacyPrivateIPCHeader()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Set the flags
+
+#if defined(_TARGET_X86_)
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_Flags = IPC_FLAG_USES_FLAGS | IPC_FLAG_X86;
+#else
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_Flags = IPC_FLAG_USES_FLAGS;
+#endif
+
+ // Stamp the IPC block with the version
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_Version = VER_LEGACYPRIVATE_IPC_BLOCK;
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_blockSize = sizeof(LegacyPrivateIPCControlBlock);
+
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_hInstance = GetModuleInst();
+
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_BuildYear = BuildYear;
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_BuildNumber = BuildNumber;
+
+ m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_numEntries = eLegacyPrivateIPC_MAX;
+
+ //
+ // Fill out directory (offset and size of each block).
+ // First fill in the used entries.
+ //
+
+ // Even though this first entry is obsolete, it needs to remain
+ // here for binary compatibility and can't be marked empty/obsolete
+ // as long as m_perf exists in the struct.
+ WriteEntryHelper(eLegacyPrivateIPC_PerfCounters,
+ offsetof(LegacyPrivateIPCControlBlock, m_perf),
+ sizeof(PerfCounterIPCControlBlock));
+
+ WriteEntryHelper(eLegacyPrivateIPC_AppDomain,
+ offsetof(LegacyPrivateIPCControlBlock, m_appdomain),
+ sizeof(AppDomainEnumerationIPCBlock));
+ WriteEntryHelper(eLegacyPrivateIPC_InstancePath,
+ offsetof(LegacyPrivateIPCControlBlock, m_instancePath),
+ sizeof(m_ptrLegacyPrivateBlock->m_instancePath));
+
+ //
+ // Now explicitly mark the unused entries as empty.
+ //
+
+ WriteEntryHelper(eLegacyPrivateIPC_Obsolete_Debugger,
+ EMPTY_ENTRY_OFFSET, EMPTY_ENTRY_SIZE);
+ WriteEntryHelper(eLegacyPrivateIPC_Obsolete_ClassDump,
+ EMPTY_ENTRY_OFFSET, EMPTY_ENTRY_SIZE);
+ WriteEntryHelper(eLegacyPrivateIPC_Obsolete_MiniDump,
+ EMPTY_ENTRY_OFFSET, EMPTY_ENTRY_SIZE);
+ WriteEntryHelper(eLegacyPrivateIPC_Obsolete_Service,
+ EMPTY_ENTRY_OFFSET, EMPTY_ENTRY_SIZE);
+
+ // Cache our client pointers
+ m_pAppDomain = &(m_ptrLegacyPrivateBlock->m_appdomain);
+ m_pInstancePath = m_ptrLegacyPrivateBlock->m_instancePath;
+}
+
+PCWSTR IPCWriterInterface::GetInstancePath()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(IsLegacyPrivateBlockOpen());
+ return m_pInstancePath;
+}
+
+#endif
+
+PTR_VOID IPCWriterInterface::GetBlockStart()
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ return m_ptrLegacyPrivateBlock;
+}
+
+DWORD IPCWriterInterface::GetBlockSize()
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ _ASSERTE(IsLegacyPrivateBlockOpen());
+ return m_ptrLegacyPrivateBlock->m_FullIPCHeader.m_header.m_blockSize;
+}
+
+PTR_VOID IPCWriterInterface::GetBlockTableStart()
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ return m_pBlockTable;
+}
+
+DWORD IPCWriterInterface::GetBlockTableSize()
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+
+ _ASSERTE(IsBlockTableOpen());
+ return IPC_BLOCK_TABLE_SIZE;
+}
+
+#endif // FEATURE_IPCMAN
diff --git a/src/ipcman/stdafx.cpp b/src/ipcman/stdafx.cpp
new file mode 100644
index 0000000000..ca63c13d75
--- /dev/null
+++ b/src/ipcman/stdafx.cpp
@@ -0,0 +1,11 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+#include "stdafx.h"
diff --git a/src/ipcman/stdafx.h b/src/ipcman/stdafx.h
new file mode 100644
index 0000000000..65aa792ac9
--- /dev/null
+++ b/src/ipcman/stdafx.h
@@ -0,0 +1,30 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+// ==--==
+//*****************************************************************************
+// File: src\IPCMan\StdAfx.h
+//
+// Precompiled header for COM+ IPC Manager
+//
+//*****************************************************************************
+
+#include <winwrap.h>
+#include <windows.h>
+#include <stdlib.h>
+#include <objbase.h>
+#include <stddef.h>
+#include <float.h>
+#include <limits.h>
+#include <specstrings.h>
+#include "utilcode.h"
+#include "ex.h"
+#include "corpriv.h"
+#include "warningcontrol.h"
+#include "log.h"