/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuProcessSectionViewReserved.NT.cpp Date: 2022-9-30 Author: Reece ***/ #include #include "AuProcessSectionViewReserved.NT.hpp" #include "Process.hpp" #include #include "AuProcessSectionFileMapView.NT.hpp" #include "AuProcessSectionView.NT.hpp" #include #include 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 = AuReinterpretCastpBaseAddress)>(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 ProcessSectionViewReserved::Allocate(AuUInt uLength) { PageTable table {}; table.NX = true; table.readable = true; table.writable = true; return this->AllocateEx2(uLength, -1, table); } AuSPtr 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 ProcessSectionViewReserved::MapFileByObject(const AuSPtr &pIOHandle, AuUInt64 uOffset, AuUInt uLength, AuFS::EFileOpenMode mode, AuFS::EFileAdvisoryLockLevel processLockLevel) { return this->MapFileByObjectEx(-1, pIOHandle, uOffset, uLength, mode, processLockLevel); } AuSPtr ProcessSectionViewReserved::MapIPCMemory(const AuString &handleString, AuUInt64 uOffset, AuUInt uLength, AuFS::EFileOpenMode mode) { return this->MapIPCMemoryEx(-1, handleString, uOffset, uLength, mode); } AuSPtr ProcessSectionViewReserved::AllocateEx(AuUInt uLength, AuUInt uOffset) { PageTable table {}; table.NX = true; table.readable = true; table.writable = true; return this->AllocateEx2(uLength, uOffset, table); } AuSPtr 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(AuUInt(map), hFileMap); if (!pNewObject) { SysPushErrorMem(); AuWin32CloseHandle(hFileMap); return {}; } pNewObject->pSharedSectionHint = AuSharedFromThis(); pNewObject->uOffset = uAddressOut; pNewObject->uLength = uLength; return pNewObject; } AuSPtr 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 ProcessSectionViewReserved::MapFileByObjectEx(AuUInt viewOffset, const AuSPtr &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(AuUInt(map), hFileMap); if (!pNewObject) { SysPushErrorMem(); AuWin32CloseHandle(hFileMap); ::UnmapViewOfFile(map); return {}; } pNewObject->pSharedSectionHint = AuSharedFromThis(); pNewObject->uOffset = uAddressOut; pNewObject->uLength = uLength; return pNewObject; } AuSPtr 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(AuUInt(map), hFileMap); if (!pNewObject) { SysPushErrorMem(); AuWin32CloseHandle(hFileMap); ::UnmapViewOfFile(map); return {}; } pNewObject->pSharedSectionHint = AuSharedFromThis(); pNewObject->uOffset = uAddressOut; pNewObject->uLength = uLength; return pNewObject; } AuList> ProcessSectionViewReserved::GetAllocations() { AU_LOCK_GUARD(this->spinlock); return this->allocations; } AUKN_SYM AuSPtr 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(); if (!pSectionView) { SysPushErrorMemory(); return {}; } if (!pSectionView->Init(uLength)) { SysPushErrorMemory(); return {}; } return pSectionView; } void LoadProcessSectionViewSymbol() { } }