Xbox permissions work

This commit is contained in:
ergo720 2018-03-03 20:24:57 +01:00
parent c1c989c077
commit bc0e0734ae
4 changed files with 151 additions and 59 deletions

View File

@ -446,6 +446,111 @@ bool PhysicalMemory::ConvertXboxToSystemPteProtection(DWORD perms, PMMPTE pPte)
return true;
}
DWORD PhysicalMemory::PatchXboxPermissions(DWORD Perms)
{
// Usage notes: this routine expects the permissions to be already sanitized by ConvertXboxToSystemPteProtection or
// similar. If not, it can produce incorrect results
// ergo720: this checks if the specified Xbox permission mask has the execute flag enabled. If not, it adds it. This is
// necessary because, afaik, the Xbox grants execute rights to all allocations, even if PAGE_EXECUTE was not specified
if ((Perms >> 4) & 0xF)
{
// All high nibble flags grant execute rights, nothing to do
return Perms;
}
if (Perms & XBOX_PAGE_NOACCESS)
{
// XBOX_PAGE_NOACCESS disables all access, nothing to do
return Perms;
}
switch (Perms & (XBOX_PAGE_READONLY | XBOX_PAGE_READWRITE))
{
case XBOX_PAGE_READONLY:
return XBOX_PAGE_EXECUTE_READ;
case XBOX_PAGE_READWRITE:
return XBOX_PAGE_EXECUTE_READWRITE;
default:
{
// If we reach here it means that both XBOX_PAGE_READONLY and XBOX_PAGE_READWRITE were specified, and so the
// input is probably invalid
DbgPrintf("PatchXboxPermissions: Memory permissions bug detected\n");
return XBOX_PAGE_EXECUTE_READWRITE;
}
}
}
DWORD PhysicalMemory::ConvertXboxToWinProtection(DWORD Perms)
{
// This function assumes that the supplied permissions have been sanitized already
DWORD Mask = 0;
if (Perms & XBOX_PAGE_NOACCESS)
{
// PAGE_NOACCESS cannot be specified with anything else
return PAGE_NOACCESS;
}
DWORD LowNibble = Perms & 0xF;
DWORD HighNibble = (Perms >> 4) & 0xF;
if (HighNibble)
{
if (HighNibble == 1) { Mask |= PAGE_EXECUTE; }
else if (HighNibble == 2) { Mask |= PAGE_EXECUTE_READ; }
else { Mask |= PAGE_EXECUTE_READWRITE; }
}
if (LowNibble)
{
if (LowNibble == 2) { Mask |= PAGE_READONLY; }
else { Mask |= PAGE_READWRITE; }
}
// Even though PAGE_NOCACHE and PAGE_WRITECOMBINE are unsupported on shared memory, that's a limitation on our side,
// this function still adds them if they are present
switch (Perms & (XBOX_PAGE_GUARD | XBOX_PAGE_NOCACHE | XBOX_PAGE_WRITECOMBINE))
{
case XBOX_PAGE_GUARD:
{
Mask |= PAGE_GUARD;
break;
}
case XBOX_PAGE_NOCACHE:
{
Mask |= PAGE_NOCACHE;
break;
}
case XBOX_PAGE_WRITECOMBINE:
{
Mask |= PAGE_WRITECOMBINE;
break;
}
default:
{
// If we reach here it means that more than one permission modifier was specified, and so the input is
// probably invalid
DbgPrintf("ConvertXboxToWinProtection: Memory permissions bug detected\n");
return PAGE_EXECUTE_READWRITE;
}
}
return Mask;
}
bool PhysicalMemory::AllocatePT(PFN_COUNT PteNumber, VAddr addr)
{
PMMPTE pPde;

View File

@ -50,13 +50,6 @@ namespace xboxkrnl
#include <map>
/* PhysicalMemory class error codes */
#define PMEMORY_STATUS unsigned int
#define PMEMORY_SUCCESS 0x0
#define PMEMORY_INSUFFICIENT_MEMORY 0x1
#define PMEMORY_ALLOCATE_FRAGMENTED 0x2
/* Global typedefs */
typedef uintptr_t VAddr;
typedef uintptr_t PAddr;
@ -266,6 +259,10 @@ class PhysicalMemory
void InsertFree(PFN start, PFN end);
// convert from Xbox to the desired system pte protection (if possible) and return it
bool ConvertXboxToSystemPteProtection(DWORD perms, PMMPTE pPte);
// convert from Xbox to Windows permissions
DWORD ConvertXboxToWinProtection(DWORD Perms);
// add execute rights if the permission mask doesn't include it
DWORD PatchXboxPermissions(DWORD Perms);
// commit page tables (if necessary)
bool AllocatePT(PFN_COUNT PteNumber, VAddr addr);
// commit whatever free page is available and zero it

View File

@ -409,7 +409,7 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz
if (RemoveFree(PteNumber, &pfn, 0, 0, m_HighestPage)) // MapViewOfFileEx path
{
MappingRoutine = &MapBlockWithMapViewOfFileEx;
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, Perms, pfn);
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, pfn);
if (!addr)
{
@ -426,7 +426,7 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz
bVAlloc = true;
MappingRoutine = &MapBlockWithVirtualAlloc;
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, Perms, 0);
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, 0);
if (!addr) { goto Fail; }
@ -469,7 +469,7 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz
PointerPte++;
}
EndingPte = PointerPte + PteNumber - 1;
EndingPfn = pfn + (EndingPte - PointerPte);
EndingPfn = pfn + PteNumber;
WritePte(PointerPte, EndingPte, TempPte, pfn);
EndingPte->Hardware.GuardOrEnd = 1;
@ -486,7 +486,17 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz
m_PagesByUsage[BusyType] += PteNumber;
}
ConstructVMA(addr, ROUND_UP_4K(Size), MemoryRegionType::System, VMAType::Allocated, bVAlloc ? true : false);
ConstructVMA(addr, ROUND_UP_4K(Size), MemoryRegionType::System, VMAType::Allocated, bVAlloc);
if (bAddGuardPage)
{
UpdateMemoryPermissions(addr, PAGE_SIZE, XBOX_PAGE_NOACCESS); // guard page of the stack
UpdateMemoryPermissions(addr + PAGE_SIZE, ROUND_UP_4K(Size) - PAGE_SIZE, Perms); // actual stack pages
}
else
{
UpdateMemoryPermissions(addr, ROUND_UP_4K(Size), Perms);
}
Unlock();
RETURN(addr);
@ -545,13 +555,14 @@ VAddr VMManager::AllocateContiguous(size_t Size, PAddr LowerAddress, PAddr Highe
// Finally, write the pte's and the pfn's
PointerPte = GetPteAddress(addr);
EndingPte = PointerPte + PteNumber - 1;
EndingPfn = pfn + (EndingPte - PointerPte);
EndingPfn = pfn + PteNumber;
WritePte(PointerPte, EndingPte, TempPte, pfn);
WritePfn(pfn, EndingPfn, PointerPte, PageType::Contiguous, true);
EndingPte->Hardware.GuardOrEnd = 1;
ConstructVMA(addr, ROUND_UP_4K(Size), MemoryRegionType::Contiguous, VMAType::Allocated, false);
UpdateMemoryPermissions(addr, ROUND_UP_4K(Size), Perms);
Unlock();
RETURN(addr);
@ -603,7 +614,7 @@ VAddr VMManager::MapDeviceMemory(PAddr Paddr, size_t Size, DWORD Perms)
Lock();
MappingRoutine = &ReserveBlockWithVirtualAlloc;
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, Perms, 0);
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, 0);
if (!addr) { goto Fail; }
@ -980,7 +991,7 @@ xboxkrnl::NTSTATUS VMManager::XbFreeVirtualMemory(VAddr* addr, size_t* size, DWO
RETURN(ret);
}
VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, PFN_COUNT PteNumber, DWORD perms, PFN pfn)
VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, PFN_COUNT PteNumber, PFN pfn)
{
PFN pfn;
VAddr addr;
@ -990,7 +1001,6 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type,
if (pfn) // MapViewOfFileEx specific
{
ConvertVProtectToMapViewProtection(&perms);
FileOffsetLow = ROUND_DOWN(pfn << PAGE_SHIFT, m_AllocationGranularity);
Size = (pfn << PAGE_SHIFT) + Size - FileOffsetLow;
}
@ -1017,7 +1027,7 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type,
size_t vma_end = it->first + it->second.size;
addr = (this->*MappingRoutine)(addr, Size, vma_end, perms, FileOffsetLow, pfn);
addr = (this->*MappingRoutine)(addr, Size, vma_end, FileOffsetLow, pfn);
if (addr) { return addr; }
@ -1027,7 +1037,7 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type,
// If we are here, it means we reached the end of the memory region. In desperation, we also try to map it from the
// LastFree iterator and going backwards, since there could be holes created by deallocation operations...
auto begin_it = m_MemoryRegionArray[Type].RegionMap.begin();
VMAIter begin_it = m_MemoryRegionArray[Type].RegionMap.begin();
if (m_MemoryRegionArray[Type].LastFree == begin_it)
{
@ -1052,7 +1062,7 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type,
size_t vma_end = it->first + it->second.size;
addr = (this->*MappingRoutine)(addr, Size, vma_end, perms, FileOffsetLow, pfn);
addr = (this->*MappingRoutine)(addr, Size, vma_end, FileOffsetLow, pfn);
if (addr) { return addr; }
}
@ -1069,12 +1079,12 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type,
return NULL;
}
VAddr VMManager::MapBlockWithMapViewOfFileEx(VAddr StartingAddr, size_t ViewSize, size_t VmaEnd, DWORD Perms,
DWORD OffsetLow, PFN pfn)
VAddr VMManager::MapBlockWithMapViewOfFileEx(VAddr StartingAddr, size_t ViewSize, size_t VmaEnd, DWORD OffsetLow, PFN pfn)
{
for (; StartingAddr + ViewSize - 1 < VmaEnd; StartingAddr += m_AllocationGranularity)
{
if ((VAddr)MapViewOfFileEx(m_hContiguousFile, Perms, 0, OffsetLow, ViewSize, (void*)StartingAddr) == StartingAddr)
if ((VAddr)MapViewOfFileEx(m_hContiguousFile, FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, 0, OffsetLow,
ViewSize, (void*)StartingAddr) == StartingAddr)
{
return StartingAddr + (pfn << PAGE_SHIFT) - OffsetLow; // sum the offset into the file view
}
@ -1082,12 +1092,11 @@ VAddr VMManager::MapBlockWithMapViewOfFileEx(VAddr StartingAddr, size_t ViewSize
return NULL;
}
VAddr VMManager::MapBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Perms,
DWORD Unused, PFN Unused2)
VAddr VMManager::MapBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Unused, PFN Unused2)
{
for (; StartingAddr + Size - 1 < VmaEnd; StartingAddr += m_AllocationGranularity)
{
if ((VAddr)VirtualAlloc((void*)StartingAddr, Size, XBOX_MEM_RESERVE | XBOX_MEM_COMMIT, Perms) == StartingAddr)
if ((VAddr)VirtualAlloc((void*)StartingAddr, Size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE) == StartingAddr)
{
return StartingAddr;
}
@ -1095,12 +1104,11 @@ VAddr VMManager::MapBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_
return NULL;
}
VAddr VMManager::ReserveBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Unused,
DWORD Unused2, PFN Unused3)
VAddr VMManager::ReserveBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Unused, PFN Unused2)
{
for (; StartingAddr + Size - 1 < VmaEnd; StartingAddr += m_AllocationGranularity)
{
if ((VAddr)VirtualAlloc((void*)StartingAddr, Size, XBOX_MEM_RESERVE, XBOX_PAGE_NOACCESS) == StartingAddr)
if ((VAddr)VirtualAlloc((void*)StartingAddr, Size, MEM_RESERVE, PAGE_NOACCESS) == StartingAddr)
{
return StartingAddr;
}
@ -1108,19 +1116,6 @@ VAddr VMManager::ReserveBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, s
return NULL;
}
void VMManager::ConvertVProtectToMapViewProtection(DWORD* perms)
{
// We always grant read / write access to the file view, we just check if we should also grant execute rights
if (*perms & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE))
{
*perms = FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE;
return;
}
*perms = FILE_MAP_READ | FILE_MAP_WRITE;
}
void VMManager::ReprotectVMARange(VAddr target, size_t size, DWORD new_perms)
{
VAddr aligned_start = target & ~(UINT_PTR)PAGE_MASK;
@ -1282,22 +1277,19 @@ VMAIter VMManager::MergeAdjacentVMA(VMAIter vma_handle, MemoryRegionType Type)
return vma_handle;
}
VMAIter VMManager::ReprotectVMA(VMAIter vma_handle, DWORD new_perms)
void VMManager::UpdateMemoryPermissions(VAddr addr, size_t Size, DWORD Perms)
{
// PAGE_WRITECOMBINE/PAGE_NOCACHE are not allowed for shared memory, unless SEC_WRITECOMBINE/SEC_NOCACHE flag
// was specified when calling the CreateFileMapping function. Considering that Cxbx doesn't emulate the caches,
// it's probably safe to ignore these flags
VirtualMemoryArea& vma = vma_handle->second;
DWORD dummy;
vma.permissions = new_perms;
if (!VirtualProtect((void*)vma.base, vma.size, vma.permissions & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE), &dummy))
{
CxbxKrnlCleanup("ReprotectVMA: VirtualProtect could not protect the vma! The error code was %d", GetLastError());
}
UpdatePageTableForVMA(vma);
DWORD WindowsPerms = ConvertXboxToWinProtection(PatchXboxPermissions(Perms));
return MergeAdjacentVMA(vma_handle);
DWORD dummy;
if (!VirtualProtect((void*)addr, Size, WindowsPerms & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE), &dummy))
{
DbgPrintf("VirtualProtect failed. The error code was %d\n", GetLastError());
}
}
VMAIter VMManager::CheckExistenceVMA(VAddr addr, MemoryRegionType Type, size_t Size)

View File

@ -161,7 +161,7 @@ class VMManager : public PhysicalMemory
private:
// typedef of pointer to a member function mapping a memory block
typedef VAddr (VMManager::*MappingFn) (VAddr, size_t, size_t, DWORD, DWORD, PFN);
typedef VAddr (VMManager::*MappingFn) (VAddr, size_t, size_t, DWORD, PFN);
// an array of structs used to track the free/allocated vma's in the various memory regions
MemoryRegion m_MemoryRegionArray[MemoryRegionType::COUNT];
// handle of the contiguous file mapping
@ -188,19 +188,17 @@ class VMManager : public PhysicalMemory
// clear all memory region structs
void DestroyMemoryRegions();
// map a memory block with the supplied allocation routine
VAddr MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, PFN_COUNT PteNumber, DWORD perms, PFN pfn);
VAddr MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, PFN_COUNT PteNumber, PFN pfn);
// helper function which maps a block with VirtualAlloc
VAddr MapBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Perms, DWORD Unused, PFN Unused2);
VAddr MapBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Unused, PFN Unused2);
// helper function which reserves a block of virtual memory with VirtualAlloc
VAddr ReserveBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Unused, DWORD Unused2, PFN Unused3);
VAddr ReserveBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Unused, PFN Unused2);
// helper function which maps a block with MapViewOfFileEx
VAddr MapBlockWithMapViewOfFileEx(VAddr StartingAddr, size_t ViewSize, size_t VmaEnd, DWORD Perms, DWORD OffsetLow, PFN pfn);
VAddr MapBlockWithMapViewOfFileEx(VAddr StartingAddr, size_t ViewSize, size_t VmaEnd, DWORD OffsetLow, PFN pfn);
// destruct a vma
void DestructVMA(VMAIter it, MemoryRegionType Type);
// check if a vma exists at the supplied address. Also checks its size if specified
VMAIter CheckExistenceVMA(VAddr addr, MemoryRegionType Type, size_t Size = 0);
// convert VirtualProtect protection flags to file mapping protection flags
void ConvertVProtectToMapViewProtection(DWORD* perms);
// changes access permissions for a range of vma's, splitting them if necessary
void ReprotectVMARange(VAddr target, size_t size, DWORD new_perms);
// checks if a VAddr is valid; returns false if not
@ -219,8 +217,8 @@ class VMManager : public PhysicalMemory
VMAIter SplitVMA(VMAIter vma_handle, u32 offset_in_vma, MemoryRegionType Type);
// merges the specified vma with adjacent ones if possible
VMAIter MergeAdjacentVMA(VMAIter vma_handle, MemoryRegionType Type);
// changes access permissions for a vma
VMAIter ReprotectVMA(VMAIter vma_handle, DWORD new_perms);
// changes the access permissions of a block of memory
void UpdateMemoryPermissions(VAddr addr, size_t Size, DWORD Perms);
// acquires the critical section
void Lock();
// releases the critical section