Parcourir la source

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.
Michael Scire il y a 7 ans
Parent
commit
072a9796f5
2 fichiers modifiés avec 34 ajouts et 7 suppressions
  1. 8 1
      src/core/hle/kernel/svc.cpp
  2. 26 6
      src/core/hle/service/ldr/ldr.cpp

+ 8 - 1
src/core/hle/kernel/svc.cpp

@@ -318,7 +318,14 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
         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

+ 26 - 6
src/core/hle/service/ldr/ldr.cpp

@@ -345,14 +345,16 @@ public:
             vm_manager
                 .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode)
                 .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) {
             ASSERT(vm_manager
                        .MirrorMemory(*map_address + nro_size, bss_address, bss_size,
                                      Kernel::MemoryState::ModuleCode)
                        .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,
@@ -364,7 +366,8 @@ public:
 
         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};
         rb.Push(RESULT_SUCCESS);
@@ -409,9 +412,23 @@ public:
         }
 
         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();
 
@@ -473,7 +490,10 @@ private:
 
     struct NROInfo {
         SHA256Hash hash;
-        u64 size;
+        VAddr nro_address;
+        u64 nro_size;
+        VAddr bss_address;
+        u64 bss_size;
     };
 
     bool initialized = false;