diff --git a/src/CxbxKrnl/EmuKrnlMm.cpp b/src/CxbxKrnl/EmuKrnlMm.cpp index 652b45f2f..479ea2aa9 100644 --- a/src/CxbxKrnl/EmuKrnlMm.cpp +++ b/src/CxbxKrnl/EmuKrnlMm.cpp @@ -444,9 +444,9 @@ XBSYSAPI EXPORTNUM(180) xboxkrnl::ULONG NTAPI xboxkrnl::MmQueryAllocationSize { LOG_FUNC_ONE_ARG(BaseAddress); - ULONG uiSize = g_VMManager.QuerySize((VAddr)BaseAddress); + ULONG Size = g_VMManager.QuerySize((VAddr)BaseAddress); - RETURN(uiSize); + RETURN(Size); } // ****************************************************************** diff --git a/src/CxbxKrnl/PhysicalMemory.cpp b/src/CxbxKrnl/PhysicalMemory.cpp index 649722f02..8ad0723dc 100644 --- a/src/CxbxKrnl/PhysicalMemory.cpp +++ b/src/CxbxKrnl/PhysicalMemory.cpp @@ -449,6 +449,9 @@ bool PhysicalMemory::ConvertXboxToSystemPteProtection(DWORD perms, PMMPTE pPte) DWORD PhysicalMemory::ConvertPteToXboxProtection(ULONG PteMask) { + // This routine assumes that the pte has valid protection bits. If it doesn't, it can produce invalid + // access permissions + ULONG Protect; if (PteMask & PTE_READWRITE) { Protect = XBOX_PAGE_READWRITE; } diff --git a/src/CxbxKrnl/VMManager.cpp b/src/CxbxKrnl/VMManager.cpp index 62c8cc647..8614860f2 100644 --- a/src/CxbxKrnl/VMManager.cpp +++ b/src/CxbxKrnl/VMManager.cpp @@ -491,6 +491,11 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz MemoryType = MemoryRegionType::Devkit; } + // ergo720: if a guard page is requested, because we only commit the actual stack pages, there is the remote possibility + // that the stack is mapped immediately after the end of a host allocation, since those will appear as free areas and + // the VMManager can't know anything about those. In practice, I wouldn't worry about this case since, if emulation is + // done properly, a game will never try to write beyond the stack bottom since that would imply a stack overflow + if (RemoveFree(PagesNumber, &pfn, 0, LowestAcceptablePfn, m_HighestPage)) // MapViewOfFileEx path { MappingRoutine = &MapBlockWithMapViewOfFileEx; @@ -1056,46 +1061,47 @@ size_t VMManager::QuerySize(VAddr addr) { LOG_FUNC_ONE_ARG(addr); - // The Xbox returns at least PAGE_SIZE even for invalid addresses - size_t size = PAGE_SIZE; + PMMPTE PointerPte; + PFN_COUNT PagesNumber; + size_t Size = 0; Lock(); - auto it = m_Vma_map.lower_bound(addr); - if (it != m_Vma_map.end()) + + if (IS_USER_ADDRESS(addr)) { - if (it->second.type == VMAType::Free) + // This is designed to handle Cxbx callers that provide an offset instead of the beginning of the allocation. Such + // callers should only allocate the corresponding memory with the generic Allocate or AllocateZeroed functions, or + // else this will fail. At the moment this is indeed the case + + VMAIter it = GetVMAIterator(addr, MemoryRegionType::User); + + if (it != m_MemoryRegionArray[MemoryRegionType::User].RegionMap.end() && it->second.type != VMAType::Free) { - size = 0; - EmuWarning("VMManager: QuerySize : queried a free region!\n"); - } - else - { - if (it->second.base != addr) - { - // This shouldn't happen for MmQueryAllocationSize, but if this function is called by other callers then it's possible - auto prev_it = std::prev(it); - PAddr prev_backing_block = prev_it->second.backing_block; - while (prev_it != m_Vma_map.begin() && prev_backing_block == prev_it->second.backing_block) - { - --prev_it; - } - it = std::next(prev_it); - EmuWarning("VMManager: QuerySize : quering not the start address of an allocation\n"); - } - // We can't just return the size of the vma because it could have been split by ReprotectVMARange so, instead, - // we must check the corresponding physical allocation size - size = it->second.size; - auto next_it = std::next(it); - while (next_it != m_Vma_map.end() && it->second.backing_block == next_it->second.backing_block) - { - size += next_it->second.size; - ++next_it; - } + Size = it->second.size; } } + else + { + // This will only work for allocations made by MmAllocateContiguousMemory(Ex), MmAllocateSystemMemory and + // MmCreateKernelStack which is what MmQueryAllocationSize expects. If they are not, this will either fault + // or return an incorrect size of at least PAGE_SIZE + + PagesNumber = 1; + PointerPte = GetPteAddress(addr); + + while (PointerPte->Hardware.GuardOrEnd == 0) + { + assert(PointerPte->Hardware.Valid != 0); // pte must be valid + + PagesNumber++; + PointerPte++; + } + Size = PagesNumber << PAGE_SHIFT; + } + Unlock(); - RETURN(size); + RETURN(Size); } xboxkrnl::NTSTATUS VMManager::XbAllocateVirtualMemory(VAddr* addr, ULONG zero_bits, size_t* size, DWORD allocation_type,