diff options
Diffstat (limited to 'src/ipcman/ipcwriterimpl.cpp')
-rw-r--r-- | src/ipcman/ipcwriterimpl.cpp | 917 |
1 files changed, 917 insertions, 0 deletions
diff --git a/src/ipcman/ipcwriterimpl.cpp b/src/ipcman/ipcwriterimpl.cpp new file mode 100644 index 0000000000..b618f4b542 --- /dev/null +++ b/src/ipcman/ipcwriterimpl.cpp @@ -0,0 +1,917 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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 |