AuroraRuntime/Source/Process/AuProcessSectionViewReserved.NT.cpp

604 lines
20 KiB
C++

/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuProcessSectionViewReserved.NT.cpp
Date: 2022-9-30
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuProcessSectionViewReserved.NT.hpp"
#include "Process.hpp"
#include <Source/IO/FS/FileStream.NT.hpp>
#include "AuProcessSectionFileMapView.NT.hpp"
#include "AuProcessSectionView.NT.hpp"
#include <Source/IO/IPC/AuIPCHandle.hpp>
#include <Windows.h>
namespace Aurora::Process
{
AuUInt ProcessSectionViewReserved::GetStart()
{
return (AuUInt)this->pBaseAddress;
}
AuUInt ProcessSectionViewReserved::GetEnd()
{
return this->GetStart() + this->uMaxLength;
}
bool ProcessSectionViewReserved::Init(AuUInt uLength)
{
this->uMaxLength = uLength;
this->pBaseAddress = AuReinterpretCast<decltype(this->pBaseAddress)>(pVirtualAlloc2(
nullptr,
nullptr,
this->uMaxLength,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS,
nullptr,
0));
return bool(this->pBaseAddress);
}
bool ProcessSectionViewReserved::AllocateAddress(AuUInt uOffset,
AuUInt uLength,
AuUInt &uAddressOut)
{
bool bCompleteBlock {};
AuUInt uFoundOffset {};
uAddressOut = 0;
if (!this->GetAddress(uOffset, uLength, uFoundOffset, bCompleteBlock))
{
SysPushErrorMemory("Reserved address space has no available space");
return false;
}
if (bCompleteBlock)
{
uAddressOut = uFoundOffset;
return true;
}
if (!::VirtualFree(this->pBaseAddress + uFoundOffset,
uLength,
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER))
{
SysPushErrorMemory("System refused to split desired region");
ReleaseAddress(uFoundOffset);
uAddressOut = uFoundOffset;
return false;
}
return true;
}
bool ProcessSectionViewReserved::ReleaseAndCoaleceAddress(AuUInt uOffset, AuUInt uLength)
{
AU_LOCK_GUARD(this->spinlock);
bool bSuccess {};
for (AuUInt i = 0; i < this->allocations.size(); i++)
{
auto &a = this->allocations[i];
if (a.first != uOffset)
{
continue;
}
if (uLength != a.second)
{
break;
}
AuUInt uPrevFree {};
AuUInt uNextFree {};
{
if (i != 0)
{
auto eh = this->allocations[i - 1];
uPrevFree = eh.first + eh.second;
}
else
{
uPrevFree = uOffset;
}
}
{
AuUInt uStart {};
if (i != this->allocations.size() - 1)
{
uNextFree = this->allocations[i + 1].first;
}
else if (this->allocations.size() == 1)
{
uNextFree = this->uMaxLength;
}
else
{
uNextFree = uOffset + uLength;
}
}
if (!pUnmapViewOfFile2(INVALID_HANDLE_VALUE,
this->pBaseAddress + uOffset,
MEM_PRESERVE_PLACEHOLDER))
{
SysPushErrorMemory("System refused to restore placeholder: {}", GetLastError());
break;
}
#if 0
if (!::VirtualFree(this->pBaseAddress + uOffset,
0,
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER))
{
SysPushErrorMemory("System refused to restore placeholder: {}", GetLastError());
break;
}
#endif
auto uFreeRange = uNextFree - uPrevFree;
if (uFreeRange != uLength)
{
if (!::VirtualFree(this->pBaseAddress + uPrevFree,
uFreeRange,
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS))
{
SysPushErrorMemory("System refused to coalece memory segments");
break;
}
}
bSuccess = true;
break;
}
if (bSuccess)
{
AuTryRemoveByTupleN<0>(this->allocations, uOffset);
}
return bSuccess;
}
AuSPtr<IProcessSectionMapView> ProcessSectionViewReserved::Allocate(AuUInt uLength)
{
PageTable table {};
table.NX = true;
table.readable = true;
table.writable = true;
return this->AllocateEx2(uLength, -1, table);
}
AuSPtr<IProcessSectionMapView> ProcessSectionViewReserved::MapFileByPath(const AuString &str,
AuUInt64 uOffset,
AuUInt uLength,
AuFS::EFileOpenMode mode,
AuFS::EFileAdvisoryLockLevel sectionLock)
{
return this->MapFileByPathEx(-1, str, uOffset, uLength, mode, sectionLock);
}
AuSPtr<IProcessSectionMapView> ProcessSectionViewReserved::MapFileByObject(const AuSPtr<IO::IIOHandle> &pIOHandle,
AuUInt64 uOffset,
AuUInt uLength,
AuFS::EFileOpenMode mode,
AuFS::EFileAdvisoryLockLevel processLockLevel)
{
return this->MapFileByObjectEx(-1, pIOHandle, uOffset, uLength, mode, processLockLevel);
}
AuSPtr<IProcessSectionMapView> ProcessSectionViewReserved::MapIPCMemory(const AuString &handleString,
AuUInt64 uOffset,
AuUInt uLength,
AuFS::EFileOpenMode mode)
{
return this->MapIPCMemoryEx(-1, handleString, uOffset, uLength, mode);
}
AuSPtr<IProcessSectionMapView> ProcessSectionViewReserved::AllocateEx(AuUInt uLength,
AuUInt uOffset)
{
PageTable table {};
table.NX = true;
table.readable = true;
table.writable = true;
return this->AllocateEx2(uLength, uOffset, table);
}
AuSPtr<IProcessSectionMapView> ProcessSectionViewReserved::AllocateEx2(AuUInt uLength,
AuUInt uOffset,
PageTable permissions)
{
HANDLE hFileMap;
DWORD uPageFlags {};
AuUInt uAddressOut;
if (!uLength)
{
SysPushErrorArg("invalid uLength");
return {};
}
if (permissions.writable && permissions.NX)
{
uPageFlags = PAGE_READWRITE;
}
else if (permissions.readable && permissions.NX)
{
uPageFlags = PAGE_READONLY;
}
else if (permissions.writable)
{
uPageFlags = PAGE_EXECUTE_READWRITE;
}
else if (permissions.readable)
{
uPageFlags = PAGE_EXECUTE_READ;
}
if (!this->AllocateAddress(uOffset, uLength, uAddressOut))
{
return {};
}
hFileMap = ::CreateFileMappingA(INVALID_HANDLE_VALUE,
nullptr,
uPageFlags,
#if defined(AURORA_IS_64BIT)
AuBitsToHigher(uLength),
AuBitsToLower(uLength),
#else
0,
uLength,
#endif
nullptr);
if ((hFileMap == INVALID_HANDLE_VALUE) ||
(!hFileMap))
{
SysPushErrorIO("Couldn't create file map");
return {};
}
auto map = pMapViewOfFile3(hFileMap,
INVALID_HANDLE_VALUE,
this->pBaseAddress + uAddressOut,
0,
uLength,
MEM_REPLACE_PLACEHOLDER,
uPageFlags,
nullptr,
0);
if (!map)
{
SysPushErrorIO("Couldn't create allocation of section");
AuWin32CloseHandle(hFileMap);
return {};
}
auto pNewObject = AuMakeShared<ProcessSectionFileMapView>(AuUInt(map), hFileMap);
if (!pNewObject)
{
SysPushErrorMem();
AuWin32CloseHandle(hFileMap);
return {};
}
pNewObject->pSharedSectionHint = AuSharedFromThis();
pNewObject->uOffset = uAddressOut;
pNewObject->uLength = uLength;
return pNewObject;
}
AuSPtr<IProcessSectionMapView> ProcessSectionViewReserved::MapFileByPathEx(AuUInt viewOffset,
const AuString &str,
AuUInt64 uOffset,
AuUInt uLength,
Aurora::IO::FS::EFileOpenMode mode,
Aurora::IO::FS::EFileAdvisoryLockLevel sectionLock)
{
auto pHandle = AuIO::IOHandleShared();
if (!pHandle)
{
SysPushErrorMemory();
return nullptr;
}
AuIO::IIOHandle::HandleCreate createhandle(str);
createhandle.eAdvisoryLevel = AuFS::EFileAdvisoryLockLevel::eNoSafety;
createhandle.eMode = mode;
createhandle.bFailIfNonEmptyFile = false;
createhandle.bDirectIOMode = false;
createhandle.bAsyncHandle = false;
if (!pHandle->InitFromPath(createhandle))
{
return nullptr;
}
return this->MapFileByObjectEx(viewOffset, pHandle, uOffset, uLength, mode, sectionLock);
}
AuSPtr<IProcessSectionMapView> ProcessSectionViewReserved::MapFileByObjectEx(AuUInt viewOffset,
const AuSPtr<IO::IIOHandle> &pIOHandle,
AuUInt64 uOffset,
AuUInt uLength,
Aurora::IO::FS::EFileOpenMode mode,
Aurora::IO::FS::EFileAdvisoryLockLevel processLockLevel)
{
HANDLE hFileMap;
ULONG desiredAccess {}, pageAttributes {};
AuUInt uAddressOut;
if (!pIOHandle)
{
SysPushErrorArg();
return {};
}
if (!uLength)
{
SysPushErrorArg("invalid uLength");
return {};
}
if (!this->AllocateAddress(viewOffset, uLength, uAddressOut))
{
return {};
}
if (processLockLevel != AuFS::EFileAdvisoryLockLevel::eNoSafety)
{
DWORD dwFlags {};
OVERLAPPED overlapped {};
if (processLockLevel == AuFS::EFileAdvisoryLockLevel::eBlockReadWrite)
{
dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
}
dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
overlapped.Offset = AuBitsToLower(uOffset);
overlapped.OffsetHigh = AuBitsToHigher(uOffset);
if (!::LockFileEx((HANDLE)pIOHandle->GetOSHandle(),
dwFlags,
0,
AuBitsToLower(uLength),
AuBitsToHigher(uLength),
&overlapped))
{
SysPushErrorIO("No Lock");
return {};
}
}
switch (mode)
{
case AuFS::EFileOpenMode::eRead:
{
desiredAccess = SECTION_MAP_READ;
pageAttributes = PAGE_READONLY;
break;
}
case AuFS::EFileOpenMode::eWrite:
case AuFS::EFileOpenMode::eReadWrite:
{
desiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE;
pageAttributes = PAGE_READWRITE;
break;
}
default:
SysPushErrorGeneric();
return {};
};
hFileMap = ::CreateFileMappingA((HANDLE)pIOHandle->GetOSHandle(),
nullptr,
pageAttributes,
#if defined(AURORA_IS_64BIT)
AuBitsToHigher(uLength),
AuBitsToLower(uLength),
#else
0,
uLength,
#endif
nullptr);
if ((hFileMap == INVALID_HANDLE_VALUE) ||
(!hFileMap))
{
SysPushErrorIO("Couldn't create file map. Is the requeted access mode too high for the given object?");
return {};
}
auto map = pMapViewOfFile3(hFileMap,
INVALID_HANDLE_VALUE,
this->pBaseAddress + uAddressOut,
uOffset,
uLength,
MEM_REPLACE_PLACEHOLDER,
pageAttributes,
nullptr,
0);
if (!map)
{
SysPushErrorIO("Couldn't create map of section: {}", GetLastError());
AuWin32CloseHandle(hFileMap);
return {};
}
auto pNewObject = AuMakeShared<ProcessSectionFileMapView>(AuUInt(map), hFileMap);
if (!pNewObject)
{
SysPushErrorMem();
AuWin32CloseHandle(hFileMap);
::UnmapViewOfFile(map);
return {};
}
pNewObject->pSharedSectionHint = AuSharedFromThis();
pNewObject->uOffset = uAddressOut;
pNewObject->uLength = uLength;
return pNewObject;
}
AuSPtr<IProcessSectionMapView> ProcessSectionViewReserved::MapIPCMemoryEx(AuUInt viewOffset,
const AuString &handleString,
AuUInt64 uOffset,
AuUInt uLength,
Aurora::IO::FS::EFileOpenMode mode)
{
AuIPC::IPCHandle handle;
HANDLE hFileMap;
ULONG desiredAccess {}, pageAttributes {};
AuUInt uAddressOut;
if (!uLength)
{
SysPushErrorArg("invalid uLength");
return {};
}
if (!handle.FromString(handleString))
{
SysPushErrorParseError("{}", handleString);
return {};
}
auto token = handle.GetToken(AuIPC::EIPCHandleType::eIPCMemory, 0);
if (!token)
{
SysPushErrorParseError();
return {};
}
auto actualLength = token->token.word;
auto path = token->token.ToNTPath();
if (actualLength < uOffset + uLength)
{
SysPushErrorIO("Out of range");
return {};
}
switch (mode)
{
case AuFS::EFileOpenMode::eRead:
{
desiredAccess = SECTION_MAP_READ;
pageAttributes = PAGE_READONLY;
break;
}
case AuFS::EFileOpenMode::eWrite:
case AuFS::EFileOpenMode::eReadWrite:
{
pageAttributes = PAGE_READWRITE;
break;
}
default:
SysPushErrorGeneric();
return {};
};
if (!this->AllocateAddress(viewOffset, uLength, uAddressOut))
{
return {};
}
hFileMap = ::OpenFileMappingA(desiredAccess,
FALSE,
path.c_str());
if ((hFileMap == INVALID_HANDLE_VALUE) ||
(!hFileMap))
{
SysPushErrorIO("Couldn't create IPC map (handle: {})", handleString);
return {};
}
auto map = pMapViewOfFile3(hFileMap,
INVALID_HANDLE_VALUE,
this->pBaseAddress + uAddressOut,
uOffset,
uLength,
MEM_REPLACE_PLACEHOLDER,
pageAttributes,
nullptr,
0);
if (!map)
{
SysPushErrorIO("Couldn't create map of IPC section (handle: {})", handleString);
AuWin32CloseHandle(hFileMap);
return {};
}
auto pNewObject = AuMakeShared<ProcessSectionFileMapView>(AuUInt(map), hFileMap);
if (!pNewObject)
{
SysPushErrorMem();
AuWin32CloseHandle(hFileMap);
::UnmapViewOfFile(map);
return {};
}
pNewObject->pSharedSectionHint = AuSharedFromThis();
pNewObject->uOffset = uAddressOut;
pNewObject->uLength = uLength;
return pNewObject;
}
AuList<AuPair<AuUInt, AuUInt>> ProcessSectionViewReserved::GetAllocations()
{
AU_LOCK_GUARD(this->spinlock);
return this->allocations;
}
AUKN_SYM AuSPtr<IProcessSectionView> ReserveAddressSpace(AuUInt uLength)
{
auto &platform = AuSwInfo::GetPlatformInfo();
#if 0
if (!AuSwInfo::IsWindows10OrGreater() || // < win10 or
((platform.uKernelMajor == 10) && (platform.uKernelPatch < 1803)) ||
!VirtualAlloc2_f ||
!MapViewOfFile3_f) // < RS4
#else
if (!pVirtualAlloc2) // allow modified windows runtimes
#endif
{
static ProcessSectionView gSingleton;
gSingleton.DoVanillaDriverlessExtesionWin7Test();
return AuUnsafeRaiiToShared(&gSingleton);
}
auto pSectionView = AuMakeShared<ProcessSectionViewReserved>();
if (!pSectionView)
{
SysPushErrorMemory();
return {};
}
if (!pSectionView->Init(uLength))
{
SysPushErrorMemory();
return {};
}
return pSectionView;
}
void LoadProcessSectionViewSymbol()
{
}
}