diff options
| author | Lioncash <mathew1800@gmail.com> | 2019-04-12 00:38:54 -0400 | 
|---|---|---|
| committer | Lioncash <mathew1800@gmail.com> | 2019-04-12 21:56:03 -0400 | 
| commit | 4d293bb5cbb65d3551dd0d22e92b7065a7ebba14 (patch) | |
| tree | d57782959b51296eef132a721a1b6d0600e8e117 /src/core/hle | |
| parent | 76a2465655be493492446b4ef686f4b59b2e3969 (diff) | |
kernel/svc: Implement svcUnmapProcessCodeMemory
Essentially performs the inverse of svcMapProcessCodeMemory. This unmaps
the aliasing region first, then restores the general traits of the
aliased memory.
What this entails, is:
- Restoring Read/Write permissions to the VMA.
- Restoring its memory state to reflect it as a general heap memory region.
- Clearing the memory attributes on the region.
Diffstat (limited to 'src/core/hle')
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 70 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.cpp | 51 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.h | 23 | 
3 files changed, 143 insertions, 1 deletions
| diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 0aa2e358e..d48a2203a 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1257,6 +1257,74 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand      return vm_manager.MapCodeMemory(dst_address, src_address, size);  } +ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, +                                  u64 src_address, u64 size) { +    LOG_DEBUG(Kernel_SVC, +              "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " +              "size=0x{:016X}", +              process_handle, dst_address, src_address, size); + +    if (!Common::Is4KBAligned(dst_address)) { +        LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", +                  dst_address); +        return ERR_INVALID_ADDRESS; +    } + +    if (!Common::Is4KBAligned(src_address)) { +        LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", +                  src_address); +        return ERR_INVALID_ADDRESS; +    } + +    if (size == 0 || Common::Is4KBAligned(size)) { +        LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); +        return ERR_INVALID_SIZE; +    } + +    if (!IsValidAddressRange(dst_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Destination address range overflows the address space (dst_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  dst_address, size); +        return ERR_INVALID_ADDRESS_STATE; +    } + +    if (!IsValidAddressRange(src_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Source address range overflows the address space (src_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  src_address, size); +        return ERR_INVALID_ADDRESS_STATE; +    } + +    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); +    auto process = handle_table.Get<Process>(process_handle); +    if (!process) { +        LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", +                  process_handle); +        return ERR_INVALID_HANDLE; +    } + +    auto& vm_manager = process->VMManager(); +    if (!vm_manager.IsWithinAddressSpace(src_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Source address range is not within the address space (src_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  src_address, size); +        return ERR_INVALID_ADDRESS_STATE; +    } + +    if (!vm_manager.IsWithinASLRRegion(dst_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  dst_address, size); +        return ERR_INVALID_MEMORY_RANGE; +    } + +    return vm_manager.UnmapCodeMemory(dst_address, src_address, size); +} +  /// Exits the current process  static void ExitProcess(Core::System& system) {      auto* current_process = system.Kernel().CurrentProcess(); @@ -2286,7 +2354,7 @@ static const FunctionDef SVC_Table[] = {      {0x75, nullptr, "UnmapProcessMemory"},      {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"},      {0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"}, -    {0x78, nullptr, "UnmapProcessCodeMemory"}, +    {0x78, SvcWrap<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},      {0x79, nullptr, "CreateProcess"},      {0x7A, nullptr, "StartProcess"},      {0x7B, nullptr, "TerminateProcess"}, diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 76b491c47..f0c0c12fc 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -331,6 +331,57 @@ ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 si      return ReprotectRange(dst_address, size, VMAPermission::Read);  } +ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) { +    constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped; +    const auto src_check_result = CheckRangeState( +        src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::None, +        VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, ignore_attribute); + +    if (src_check_result.Failed()) { +        return src_check_result.Code(); +    } + +    // Yes, the kernel only checks the first page of the region. +    const auto dst_check_result = +        CheckRangeState(dst_address, Memory::PAGE_SIZE, MemoryState::FlagModule, +                        MemoryState::FlagModule, VMAPermission::None, VMAPermission::None, +                        MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); + +    if (dst_check_result.Failed()) { +        return dst_check_result.Code(); +    } + +    const auto dst_memory_state = std::get<MemoryState>(*dst_check_result); +    const auto dst_contiguous_check_result = CheckRangeState( +        dst_address, size, MemoryState::All, dst_memory_state, VMAPermission::None, +        VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); + +    if (dst_contiguous_check_result.Failed()) { +        return dst_contiguous_check_result.Code(); +    } + +    const auto unmap_result = UnmapRange(dst_address, size); +    if (unmap_result.IsError()) { +        return unmap_result; +    } + +    // With the mirrored portion unmapped, restore the original region's traits. +    const auto src_vma_result = CarveVMARange(src_address, size); +    if (src_vma_result.Failed()) { +        return src_vma_result.Code(); +    } +    auto src_vma_iter = *src_vma_result; +    src_vma_iter->second.state = MemoryState::Heap; +    src_vma_iter->second.attribute = MemoryAttribute::None; +    Reprotect(src_vma_iter, VMAPermission::ReadWrite); + +    if (dst_memory_state == MemoryState::ModuleCode) { +        Core::System::GetInstance().InvalidateCpuInstructionCaches(); +    } + +    return unmap_result; +} +  MemoryInfo VMManager::QueryMemory(VAddr address) const {      const auto vma = FindVMA(address);      MemoryInfo memory_info{}; diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 27120d3b1..288eb9450 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -441,6 +441,29 @@ public:      ///      ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); +    /// Unmaps a region of memory designated as code module memory. +    /// +    /// @param dst_address The base address of the memory region aliasing the source memory region. +    /// @param src_address The base address of the memory region being aliased. +    /// @param size        The size of the memory region to unmap in bytes. +    /// +    /// @pre Both memory ranges lie within the actual addressable address space. +    /// +    /// @pre The memory region being unmapped has been previously been mapped +    ///      by a call to MapCodeMemory. +    /// +    /// @post After execution of the function, if successful. the aliasing memory region +    ///       will be unmapped and the aliased region will have various traits about it +    ///       restored to what they were prior to the original mapping call preceding +    ///       this function call. +    ///       <p> +    ///       What this also entails is as follows: +    ///           1. The state of the memory region will now indicate a general heap region. +    ///           2. All memory attributes for the memory region are cleared. +    ///           3. Memory permissions for the region are restored to user read/write. +    /// +    ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); +      /// Queries the memory manager for information about the given address.      ///      /// @param address The address to query the memory manager about for information. | 
