diff options
author | dotnet-bot <dotnet-bot@microsoft.com> | 2015-01-30 14:14:42 -0800 |
---|---|---|
committer | dotnet-bot <dotnet-bot@microsoft.com> | 2015-01-30 14:14:42 -0800 |
commit | ef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch) | |
tree | dee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/ipcman | |
download | coreclr-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/.gitmirror | 1 | ||||
-rw-r--r-- | src/ipcman/CMakeLists.txt | 14 | ||||
-rw-r--r-- | src/ipcman/dirs.proj | 19 | ||||
-rw-r--r-- | src/ipcman/ipcenums.h | 93 | ||||
-rw-r--r-- | src/ipcman/ipcfunccallimpl.cpp | 654 | ||||
-rw-r--r-- | src/ipcman/ipcheader.h | 561 | ||||
-rw-r--r-- | src/ipcman/ipcheader.inl | 91 | ||||
-rw-r--r-- | src/ipcman/ipcman-staticcrt/.gitmirror | 1 | ||||
-rw-r--r-- | src/ipcman/ipcman-staticcrt/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/ipcman/ipcman-staticcrt/dirs.proj | 17 | ||||
-rw-r--r-- | src/ipcman/ipcman-staticcrt/ipcman-staticcrt.props | 11 | ||||
-rw-r--r-- | src/ipcman/ipcmanager.props | 28 | ||||
-rw-r--r-- | src/ipcman/ipcmanagerimpl.h | 148 | ||||
-rw-r--r-- | src/ipcman/ipcmanagerimpl.inl | 193 | ||||
-rw-r--r-- | src/ipcman/ipcreaderimpl.cpp | 1114 | ||||
-rw-r--r-- | src/ipcman/ipcshared.h | 95 | ||||
-rw-r--r-- | src/ipcman/ipcsharedsrc.cpp | 966 | ||||
-rw-r--r-- | src/ipcman/ipcwriterimpl.cpp | 918 | ||||
-rw-r--r-- | src/ipcman/stdafx.cpp | 11 | ||||
-rw-r--r-- | src/ipcman/stdafx.h | 30 |
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, ¶m) + { + 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, ¶m) + { + 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" |