Jamie Reece Wilson
b5c4271807
[*] Use waitpid instead of wait3 (BSD, now deprecated by POSIX) [*] Win32 / x86_32: I guess the file map maximum size should be the same as 64bit since it lets us? we need this span constraint to be the entire file or less. [*] Improved AuProcess UNIX signal safety [*] Comments [+] Secret API: RuntimeCollectMemory
651 lines
21 KiB
C++
651 lines
21 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 {};
|
|
}
|
|
|
|
if (!pCreateFileMappingA)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
hFileMap = pCreateFileMappingA(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 {};
|
|
HANDLE hEventHandle { INVALID_HANDLE_VALUE };
|
|
OVERLAPPED overlapped {};
|
|
|
|
if (processLockLevel == AuFS::EFileAdvisoryLockLevel::eBlockReadWrite)
|
|
{
|
|
dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
|
|
}
|
|
|
|
dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
|
|
|
|
overlapped.Offset = AuBitsToLower(uOffset);
|
|
overlapped.OffsetHigh = AuBitsToHigher(uOffset);
|
|
|
|
#if 0
|
|
bool bIsAsync = pIOHandle->IsAsync();
|
|
if (bIsAsync)
|
|
{
|
|
hEventHandle = ::CreateEventA(nullptr, false, false, nullptr);
|
|
if (!hEventHandle)
|
|
{
|
|
SysPushErrorGeneric();
|
|
return {};
|
|
}
|
|
|
|
overlapped.hEvent = hEventHandle;
|
|
}
|
|
#endif
|
|
|
|
auto hHandle = (HANDLE)pIOHandle->GetOSHandle();
|
|
if (!::LockFileEx(hHandle,
|
|
dwFlags,
|
|
0,
|
|
AuBitsToLower(uLength),
|
|
AuBitsToHigher(uLength),
|
|
&overlapped))
|
|
{
|
|
#if 0
|
|
if (GetLastError() == ERROR_IO_PENDING)
|
|
{
|
|
::WaitForSingleObject(hEventHandle, 0);
|
|
|
|
DWORD idc {};
|
|
if (!::GetOverlappedResult(hHandle,
|
|
&overlapped,
|
|
&idc,
|
|
true))
|
|
{
|
|
SysPushErrorIO("No Lock");
|
|
AuWin32CloseHandle(hEventHandle);
|
|
return {};
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
SysPushErrorIO("No Lock");
|
|
AuWin32CloseHandle(hEventHandle);
|
|
return {};
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AuWin32CloseHandle(hEventHandle);
|
|
}
|
|
}
|
|
|
|
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 {};
|
|
};
|
|
|
|
if (!pCreateFileMappingA)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
hFileMap = pCreateFileMappingA((HANDLE)pIOHandle->GetOSHandle(),
|
|
nullptr,
|
|
pageAttributes,
|
|
AuBitsToHigher(AuUInt64(uLength) + uOffset),
|
|
AuBitsToLower(AuUInt64(uLength) + uOffset),
|
|
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 = pOpenFileMappingA(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()
|
|
{
|
|
|
|
}
|
|
} |