Restore memory perms on svcUnmapMemory/UnloadNro

Prior to PR, Yuzu did not restore memory to RW-
on unmap of mirrored memory or unloading of NRO.

(In fact, in the NRO case, the memory was unmapped
instead of reprotected to --- on Load, so it was
actually lost entirely...)

This PR addresses that, and restores memory to RW-
as it should.

This fixes a crash in Super Smash Bros when creating
a World of Light save for the first time, and possibly
other games/circumstances.
This commit is contained in:
Michael Scire 2019-07-11 01:38:28 -07:00
parent 0b3901bdd0
commit 072a9796f5
2 changed files with 34 additions and 7 deletions

View file

@ -318,7 +318,14 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
return result; return result;
} }
return vm_manager.UnmapRange(dst_addr, size); const auto unmap_res = vm_manager.UnmapRange(dst_addr, size);
// Reprotect the source mapping on success
if (unmap_res.IsSuccess()) {
ASSERT(vm_manager.ReprotectRange(src_addr, size, VMAPermission::ReadWrite).IsSuccess());
}
return unmap_res;
} }
/// Connect to an OS service given the port name, returns the handle to the port to out /// Connect to an OS service given the port name, returns the handle to the port to out

View file

@ -345,14 +345,16 @@ public:
vm_manager vm_manager
.MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode) .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode)
.IsSuccess()); .IsSuccess());
ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None)
.IsSuccess());
if (bss_size > 0) { if (bss_size > 0) {
ASSERT(vm_manager ASSERT(vm_manager
.MirrorMemory(*map_address + nro_size, bss_address, bss_size, .MirrorMemory(*map_address + nro_size, bss_address, bss_size,
Kernel::MemoryState::ModuleCode) Kernel::MemoryState::ModuleCode)
.IsSuccess()); .IsSuccess());
ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess()); ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None)
.IsSuccess());
} }
vm_manager.ReprotectRange(*map_address, header.text_size, vm_manager.ReprotectRange(*map_address, header.text_size,
@ -364,7 +366,8 @@ public:
Core::System::GetInstance().InvalidateCpuInstructionCaches(); Core::System::GetInstance().InvalidateCpuInstructionCaches();
nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size}); nro.insert_or_assign(*map_address,
NROInfo{hash, nro_address, nro_size, bss_address, bss_size});
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
@ -409,9 +412,23 @@ public:
} }
auto& vm_manager = Core::CurrentProcess()->VMManager(); auto& vm_manager = Core::CurrentProcess()->VMManager();
const auto& nro_size = iter->second.size; const auto& nro_info = iter->second;
ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); // Unmap the mirrored memory
ASSERT(
vm_manager.UnmapRange(nro_address, nro_info.nro_size + nro_info.bss_size).IsSuccess());
// Reprotect the source memory
ASSERT(vm_manager
.ReprotectRange(nro_info.nro_address, nro_info.nro_size,
Kernel::VMAPermission::ReadWrite)
.IsSuccess());
if (nro_info.bss_size > 0) {
ASSERT(vm_manager
.ReprotectRange(nro_info.bss_address, nro_info.bss_size,
Kernel::VMAPermission::ReadWrite)
.IsSuccess());
}
Core::System::GetInstance().InvalidateCpuInstructionCaches(); Core::System::GetInstance().InvalidateCpuInstructionCaches();
@ -473,7 +490,10 @@ private:
struct NROInfo { struct NROInfo {
SHA256Hash hash; SHA256Hash hash;
u64 size; VAddr nro_address;
u64 nro_size;
VAddr bss_address;
u64 bss_size;
}; };
bool initialized = false; bool initialized = false;