diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index e43a4884d..bb0a0e27e 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_process_code_memory_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_process_code_memory_api.hpp new file mode 100644 index 000000000..0789b3730 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_process_code_memory_api.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + struct ProcessMemoryRegion { + u64 address; + u64 size; + }; + + Result MapProcessCodeMemory(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions); + Result UnmapProcessCodeMemory(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions); + +} diff --git a/libraries/libstratosphere/source/os/impl/os_address_space_allocator.hpp b/libraries/libstratosphere/source/os/impl/os_address_space_allocator.hpp index f76031bf8..d3ff688b5 100644 --- a/libraries/libstratosphere/source/os/impl/os_address_space_allocator.hpp +++ b/libraries/libstratosphere/source/os/impl/os_address_space_allocator.hpp @@ -25,10 +25,13 @@ namespace ams::os::impl { AddressAllocationResult_OutOfSpace, }; - template + template class AddressSpaceAllocatorBase { NON_COPYABLE(AddressSpaceAllocatorBase); NON_MOVEABLE(AddressSpaceAllocatorBase); + public: + using AddressType = AddressType_; + using SizeType = SizeType_; private: static constexpr size_t MaxForbiddenRegions = 2; private: diff --git a/libraries/libstratosphere/source/os/impl/os_address_space_allocator_impl.os.horizon.hpp b/libraries/libstratosphere/source/os/impl/os_address_space_allocator_impl.os.horizon.hpp index 9ba15af43..d6981188c 100644 --- a/libraries/libstratosphere/source/os/impl/os_address_space_allocator_impl.os.horizon.hpp +++ b/libraries/libstratosphere/source/os/impl/os_address_space_allocator_impl.os.horizon.hpp @@ -24,7 +24,7 @@ namespace ams::os::impl { public: using Base::Base; public: - virtual bool CheckFreeSpace(uintptr_t address, size_t size) override { + virtual bool CheckFreeSpace(AddressType address, SizeType size) override { /* Query the memory. */ svc::MemoryInfo memory_info; svc::PageInfo page_info; diff --git a/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_types.hpp b/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_types.hpp index dc0517f6c..2caaa543a 100644 --- a/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_types.hpp +++ b/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_types.hpp @@ -38,17 +38,21 @@ namespace ams::os::impl { class AslrSpaceManagerTemplate { NON_COPYABLE(AslrSpaceManagerTemplate); NON_MOVEABLE(AslrSpaceManagerTemplate); + private: + using AddressType = typename Allocator::AddressType; + using SizeType = typename Allocator::SizeType; private: Impl m_impl; Allocator m_allocator; public: - AslrSpaceManagerTemplate() : m_impl(), m_allocator(m_impl.GetAslrSpaceBeginAddress(), m_impl.GetAslrSpaceEndAddress(), AslrSpaceGuardSize, m_impl.GetForbiddenRegions(), m_impl.GetForbiddenRegionCount()) { + template + AslrSpaceManagerTemplate(Args &&... args) : m_impl(), m_allocator(m_impl.GetAslrSpaceBeginAddress(), m_impl.GetAslrSpaceEndAddress(), AslrSpaceGuardSize, m_impl.GetForbiddenRegions(), m_impl.GetForbiddenRegionCount(), std::forward(args)...) { /* ... */ } - uintptr_t AllocateSpace(size_t size, size_t align_offset) { + AddressType AllocateSpace(SizeType size, SizeType align_offset) { /* Try to allocate a large-aligned space, if we can. */ - if (align_offset || size >= AslrSpaceLargeAlign) { + if (align_offset || (size / AslrSpaceLargeAlign) != 0) { if (auto large_align = m_allocator.AllocateSpace(size, AslrSpaceLargeAlign, align_offset & (AslrSpaceLargeAlign - 1)); large_align != 0) { return large_align; } @@ -58,14 +62,14 @@ namespace ams::os::impl { return m_allocator.AllocateSpace(size, MemoryPageSize, 0); } - bool CheckGuardSpace(uintptr_t address, size_t size) { + bool CheckGuardSpace(AddressType address, SizeType size) { return m_allocator.CheckGuardSpace(address, size, AslrSpaceGuardSize); } template - Result MapAtRandomAddress(uintptr_t *out, MapFunction map_function, UnmapFunction unmap_function, size_t size, size_t align_offset) { + Result MapAtRandomAddress(AddressType *out, MapFunction map_function, UnmapFunction unmap_function, SizeType size, SizeType align_offset) { /* Try to map up to 64 times. */ - for (int i = 0; i < 64; ++i) { + for (auto i = 0; i < 64; ++i) { /* Reserve space to map the memory. */ const uintptr_t map_address = this->AllocateSpace(size, align_offset); if (map_address == 0) { @@ -82,6 +86,9 @@ namespace ams::os::impl { if (!this->CheckGuardSpace(map_address, size)) { /* We don't have guard space, so unmap. */ unmap_function(map_address, size); + + /* NOTE: Nintendo is missing this continue; this is almost certainly a bug. */ + /* This will cause them to incorrectly return success after unmapping if guard space is not present. */ continue; } diff --git a/stratosphere/ro/source/impl/ro_auto_close.hpp b/libraries/libstratosphere/source/os/impl/os_process_code_memory_impl.hpp similarity index 66% rename from stratosphere/ro/source/impl/ro_auto_close.hpp rename to libraries/libstratosphere/source/os/impl/os_process_code_memory_impl.hpp index e81abca72..45fe4e213 100644 --- a/stratosphere/ro/source/impl/ro_auto_close.hpp +++ b/libraries/libstratosphere/source/os/impl/os_process_code_memory_impl.hpp @@ -16,6 +16,12 @@ #pragma once #include -namespace ams::ro::impl { +namespace ams::os::impl { -} + class ProcessCodeMemoryImpl { + public: + static Result Map(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions); + static Result Unmap(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions); + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/os/impl/os_process_code_memory_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_process_code_memory_impl.os.horizon.cpp new file mode 100644 index 000000000..5a25538e1 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_process_code_memory_impl.os.horizon.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "os_process_code_memory_impl.hpp" +#include "os_aslr_space_manager.hpp" + +namespace ams::os::impl { + + namespace { + + class ProcessAddressSpaceAllocator final : public AddressSpaceAllocatorBase { + private: + using Base = AddressSpaceAllocatorBase; + private: + NativeHandle m_handle; + public: + ProcessAddressSpaceAllocator(u64 start_address, u64 end_address, SizeType guard_size, const AddressSpaceAllocatorForbiddenRegion *forbidden_regions, size_t num_forbidden_regions, NativeHandle handle) : Base(start_address, end_address, guard_size, forbidden_regions, num_forbidden_regions), m_handle(handle) { + /* ... */ + } + public: + virtual bool CheckFreeSpace(AddressType address, SizeType size) override { + /* Query the memory. */ + svc::MemoryInfo memory_info; + svc::PageInfo page_info; + R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(memory_info), std::addressof(page_info), m_handle, address)); + + return memory_info.state == svc::MemoryState_Free && address + size <= memory_info.base_address + memory_info.size; + } + }; + + using ProcessAslrSpaceManager = AslrSpaceManagerTemplate; + + size_t GetTotalProcessMemoryRegionSize(const ProcessMemoryRegion *regions, size_t num_regions) { + size_t total = 0; + + for (size_t i = 0; i < num_regions; ++i) { + total += regions[i].size; + } + + return total; + } + + } + + Result ProcessCodeMemoryImpl::Map(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions) { + /* Get the total process memory region size. */ + const size_t total_size = GetTotalProcessMemoryRegionSize(regions, num_regions); + + /* Create an aslr space manager for the process. */ + auto process_aslr_space_manager = ProcessAslrSpaceManager(handle); + + /* Map at a random address. */ + u64 mapped_address; + R_TRY(process_aslr_space_manager.MapAtRandomAddress(std::addressof(mapped_address), + [handle, regions, num_regions](u64 map_address, u64 map_size) -> Result { + AMS_UNUSED(map_size); + + /* Map the regions in order. */ + u64 mapped_size = 0; + for (size_t i = 0; i < num_regions; ++i) { + /* If we fail, unmap up to where we've mapped. */ + ON_RESULT_FAILURE { R_ABORT_UNLESS(ProcessCodeMemoryImpl::Unmap(handle, map_address, regions, i)); }; + + /* Map the current region. */ + R_TRY_CATCH(svc::MapProcessCodeMemory(handle, map_address + mapped_size, regions[i].address, regions[i].size)) { + R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource()) + R_CATCH(svc::ResultInvalidCurrentMemory) { + /* Check if the process memory is invalid. */ + const u64 last_address = regions[i].address + regions[i].size - 1; + u64 cur_address = regions[i].address; + while (true) { + svc::MemoryInfo memory_info; + svc::PageInfo page_info; + R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(memory_info), std::addressof(page_info), handle, cur_address)); + + R_UNLESS(memory_info.state == svc::MemoryState_Normal, os::ResultInvalidProcessMemory()); + R_UNLESS(memory_info.permission == svc::MemoryPermission_ReadWrite, os::ResultInvalidProcessMemory()); + R_UNLESS(memory_info.attribute == static_cast(0), os::ResultInvalidProcessMemory()); + + cur_address = memory_info.base_address + memory_info.size; + if (cur_address > last_address) { + break; + } + } + + R_THROW(os::ResultInvalidCurrentMemoryState()); + } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + } + + R_SUCCEED(); + }, + [handle, regions, num_regions](u64 map_address, u64 map_size) -> void { + AMS_UNUSED(map_size); + R_ABORT_UNLESS(ProcessCodeMemoryImpl::Unmap(handle, map_address, regions, num_regions)); + }, + total_size, + regions[0].address /* NOTE: This seems like a Nintendo bug, if the caller passed no regions. */ + )); + + /* Set the output address. */ + *out = mapped_address; + R_SUCCEED(); + } + + Result ProcessCodeMemoryImpl::Unmap(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions) { + /* Get the total process memory region size. */ + const size_t total_size = GetTotalProcessMemoryRegionSize(regions, num_regions); + + /* Unmap each region in order. */ + size_t cur_offset = total_size; + for (size_t i = 0; i < num_regions; ++i) { + /* We want to unmap in reverse order. */ + const auto &cur_region = regions[num_regions - 1 - i]; + + /* Subtract to update the current offset. */ + cur_offset -= cur_region.size; + + /* Unmap. */ + R_TRY_CATCH(svc::UnmapProcessCodeMemory(handle, process_code_address + cur_offset, cur_region.address, cur_region.size)) { + R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource()) + R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState()) + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + } + + R_SUCCEED(); + } + +} diff --git a/libraries/libstratosphere/source/os/os_process_code_memory.cpp b/libraries/libstratosphere/source/os/os_process_code_memory.cpp new file mode 100644 index 000000000..ccd24d158 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_process_code_memory.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/os_process_code_memory_impl.hpp" + +namespace ams::os { + + Result MapProcessCodeMemory(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions) { + R_RETURN(::ams::os::impl::ProcessCodeMemoryImpl::Map(out, handle, regions, num_regions)); + } + + Result UnmapProcessCodeMemory(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions) { + R_RETURN(::ams::os::impl::ProcessCodeMemoryImpl::Unmap(handle, process_code_address, regions, num_regions)); + } + +} diff --git a/libraries/libvapours/include/vapours/results/os_results.hpp b/libraries/libvapours/include/vapours/results/os_results.hpp index b83389e84..680a12db2 100644 --- a/libraries/libvapours/include/vapours/results/os_results.hpp +++ b/libraries/libvapours/include/vapours/results/os_results.hpp @@ -41,6 +41,7 @@ namespace ams::os { R_DEFINE_ERROR_RESULT(SessionClosedForReceive, 510); R_DEFINE_ERROR_RESULT(SessionClosedForReply, 511); R_DEFINE_ERROR_RESULT(ReceiveListBroken, 512); + R_DEFINE_ERROR_RESULT(InvalidProcessMemory, 513); R_DEFINE_ERROR_RESULT(NotImplemented, 1000); R_DEFINE_ERROR_RESULT(NotSupported, 1001); diff --git a/stratosphere/ro/source/impl/ro_map_utils.cpp b/stratosphere/ro/source/impl/ro_map_utils.cpp deleted file mode 100644 index e5c525a02..000000000 --- a/stratosphere/ro/source/impl/ro_map_utils.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "ro_map_utils.hpp" - -namespace ams::ro::impl { - - namespace { - - - ALWAYS_INLINE u64 GetCurrentProcessInfo(svc::InfoType info_type) { - u64 value; - R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), info_type, svc::PseudoHandle::CurrentProcess, 0)); - return value; - } - - } - - Result SearchFreeRegion(uintptr_t *out, size_t mapping_size) { - /* Get address space extents. */ - const uintptr_t heap_start = GetCurrentProcessInfo(svc::InfoType_HeapRegionAddress); - const size_t heap_size = GetCurrentProcessInfo(svc::InfoType_HeapRegionSize); - const uintptr_t alias_start = GetCurrentProcessInfo(svc::InfoType_AliasRegionAddress); - const size_t alias_size = GetCurrentProcessInfo(svc::InfoType_AliasRegionSize); - const uintptr_t aslr_start = GetCurrentProcessInfo(svc::InfoType_AslrRegionAddress); - const size_t aslr_size = GetCurrentProcessInfo(svc::InfoType_AslrRegionSize); - - /* Iterate upwards to find a free region. */ - uintptr_t address = aslr_start; - while (true) { - /* Declare variables for memory querying. */ - svc::MemoryInfo mem_info; - svc::PageInfo page_info; - - /* Check that we're still within bounds. */ - R_UNLESS(address < address + mapping_size, ro::ResultOutOfAddressSpace()); - - /* If we're within the heap region, skip to the end of the heap region. */ - if (heap_size != 0 && !(address + mapping_size - 1 < heap_start || heap_start + heap_size - 1 < address)) { - R_UNLESS(address < heap_start + heap_size, ro::ResultOutOfAddressSpace()); - address = heap_start + heap_size; - continue; - } - - /* If we're within the alias region, skip to the end of the alias region. */ - if (alias_size != 0 && !(address + mapping_size - 1 < alias_start || alias_start + alias_size - 1 < address)) { - R_UNLESS(address < alias_start + alias_size, ro::ResultOutOfAddressSpace()); - address = alias_start + alias_size; - continue; - } - - /* Get the current memory range. */ - R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)); - - /* If the memory range is free and big enough, use it. */ - if (mem_info.state == svc::MemoryState_Free && mapping_size <= ((mem_info.base_address + mem_info.size) - address)) { - *out = address; - R_SUCCEED(); - } - - /* Check that we can advance. */ - R_UNLESS(address < mem_info.base_address + mem_info.size, ro::ResultOutOfAddressSpace()); - R_UNLESS(mem_info.base_address + mem_info.size - 1 < aslr_start + aslr_size - 1, ro::ResultOutOfAddressSpace()); - - /* Advance. */ - address = mem_info.base_address + mem_info.size; - } - } - -} diff --git a/stratosphere/ro/source/impl/ro_map_utils.hpp b/stratosphere/ro/source/impl/ro_map_utils.hpp deleted file mode 100644 index 75cbe5337..000000000 --- a/stratosphere/ro/source/impl/ro_map_utils.hpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include - -namespace ams::ro::impl { - - constexpr inline auto RetrySearchCount = 512; - - Result SearchFreeRegion(uintptr_t *out, size_t mapping_size); - - class ProcessRegionInfo { - NON_COPYABLE(ProcessRegionInfo); - NON_MOVEABLE(ProcessRegionInfo); - private: - static constexpr size_t StackGuardSize = 4 * os::MemoryPageSize; - private: - u64 m_heap_start; - u64 m_heap_size; - u64 m_alias_start; - u64 m_alias_size; - u64 m_aslr_start; - u64 m_aslr_size; - public: - ProcessRegionInfo(os::NativeHandle process) { - R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_heap_start), svc::InfoType_HeapRegionAddress, process, 0)); - R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_heap_size), svc::InfoType_HeapRegionSize, process, 0)); - R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_alias_start), svc::InfoType_AliasRegionAddress, process, 0)); - R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_alias_size), svc::InfoType_AliasRegionSize, process, 0)); - R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_aslr_start), svc::InfoType_AslrRegionAddress, process, 0)); - R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_aslr_size), svc::InfoType_AslrRegionSize, process, 0)); - } - - u64 GetAslrRegion(u64 mapping_size) const { - /* If we can, look for a region. */ - if (mapping_size <= m_aslr_size) { - for (auto i = 0; i < RetrySearchCount; ++i) { - /* Get a random address. */ - const u64 address = m_aslr_start + os::GenerateRandomU64((m_aslr_size - mapping_size) / os::MemoryPageSize) * os::MemoryPageSize; - - /* Check that it's not contained within heap. */ - if (m_heap_size != 0 && !(address + mapping_size - 1 < m_heap_start || m_heap_start + m_heap_size - 1 < address)) { - continue; - } - - /* Check that it's not contained within alias. */ - if (m_alias_size != 0 && !(address + mapping_size - 1 < m_alias_start || m_alias_start + m_alias_size - 1 < address)) { - continue; - } - - /* Return the address. */ - return address; - } - } - - /* We failed to find a region. */ - return 0; - } - - bool CanEmplaceGuardSpaces(os::NativeHandle process, u64 address, u64 size) { - /* NOTE: Nintendo does not check the results of these svc calls. */ - svc::MemoryInfo mem_info; - svc::PageInfo page_info; - - /* Check for guard availability before the region. */ - R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(mem_info), std::addressof(page_info), process, address - 1)); - if (!(mem_info.state == svc::MemoryState_Free && mem_info.base_address <= address - StackGuardSize)) { - return false; - } - - /* Check for guard availability after the region. */ - R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(mem_info), std::addressof(page_info), process, address + size)); - if (!(mem_info.state == svc::MemoryState_Free && address + size + StackGuardSize <= mem_info.base_address + mem_info.size)) { - return false; - } - - return true; - } - }; - - class AutoCloseMap { - private: - Result m_result; - uintptr_t m_map_address; - os::NativeHandle m_handle; - u64 m_address; - u64 m_size; - public: - AutoCloseMap(uintptr_t map, os::NativeHandle handle, u64 addr, u64 size) : m_map_address(map), m_handle(handle), m_address(addr), m_size(size) { - m_result = svc::MapProcessMemory(m_map_address, m_handle, m_address, m_size); - } - - ~AutoCloseMap() { - if (m_handle != os::InvalidNativeHandle && R_SUCCEEDED(m_result)) { - R_ABORT_UNLESS(svc::UnmapProcessMemory(m_map_address, m_handle, m_address, m_size)); - } - } - - Result GetResult() const { - return m_result; - } - - bool IsSuccess() const { - return R_SUCCEEDED(m_result); - } - - void Cancel() { - m_handle = os::InvalidNativeHandle; - } - }; - - class MappedCodeMemory { - NON_COPYABLE(MappedCodeMemory); - private: - os::NativeHandle m_handle; - Result m_result; - u64 m_dst_address; - u64 m_src_address; - u64 m_size; - public: - constexpr MappedCodeMemory() : m_handle(os::InvalidNativeHandle), m_result(ro::ResultInternalError()), m_dst_address(0), m_src_address(0), m_size(0) { - /* ... */ - } - - MappedCodeMemory(os::NativeHandle handle, u64 dst, u64 src, u64 size) : m_handle(handle), m_dst_address(dst), m_src_address(src), m_size(size) { - m_result = svc::MapProcessCodeMemory(m_handle, m_dst_address, m_src_address, m_size); - } - - ~MappedCodeMemory() { - if (m_handle != os::InvalidNativeHandle && R_SUCCEEDED(m_result) && m_size > 0) { - R_ABORT_UNLESS(svc::UnmapProcessCodeMemory(m_handle, m_dst_address, m_src_address, m_size)); - } - } - - MappedCodeMemory(MappedCodeMemory &&rhs) : m_handle(rhs.m_handle), m_result(rhs.m_result), m_dst_address(rhs.m_dst_address), m_src_address(rhs.m_src_address), m_size(rhs.m_size) { - rhs.m_handle = os::InvalidNativeHandle; - } - - MappedCodeMemory &operator=(MappedCodeMemory &&rhs) { - m_handle = rhs.m_handle; - m_result = rhs.m_result; - m_dst_address = rhs.m_dst_address; - m_src_address = rhs.m_src_address; - m_size = rhs.m_size; - - rhs.m_handle = os::InvalidNativeHandle; - - return *this; - } - - Result GetResult() const { - return m_result; - } - - bool IsSuccess() const { - return R_SUCCEEDED(m_result); - } - - void Cancel() { - m_handle = os::InvalidNativeHandle; - } - }; - -} diff --git a/stratosphere/ro/source/impl/ro_nro_utils.cpp b/stratosphere/ro/source/impl/ro_nro_utils.cpp index 6d7b7effa..5ebebe6b3 100644 --- a/stratosphere/ro/source/impl/ro_nro_utils.cpp +++ b/stratosphere/ro/source/impl/ro_nro_utils.cpp @@ -15,65 +15,38 @@ */ #include #include "ro_nro_utils.hpp" -#include "ro_map_utils.hpp" namespace ams::ro::impl { - Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) { - /* Re-map the NRO/BSS as code memory in the destination process. */ - MappedCodeMemory nro_mcm; - MappedCodeMemory bss_mcm; - ProcessRegionInfo region_info(process_handle); - u64 base_address; - { - const u64 memory_size = nro_heap_size + bss_heap_size; - int i; - for (i = 0; i < RetrySearchCount; ++i) { - /* Get a random address for the nro. */ - base_address = region_info.GetAslrRegion(memory_size); - R_UNLESS(base_address != 0, ro::ResultOutOfAddressSpace()); + namespace { - /* Map the NRO, retrying if random address was invalid. */ - MappedCodeMemory tmp_nro_mcm(process_handle, base_address, nro_heap_address, nro_heap_size); - R_TRY_CATCH(tmp_nro_mcm.GetResult()) { - R_CATCH(svc::ResultInvalidCurrentMemory) { continue; } - } R_END_TRY_CATCH; + ALWAYS_INLINE size_t SetupNroProcessMemoryRegions(os::ProcessMemoryRegion *regions, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) { + /* Reset region count. */ + size_t num_regions = 0; - /* Handle bss. */ - if (bss_heap_size > 0) { - /* Map BSS, retrying if random address was invalid. */ - MappedCodeMemory tmp_bss_mcm(process_handle, base_address + nro_heap_size, bss_heap_address, bss_heap_size); - R_TRY_CATCH(tmp_bss_mcm.GetResult()) { - R_CATCH(svc::ResultInvalidCurrentMemory) { continue; } - } R_END_TRY_CATCH; + /* We always want a region for the nro. */ + regions[num_regions++] = { nro_heap_address, nro_heap_size }; - /* Check that we can have guard spaces. */ - if (!region_info.CanEmplaceGuardSpaces(process_handle, base_address, memory_size)) { - continue; - } - - /* We succeeded, so save the bss memory. */ - bss_mcm = std::move(tmp_bss_mcm); - } else { - /* Check that we can have guard spaces. */ - if (!region_info.CanEmplaceGuardSpaces(process_handle, base_address, memory_size)) { - continue; - } - } - - /* We succeeded, so save the code memory. */ - nro_mcm = std::move(tmp_nro_mcm); - break; + /* If we have bss, create a region for bss. */ + if (bss_heap_size > 0) { + regions[num_regions++] = { bss_heap_address, bss_heap_size }; } - R_UNLESS(i != RetrySearchCount, ro::ResultOutOfAddressSpace()); + return num_regions; } - /* Cancel the automatic closing of our mappings. */ - nro_mcm.Cancel(); - bss_mcm.Cancel(); + } + + Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) { + /* Set up the process memory regions. */ + os::ProcessMemoryRegion regions[2]; + const size_t num_regions = SetupNroProcessMemoryRegions(regions, nro_heap_address, nro_heap_size, bss_heap_address, bss_heap_size); + + /* Re-map the nro/bss as code memory in the destination process. */ + R_TRY_CATCH(os::MapProcessCodeMemory(out_base_address, process_handle, regions, num_regions)) { + R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace()) + } R_END_TRY_CATCH; - *out_base_address = base_address; R_SUCCEED(); } @@ -82,28 +55,20 @@ namespace ams::ro::impl { const u64 ro_offset = rx_offset + rx_size; const u64 rw_offset = ro_offset + ro_size; - R_TRY(svc::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, svc::MemoryPermission_ReadExecute)); - R_TRY(svc::SetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, svc::MemoryPermission_Read)); - R_TRY(svc::SetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, svc::MemoryPermission_ReadWrite)); + R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, os::MemoryPermission_ReadExecute)); + R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, os::MemoryPermission_ReadOnly)); + R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, os::MemoryPermission_ReadWrite)); R_SUCCEED(); } - Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size) { - /* First, unmap bss. */ - if (bss_heap_size > 0) { - R_TRY(svc::UnmapProcessCodeMemory(process_handle, base_address + code_size + rw_size, bss_heap_address, bss_heap_size)); - } + Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) { + /* Set up the process memory regions. */ + os::ProcessMemoryRegion regions[2]; + const size_t num_regions = SetupNroProcessMemoryRegions(regions, nro_heap_address, nro_heap_size, bss_heap_address, bss_heap_size); - /* Next, unmap .rwdata */ - if (rw_size > 0) { - R_TRY(svc::UnmapProcessCodeMemory(process_handle, base_address + code_size, nro_heap_address + code_size, rw_size)); - } - - /* Finally, unmap .text + .rodata. */ - R_TRY(svc::UnmapProcessCodeMemory(process_handle, base_address, nro_heap_address, code_size)); - - R_SUCCEED(); + /* Unmap the nro/bss. */ + R_RETURN(os::UnmapProcessCodeMemory(process_handle, base_address, regions, num_regions)); } } diff --git a/stratosphere/ro/source/impl/ro_nro_utils.hpp b/stratosphere/ro/source/impl/ro_nro_utils.hpp index 75fcdaa2e..df018c872 100644 --- a/stratosphere/ro/source/impl/ro_nro_utils.hpp +++ b/stratosphere/ro/source/impl/ro_nro_utils.hpp @@ -22,6 +22,6 @@ namespace ams::ro::impl { /* Utilities for working with NROs. */ Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size); Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size); - Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size); + Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size); } \ No newline at end of file diff --git a/stratosphere/ro/source/impl/ro_nrr_utils.cpp b/stratosphere/ro/source/impl/ro_nrr_utils.cpp index 17d145d4e..27eeeddb9 100644 --- a/stratosphere/ro/source/impl/ro_nrr_utils.cpp +++ b/stratosphere/ro/source/impl/ro_nrr_utils.cpp @@ -15,7 +15,6 @@ */ #include #include "ro_nrr_utils.hpp" -#include "ro_map_utils.hpp" #include "ro_service_impl.hpp" namespace ams::ro::impl { @@ -196,51 +195,29 @@ namespace ams::ro::impl { /* Utilities for working with NRRs. */ Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, void *out_hash, size_t out_hash_size, os::NativeHandle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, NrrKind nrr_kind, bool enforce_nrr_kind) { - /* Re-map the NRR as code memory in the destination process. */ - MappedCodeMemory nrr_mcm; - ProcessRegionInfo region_info(process_handle); - u64 code_address; - { - int i; - for (i = 0; i < RetrySearchCount; ++i) { - /* Get a random address for the nrr. */ - code_address = region_info.GetAslrRegion(nrr_heap_size); - R_UNLESS(code_address != 0, ro::ResultOutOfAddressSpace()); + /* Re-map the nrr as code memory in the destination process. */ + u64 code_address = 0; + const os::ProcessMemoryRegion region = { nrr_heap_address, nrr_heap_size }; + R_TRY_CATCH(os::MapProcessCodeMemory(std::addressof(code_address), process_handle, std::addressof(region), 1)) { + R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace()) + } R_END_TRY_CATCH; - /* Map the code memory, retrying if the random address was invalid. */ - MappedCodeMemory tmp_mcm(process_handle, code_address, nrr_heap_address, nrr_heap_size); - R_TRY_CATCH(tmp_mcm.GetResult()) { - R_CATCH(svc::ResultInvalidCurrentMemory) { continue; } - } R_END_TRY_CATCH; + /* If we fail, unmap the nrr code memory. */ + ON_RESULT_FAILURE { R_ABORT_UNLESS(os::UnmapProcessCodeMemory(process_handle, code_address, std::addressof(region), 1)); }; - /* Check that we can have guard spaces. */ - if (!region_info.CanEmplaceGuardSpaces(process_handle, code_address, nrr_heap_size)) { - continue; - } + /* Map the nrr in our process. */ + void *mapped_memory = nullptr; + R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, code_address, region.size)) { + R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace()) + } R_END_TRY_CATCH; - /* We succeeded, so save the code memory. */ - nrr_mcm = std::move(tmp_mcm); - break; - } + /* If we fail, unmap the nrr memory. */ + ON_RESULT_FAILURE_2 { os::UnmapProcessMemory(mapped_memory, process_handle, code_address, region.size); }; - R_UNLESS(i != RetrySearchCount, ro::ResultOutOfAddressSpace()); - } - - /* Decide where to map the NRR in our process. */ - uintptr_t map_address; - R_UNLESS(R_SUCCEEDED(SearchFreeRegion(std::addressof(map_address), nrr_heap_size)), ro::ResultOutOfAddressSpace()); - - /* NOTE: Nintendo does not check the return value of this map. We will check, instead of aborting if it fails. */ - AutoCloseMap nrr_map(map_address, process_handle, code_address, nrr_heap_size); - R_TRY(nrr_map.GetResult()); - - NrrHeader *nrr_header = reinterpret_cast(map_address); + /* Validate the nrr header. */ + NrrHeader *nrr_header = static_cast(mapped_memory); R_TRY(ValidateNrr(nrr_header, nrr_heap_size, program_id, nrr_kind, enforce_nrr_kind)); - /* Cancel the automatic closing of our mappings. */ - nrr_map.Cancel(); - nrr_mcm.Cancel(); - /* Save a copy of the hash that we verified. */ crypto::GenerateSha256(out_hash, out_hash_size, nrr_header->GetSignedArea(), nrr_header->GetSignedAreaSize()); @@ -250,16 +227,18 @@ namespace ams::ro::impl { } Result UnmapNrr(os::NativeHandle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) { - R_TRY(svc::UnmapProcessMemory(reinterpret_cast(header), process_handle, mapped_code_address, nrr_heap_size)); - R_TRY(svc::UnmapProcessCodeMemory(process_handle, mapped_code_address, nrr_heap_address, nrr_heap_size)); - R_SUCCEED(); + /* Unmap our process mapping. */ + os::UnmapProcessMemory(const_cast(header), process_handle, mapped_code_address, nrr_heap_size); + + /* Unmap the code memory mapping. */ + const os::ProcessMemoryRegion region = { nrr_heap_address, nrr_heap_size }; + R_RETURN(os::UnmapProcessCodeMemory(process_handle, mapped_code_address, std::addressof(region), 1)); } bool ValidateNrrHashTableEntry(const void *signed_area, size_t signed_area_size, size_t hashes_offset, size_t num_hashes, const void *nrr_hash, const u8 *hash_table, const void *desired_hash) { crypto::Sha256Generator sha256; sha256.Initialize(); - /* Hash data before the hash table. */ const size_t pre_hash_table_size = hashes_offset - NrrHeader::GetSignedAreaOffset(); sha256.Update(signed_area, pre_hash_table_size); diff --git a/stratosphere/ro/source/impl/ro_service_impl.cpp b/stratosphere/ro/source/impl/ro_service_impl.cpp index 054412ca3..2d9e3e923 100644 --- a/stratosphere/ro/source/impl/ro_service_impl.cpp +++ b/stratosphere/ro/source/impl/ro_service_impl.cpp @@ -14,7 +14,6 @@ * along with this program. If not, see . */ #include -#include "ro_map_utils.hpp" #include "ro_nrr_utils.hpp" #include "ro_nro_utils.hpp" #include "ro_patcher.hpp" @@ -248,16 +247,17 @@ namespace ams::ro::impl { } Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) { - /* Find space to map the NRO. */ - uintptr_t map_address; - R_UNLESS(R_SUCCEEDED(SearchFreeRegion(std::addressof(map_address), expected_nro_size)), ro::ResultOutOfAddressSpace()); + /* Map the NRO. */ + void *mapped_memory = nullptr; + R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), m_process_handle, base_address, expected_nro_size)) { + R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace()) + } R_END_TRY_CATCH; - /* Actually map the NRO. */ - AutoCloseMap nro_map(map_address, m_process_handle, base_address, expected_nro_size); - R_TRY(nro_map.GetResult()); + /* When we're done, unmap the memory. */ + ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, m_process_handle, base_address, expected_nro_size); }; /* Validate header. */ - const NroHeader *header = reinterpret_cast(map_address); + const NroHeader *header = static_cast(mapped_memory); R_UNLESS(header->IsMagicValid(), ro::ResultInvalidNro()); /* Read sizes from header. */ @@ -298,7 +298,7 @@ namespace ams::ro::impl { R_UNLESS(R_FAILED(this->GetNroInfoByModuleId(nullptr, module_id)), ro::ResultAlreadyLoaded()); /* Apply patches to NRO. */ - LocateAndApplyIpsPatchesToModule(module_id, reinterpret_cast(map_address), nro_size); + LocateAndApplyIpsPatchesToModule(module_id, static_cast(mapped_memory), nro_size); /* Copy to output. */ *out_module_id = *module_id; @@ -552,21 +552,14 @@ namespace ams::ro::impl { /* Map the NRO. */ R_TRY(MapNro(std::addressof(nro_info->base_address), context->GetProcessHandle(), nro_address, nro_size, bss_address, bss_size)); + ON_RESULT_FAILURE { UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, nro_size, bss_address, bss_size); }; /* Validate the NRO (parsing region extents). */ u64 rx_size = 0, ro_size = 0, rw_size = 0; - { - ON_RESULT_FAILURE { UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, bss_address, bss_size, nro_size, 0); }; - - R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), nro_info->base_address, nro_size, bss_size)); - } + R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), nro_info->base_address, nro_size, bss_size)); /* Set NRO perms. */ - { - ON_RESULT_FAILURE { UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, bss_address, bss_size, rx_size + ro_size, rw_size); }; - - R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size)); - } + R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size)); context->SetNroInfoInUse(nro_info, true); nro_info->code_size = rx_size + ro_size; @@ -594,7 +587,7 @@ namespace ams::ro::impl { context->SetNroInfoInUse(nro_info, false); std::memset(nro_info, 0, sizeof(*nro_info)); } - R_RETURN(UnmapNro(context->GetProcessHandle(), nro_backup.base_address, nro_backup.nro_heap_address, nro_backup.bss_heap_address, nro_backup.bss_heap_size, nro_backup.code_size, nro_backup.rw_size)); + R_RETURN(UnmapNro(context->GetProcessHandle(), nro_backup.base_address, nro_backup.nro_heap_address, nro_backup.code_size + nro_backup.rw_size, nro_backup.bss_heap_address, nro_backup.bss_heap_size)); } /* Debug service implementations. */