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.
This commit is contained in:
Pengfei Zhu 2020-03-28 20:26:54 +08:00 committed by GitHub
parent 475b0fb159
commit 3edc4a3055
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 10 deletions

View file

@ -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);
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);
MergeAdjacent(vma);
vma = std::next(MergeAdjacent(vma));
}
return RESULT_SUCCESS;
}

View file

@ -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) {