From 4d293bb5cbb65d3551dd0d22e92b7065a7ebba14 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 12 Apr 2019 00:38:54 -0400 Subject: [PATCH] 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. --- src/core/hle/kernel/svc.cpp | 70 +++++++++++++++++++++++++++++- src/core/hle/kernel/vm_manager.cpp | 51 ++++++++++++++++++++++ src/core/hle/kernel/vm_manager.h | 23 ++++++++++ 3 files changed, 143 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 0aa2e358e0..d48a2203a1 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_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"}, {0x77, SvcWrap, "MapProcessCodeMemory"}, - {0x78, nullptr, "UnmapProcessCodeMemory"}, + {0x78, SvcWrap, "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 76b491c47d..f0c0c12fce 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(*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 27120d3b15..288eb9450e 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. + ///

+ /// 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.