From 3edc4a3055a6e056f2ebc02c8f57262dd9f762b7 Mon Sep 17 00:00:00 2001 From: Pengfei Zhu Date: Sat, 28 Mar 2020 20:26:54 +0800 Subject: [PATCH] service/ldr_ro: Fix CRO loading when the buffer contained multiple VM areas (#5125) * vm_manager: Handle multiple areas in ChangeMemoryState It is possible that a few areas have the same permisson and state, but with different backing pointers. Currently, this function assumes that only one continous area is found, but this is not always the case. * service/ldr_ro: Handle multiple areas in VerifyBufferState It is possible that the buffer passed from the game is made up of multiple areas with the same permisson and state but different backing pointers. Change the check to allow that. --- src/core/hle/kernel/vm_manager.cpp | 15 +++++++++------ src/core/hle/service/ldr_ro/ldr_ro.cpp | 15 +++++++++++---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 3280f99e9..ba2e2bd1a 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -151,13 +151,16 @@ ResultCode VMManager::ChangeMemoryState(VAddr target, u32 size, MemoryState expe } CASCADE_RESULT(auto vma, CarveVMARange(target, size)); - ASSERT(vma->second.size == size); - vma->second.permissions = new_perms; - vma->second.meminfo_state = new_state; - UpdatePageTableForVMA(vma->second); - - MergeAdjacent(vma); + const VMAIter end = vma_map.end(); + // The comparison against the end of the range must be done using addresses since VMAs can be + // merged during this process, causing invalidation of the iterators. + while (vma != end && vma->second.base < target_end) { + vma->second.permissions = new_perms; + vma->second.meminfo_state = new_state; + UpdatePageTableForVMA(vma->second); + vma = std::next(MergeAdjacent(vma)); + } return RESULT_SUCCESS; } diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index 274d36ed5..a435d5e58 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -41,10 +41,17 @@ static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D static bool VerifyBufferState(Kernel::Process& process, VAddr buffer_ptr, u32 size) { auto vma = process.vm_manager.FindVMA(buffer_ptr); - return vma != process.vm_manager.vma_map.end() && - vma->second.base + vma->second.size >= buffer_ptr + size && - vma->second.permissions == Kernel::VMAPermission::ReadWrite && - vma->second.meminfo_state == Kernel::MemoryState::Private; + while (vma != process.vm_manager.vma_map.end()) { + if (vma->second.permissions != Kernel::VMAPermission::ReadWrite || + vma->second.meminfo_state != Kernel::MemoryState::Private) { + return false; + } + if (vma->second.base + vma->second.size >= buffer_ptr + size) { + return true; + } + vma = std::next(vma); + } + return false; } void RO::Initialize(Kernel::HLERequestContext& ctx) {