AuroraRuntime/Source/Process/AuProcessSectionView.NT.cpp

438 lines
16 KiB
C++

/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuProcessSectionView.NT.cpp
Date: 2022-08-09
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuProcessSectionView.NT.hpp"
#include "Process.hpp"
#include <Source/IO/FS/FileStream.NT.hpp>
#include "AuProcessSectionFileMapView.NT.hpp"
#include <Source/IO/IPC/AuIPCHandle.hpp>
#include <Windows.h>
namespace Aurora::Process
{
AuUInt ProcessSectionView::GetStart()
{
return AuNumericLimits<AuUInt>::min();
}
AuUInt ProcessSectionView::GetEnd()
{
return AuNumericLimits<AuUInt>::max();
}
AuSPtr<IProcessSectionMapView> ProcessSectionView::Allocate(AuUInt uLength)
{
PageTable table {};
table.NX = true;
table.readable = true;
table.writable = true;
return this->AllocateEx2(uLength, 0, table);
}
AuSPtr<IProcessSectionMapView> ProcessSectionView::MapFileByPath(const AuString &str,
AuUInt64 uOffset,
AuUInt uLength,
AuFS::EFileOpenMode mode,
AuFS::EFileAdvisoryLockLevel sectionLock)
{
auto file = AuFS::OpenShared(str, mode, AuFS::EFileAdvisoryLockLevel::eNoSafety);
return file ? this->MapFileByObject(file, uOffset, uLength, mode, sectionLock) : AuSPtr<IProcessSectionMapView> {};
}
AuSPtr<IProcessSectionMapView> ProcessSectionView::MapFileByObject(const AuSPtr<AuFS::IFileStream> &pStream,
AuUInt64 uOffset,
AuUInt uLength,
AuFS::EFileOpenMode mode,
AuFS::EFileAdvisoryLockLevel processLockLevel)
{
HANDLE hFileMap;
ULONG desiredAccess {}, pageAttributes {};
if (!pStream)
{
SysPushErrorArg();
return {};
}
if (!uLength)
{
SysPushErrorArg("invalid uLength");
return {};
}
auto pFileStream = AuStaticCast<AuFS::WinFileStream>(pStream);
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(pFileStream->GetHandle(),
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(pFileStream->GetHandle(),
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 = ::MapViewOfFile(hFileMap,
desiredAccess,
AuBitsToHigher(uOffset),
AuBitsToLower(uOffset),
uLength);
if (!map)
{
SysPushErrorIO("Couldn't create map of section");
AuWin32CloseHandle(hFileMap);
return {};
}
auto pNewObject = AuMakeShared<ProcessSectionFileMapView>(AuUInt(map), hFileMap);
if (!pNewObject)
{
SysPushErrorMem();
AuWin32CloseHandle(hFileMap);
::UnmapViewOfFile(map);
return {};
}
pNewObject->pProcessGlobalHint = this;
pNewObject->uLength = uLength;
return pNewObject;
}
AuSPtr<IProcessSectionMapView> ProcessSectionView::MapIPCMemory(const AuString &handleString,
AuUInt64 uOffset,
AuUInt uLength,
AuFS::EFileOpenMode mode)
{
AuIPC::IPCHandle handle;
HANDLE hFileMap;
ULONG desiredAccess {}, pageAttributes {};
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:
{
desiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE;
pageAttributes = PAGE_READWRITE;
break;
}
default:
SysPushErrorGeneric();
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 = ::MapViewOfFile(hFileMap,
desiredAccess,
AuBitsToHigher(uOffset),
AuBitsToLower(uOffset),
uLength);
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->pProcessGlobalHint = this;
pNewObject->uLength = uLength;
return pNewObject;
}
AuSPtr<IProcessSectionMapView> ProcessSectionView::AllocateEx(AuUInt uLength,
AuUInt uOffset)
{
SysAssert(this->bPanicOnEx, "Windows 7/8 called a Windows 10 RS4 memory management routine. "
"Applications requiring explicit and pre-reserved memory maps cannot run on unmodified unsupported versions of Windows."
"We lied about reserving a region of bytes with a ProcessSectionView. Unable to gurantee uOffset.");
return this->Allocate(uLength);
}
AuSPtr<IProcessSectionMapView> ProcessSectionView::AllocateEx2(AuUInt uLength,
AuUInt uOffset,
PageTable permissions)
{
SysAssert(this->bPanicOnEx, "Windows 7/8 called a Windows 10 RS4 memory management routine. "
"Applications requiring explicit and pre-reserved memory maps cannot run on unmodified unsupported versions of Windows."
"We lied about reserving a region of bytes with a ProcessSectionView. Unable to gurantee uOffset.");
HANDLE hFileMap;
if (!uLength)
{
SysPushErrorArg("invalid uLength");
return {};
}
DWORD uPageFlags {};
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;
}
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 {};
}
DWORD sectionPermission {};
if (permissions.readable)
{
sectionPermission = SECTION_MAP_READ;
}
if (permissions.writable)
{
sectionPermission |= SECTION_MAP_WRITE;
}
if (!permissions.NX)
{
sectionPermission |= SECTION_MAP_EXECUTE;
}
auto map = ::MapViewOfFile(hFileMap,
sectionPermission,
0,
0,
uLength);
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->pProcessGlobalHint = this;
pNewObject->uLength = uLength;
return pNewObject;
}
AuSPtr<IProcessSectionMapView> ProcessSectionView::MapFileByPathEx(AuUInt viewOffset,
const AuString &str,
AuUInt64 uOffset,
AuUInt uLength,
Aurora::IO::FS::EFileOpenMode mode,
Aurora::IO::FS::EFileAdvisoryLockLevel processLockLevel)
{
SysAssert(this->bPanicOnEx, "Windows 7/8 called a Windows 10 RS4 memory management routine. "
"Applications requiring explicit and pre-reserved memory maps cannot run on unmodified unsupported versions of Windows."
"We lied about reserving a region of bytes with a ProcessSectionView. Unable to gurantee uOffset.");
return this->MapFileByPath(str, uOffset, uLength, mode, processLockLevel);
}
AuSPtr<IProcessSectionMapView> ProcessSectionView::MapFileByObjectEx(AuUInt viewOffset,
const AuSPtr<Aurora::IO::FS::IFileStream> &pStream,
AuUInt64 uOffset,
AuUInt uLength,
Aurora::IO::FS::EFileOpenMode mode,
Aurora::IO::FS::EFileAdvisoryLockLevel processLockLevel)
{
SysAssert(this->bPanicOnEx, "Windows 7/8 called a Windows 10 RS4 memory management routine. "
"Applications requiring explicit and pre-reserved memory maps cannot run on unmodified unsupported versions of Windows."
"We lied about reserving a region of bytes with a ProcessSectionView. Unable to gurantee uOffset.");
return this->MapFileByObject(pStream, uOffset, uLength, mode, processLockLevel);
}
AuSPtr<IProcessSectionMapView> ProcessSectionView::MapIPCMemoryEx(AuUInt viewOffset,
const AuString &handle,
AuUInt64 uOffset,
AuUInt uLength,
Aurora::IO::FS::EFileOpenMode mode)
{
SysAssert(this->bPanicOnEx, "Windows 7/8 called a Windows 10 RS4 memory management routine. "
"Applications requiring explicit and pre-reserved memory maps cannot run on unmodified unsupported versions of Windows."
"We lied about reserving a region of bytes with a ProcessSectionView. Unable to gurantee uOffset.");
return this->MapIPCMemory(handle, uOffset, uLength, mode);
}
AuList<AuPair<AuUInt, AuUInt>> ProcessSectionView::GetAllocations()
{
AU_LOCK_GUARD(this->spinlock);
return this->allocations;
}
void ProcessSectionView::DoVanillaDriverlessExtesionWin7Test()
{
#if defined(AURORA_PLATFORM_WIN32)
#define WIN_7_WARN "WARNING: ADDRESS SPACE CANNOT BE RESERVED ON OLDER NT KERNELS. \r\n" \
"AuProcess::ReserveAddressSpace(AuUInt uOffset) is about to lie about reserving the address space, yield the entire address space, and leave a note to terminate the application if an explicit fixed-offset request is made."
SysPushErrorUnimplemented("Win7_ReserveAddressSpace_RS4_REQ");
AuUInt uEnvSize {};
::getenv_s(&uEnvSize, nullptr, 0, "AURORA_FORCE_RANDOM_ADDRESS_WITHOUT_VIRTALLOC2");
if (uEnvSize)
{
// bah
// enjoy return not respecting what was provided as the expected offset.
// this is not a workaround for missing functionality in the operating systems userland and kernel abstraction.
// this will just prevent us from panicing preemptively.
this->bPanicOnEx = true;
}
else
{
AuLogWarn(WIN_7_WARN);
this->bPanicOnEx = false;
}
#else
AuLogWarn("I don't know this platform - AuProcess");
this->bPanicOnEx = true;
#endif
}
AUKN_SYM AuSPtr<IProcessSectionView> GetGlobalProcessSpace()
{
static ProcessSectionView gSingleton;
gSingleton.DoVanillaDriverlessExtesionWin7Test();
return AuUnsafeRaiiToShared(&gSingleton);
}
}