diff --git a/libraries/libmesosphere/source/kern_k_address_space_info.cpp b/libraries/libmesosphere/source/kern_k_address_space_info.cpp index a0b1fbe9f..46a85ec89 100644 --- a/libraries/libmesosphere/source/kern_k_address_space_info.cpp +++ b/libraries/libmesosphere/source/kern_k_address_space_info.cpp @@ -22,19 +22,19 @@ namespace ams::kern { constexpr uintptr_t Invalid = std::numeric_limits::max(); constexpr KAddressSpaceInfo AddressSpaceInfos[] = { - { 32, 2_MB, 1_GB - 2_MB, KAddressSpaceInfo::Type_MapSmall, }, - { 32, 1_GB, 4_GB - 1_GB, KAddressSpaceInfo::Type_MapLarge, }, - { 32, Invalid, 1_GB, KAddressSpaceInfo::Type_Heap, }, - { 32, Invalid, 1_GB, KAddressSpaceInfo::Type_Alias, }, - { 36, 128_MB, 2_GB - 128_MB, KAddressSpaceInfo::Type_MapSmall, }, - { 36, 2_GB, 64_GB - 2_GB, KAddressSpaceInfo::Type_MapLarge, }, - { 36, Invalid, 6_GB, KAddressSpaceInfo::Type_Heap, }, - { 36, Invalid, 6_GB, KAddressSpaceInfo::Type_Alias, }, - { 39, 128_MB, 512_GB - 128_MB, KAddressSpaceInfo::Type_Map39Bit, }, - { 39, Invalid, 64_GB, KAddressSpaceInfo::Type_MapSmall, }, - { 39, Invalid, 6_GB, KAddressSpaceInfo::Type_Heap, }, - { 39, Invalid, 64_GB, KAddressSpaceInfo::Type_Alias, }, - { 39, Invalid, 2_GB, KAddressSpaceInfo::Type_Stack, }, + { 32, ams::svc::AddressSmallMap32Start, ams::svc::AddressSmallMap32Size, KAddressSpaceInfo::Type_MapSmall, }, + { 32, ams::svc::AddressLargeMap32Start, ams::svc::AddressLargeMap32Size, KAddressSpaceInfo::Type_MapLarge, }, + { 32, Invalid, ams::svc::AddressMemoryRegionHeap32Size, KAddressSpaceInfo::Type_Heap, }, + { 32, Invalid, ams::svc::AddressMemoryRegionAlias32Size, KAddressSpaceInfo::Type_Alias, }, + { 36, ams::svc::AddressSmallMap36Start, ams::svc::AddressSmallMap36Size, KAddressSpaceInfo::Type_MapSmall, }, + { 36, ams::svc::AddressLargeMap36Start, ams::svc::AddressLargeMap36Size, KAddressSpaceInfo::Type_MapLarge, }, + { 36, Invalid, ams::svc::AddressMemoryRegionHeap36Size, KAddressSpaceInfo::Type_Heap, }, + { 36, Invalid, ams::svc::AddressMemoryRegionAlias36Size, KAddressSpaceInfo::Type_Alias, }, + { 39, ams::svc::AddressMap39Start, ams::svc::AddressMap39Size, KAddressSpaceInfo::Type_Map39Bit, }, + { 39, Invalid, ams::svc::AddressMemoryRegionSmall39Size, KAddressSpaceInfo::Type_MapSmall, }, + { 39, Invalid, ams::svc::AddressMemoryRegionHeap39Size, KAddressSpaceInfo::Type_Heap, }, + { 39, Invalid, ams::svc::AddressMemoryRegionAlias39Size, KAddressSpaceInfo::Type_Alias, }, + { 39, Invalid, ams::svc::AddressMemoryRegionStack39Size, KAddressSpaceInfo::Type_Stack, }, }; constexpr bool IsAllowedIndexForAddress(size_t index) { diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index 1301e8e91..05847e4b0 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -66,7 +66,6 @@ #include #include #include -#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/map/map_api.hpp b/libraries/libstratosphere/include/stratosphere/map/map_api.hpp deleted file mode 100644 index cd459ac59..000000000 --- a/libraries/libstratosphere/include/stratosphere/map/map_api.hpp +++ /dev/null @@ -1,28 +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::map { - - /* Public API. */ - Result GetProcessAddressSpaceInfo(AddressSpaceInfo *out, os::NativeHandle process_h); - Result LocateMappableSpace(uintptr_t *out_address, size_t size); - Result MapCodeMemoryInProcess(MappedCodeMemory &out_mcm, os::NativeHandle process_handle, uintptr_t base_address, size_t size); - bool CanAddGuardRegionsInProcess(os::NativeHandle process_handle, uintptr_t address, size_t size); - -} diff --git a/libraries/libstratosphere/include/stratosphere/map/map_types.hpp b/libraries/libstratosphere/include/stratosphere/map/map_types.hpp deleted file mode 100644 index 9dc92788f..000000000 --- a/libraries/libstratosphere/include/stratosphere/map/map_types.hpp +++ /dev/null @@ -1,123 +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 -#include - -namespace ams::map { - - /* Types. */ - struct AddressSpaceInfo { - uintptr_t heap_base; - size_t heap_size; - uintptr_t heap_end; - uintptr_t alias_base; - size_t alias_size; - uintptr_t alias_end; - uintptr_t aslr_base; - size_t aslr_size; - uintptr_t aslr_end; - }; - - static constexpr uintptr_t AslrBase32Bit = 0x0000200000ul; - static constexpr size_t AslrSize32Bit = 0x003FE00000ul; - static constexpr uintptr_t AslrBase64BitDeprecated = 0x0008000000ul; - static constexpr size_t AslrSize64BitDeprecated = 0x0078000000ul; - static constexpr uintptr_t AslrBase64Bit = 0x0008000000ul; - static constexpr size_t AslrSize64Bit = 0x7FF8000000ul; - - class AutoCloseMap { - private: - os::NativeHandle process_handle; - Result result; - uintptr_t mapped_address; - uintptr_t base_address; - size_t size; - public: - AutoCloseMap(uintptr_t mp, os::NativeHandle p_h, uintptr_t ba, size_t sz) : process_handle(p_h), mapped_address(mp), base_address(ba), size(sz) { - this->result = svc::MapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size); - } - - ~AutoCloseMap() { - if (this->process_handle != os::InvalidNativeHandle && R_SUCCEEDED(this->result)) { - R_ABORT_UNLESS(svc::UnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size)); - } - } - - Result GetResult() const { - return this->result; - } - - bool IsSuccess() const { - return R_SUCCEEDED(this->result); - } - - void Invalidate() { - this->process_handle = os::InvalidNativeHandle; - } - }; - - class MappedCodeMemory { - private: - os::NativeHandle process_handle; - Result result; - uintptr_t dst_address; - uintptr_t src_address; - size_t size; - public: - MappedCodeMemory(Result init_res) : process_handle(os::InvalidNativeHandle), result(init_res), dst_address(0), src_address(0), size(0) { - /* ... */ - } - - MappedCodeMemory(os::NativeHandle p_h, uintptr_t dst, uintptr_t src, size_t sz) : process_handle(p_h), dst_address(dst), src_address(src), size(sz) { - this->result = svc::MapProcessCodeMemory(this->process_handle, this->dst_address, this->src_address, this->size); - } - - ~MappedCodeMemory() { - if (this->process_handle != os::InvalidNativeHandle && R_SUCCEEDED(this->result) && this->size > 0) { - R_ABORT_UNLESS(svc::UnmapProcessCodeMemory(this->process_handle, this->dst_address, this->src_address, this->size)); - } - } - - uintptr_t GetDstAddress() const { - return this->dst_address; - } - - Result GetResult() const { - return this->result; - } - - bool IsSuccess() const { - return R_SUCCEEDED(this->result); - } - - void Invalidate() { - this->process_handle = os::InvalidNativeHandle; - } - - MappedCodeMemory &operator=(MappedCodeMemory &&o) { - this->process_handle = o.process_handle; - this->result = o.result; - this->dst_address = o.dst_address; - this->src_address = o.src_address; - this->size = o.size; - o.Invalidate(); - return *this; - } - }; - -} diff --git a/libraries/libstratosphere/source/map/map_api.cpp b/libraries/libstratosphere/source/map/map_api.cpp deleted file mode 100644 index 4aff4b31a..000000000 --- a/libraries/libstratosphere/source/map/map_api.cpp +++ /dev/null @@ -1,205 +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 - -namespace ams::map { - - namespace { - - /* Convenience defines. */ - constexpr size_t GuardRegionSize = 0x4000; - constexpr size_t LocateRetryCount = 0x200; - - /* Deprecated/Modern implementations. */ - Result LocateMappableSpaceDeprecated(uintptr_t *out_address, size_t size) { - svc::MemoryInfo mem_info; - svc::PageInfo page_info; - uintptr_t cur_base = 0; - - AddressSpaceInfo address_space; - R_TRY(GetProcessAddressSpaceInfo(&address_space, dd::GetCurrentProcessHandle())); - cur_base = address_space.aslr_base; - - do { - R_TRY(svc::QueryMemory(&mem_info, &page_info, cur_base)); - - if (mem_info.state == svc::MemoryState_Free && mem_info.addr - cur_base + mem_info.size >= size) { - *out_address = cur_base; - return ResultSuccess(); - } - - const uintptr_t mem_end = mem_info.addr + mem_info.size; - R_UNLESS(mem_info.state != svc::MemoryState_Inaccessible, svc::ResultOutOfMemory()); - R_UNLESS(cur_base <= mem_end, svc::ResultOutOfMemory()); - R_UNLESS(mem_end <= static_cast(std::numeric_limits::max()), svc::ResultOutOfMemory()); - - cur_base = mem_end; - } while (true); - } - - Result LocateMappableSpaceModern(uintptr_t *out_address, size_t size) { - svc::MemoryInfo mem_info; - svc::PageInfo page_info; - uintptr_t cur_base = 0, cur_end = 0; - - AddressSpaceInfo address_space; - R_TRY(GetProcessAddressSpaceInfo(&address_space, dd::GetCurrentProcessHandle())); - cur_base = address_space.aslr_base; - cur_end = cur_base + size; - - R_UNLESS(cur_base < cur_end, svc::ResultOutOfMemory()); - - while (true) { - if (address_space.heap_size && (address_space.heap_base <= cur_end - 1 && cur_base <= address_space.heap_end - 1)) { - /* If we overlap the heap region, go to the end of the heap region. */ - R_UNLESS(cur_base != address_space.heap_end, svc::ResultOutOfMemory()); - cur_base = address_space.heap_end; - } else if (address_space.alias_size && (address_space.alias_base <= cur_end - 1 && cur_base <= address_space.alias_end - 1)) { - /* If we overlap the alias region, go to the end of the alias region. */ - R_UNLESS(cur_base != address_space.alias_end, svc::ResultOutOfMemory()); - cur_base = address_space.alias_end; - } else { - R_ABORT_UNLESS(svc::QueryMemory(&mem_info, &page_info, cur_base)); - if (mem_info.state == svc::MemoryState_Free && mem_info.addr - cur_base + mem_info.size >= size) { - *out_address = cur_base; - return ResultSuccess(); - } - R_UNLESS(cur_base < mem_info.addr + mem_info.size, svc::ResultOutOfMemory()); - - cur_base = mem_info.addr + mem_info.size; - R_UNLESS(cur_base < address_space.aslr_end, svc::ResultOutOfMemory()); - } - cur_end = cur_base + size; - R_UNLESS(cur_base < cur_base + size, svc::ResultOutOfMemory()); - } - } - - Result MapCodeMemoryInProcessDeprecated(MappedCodeMemory &out_mcm, os::NativeHandle process_handle, uintptr_t base_address, size_t size) { - AddressSpaceInfo address_space; - R_TRY(GetProcessAddressSpaceInfo(&address_space, process_handle)); - - R_UNLESS(size <= address_space.aslr_size, ro::ResultOutOfAddressSpace()); - - uintptr_t try_address; - for (unsigned int i = 0; i < LocateRetryCount; i++) { - try_address = address_space.aslr_base + (os::GenerateRandomU64(static_cast(address_space.aslr_size - size) / os::MemoryPageSize) * os::MemoryPageSize); - - MappedCodeMemory tmp_mcm(process_handle, try_address, base_address, size); - R_TRY_CATCH(tmp_mcm.GetResult()) { - R_CATCH(svc::ResultInvalidCurrentMemory) { continue; } - } R_END_TRY_CATCH; - - if (!CanAddGuardRegionsInProcess(process_handle, try_address, size)) { - continue; - } - - /* We're done searching. */ - out_mcm = std::move(tmp_mcm); - return ResultSuccess(); - } - - return ro::ResultOutOfAddressSpace(); - } - - Result MapCodeMemoryInProcessModern(MappedCodeMemory &out_mcm, os::NativeHandle process_handle, uintptr_t base_address, size_t size) { - AddressSpaceInfo address_space; - R_TRY(GetProcessAddressSpaceInfo(&address_space, process_handle)); - - R_UNLESS(size <= address_space.aslr_size, ro::ResultOutOfAddressSpace()); - - uintptr_t try_address; - for (unsigned int i = 0; i < LocateRetryCount; i++) { - while (true) { - try_address = address_space.aslr_base + (os::GenerateRandomU64(static_cast(address_space.aslr_size - size) / os::MemoryPageSize) * os::MemoryPageSize); - if (address_space.heap_size && (address_space.heap_base <= try_address + size - 1 && try_address <= address_space.heap_end - 1)) { - continue; - } - if (address_space.alias_size && (address_space.alias_base <= try_address + size - 1 && try_address <= address_space.alias_end - 1)) { - continue; - } - break; - } - - MappedCodeMemory tmp_mcm(process_handle, try_address, base_address, size); - R_TRY_CATCH(tmp_mcm.GetResult()) { - R_CATCH(svc::ResultInvalidCurrentMemory) { continue; } - } R_END_TRY_CATCH; - - if (!CanAddGuardRegionsInProcess(process_handle, try_address, size)) { - continue; - } - - /* We're done searching. */ - out_mcm = std::move(tmp_mcm); - return ResultSuccess(); - } - - return ro::ResultOutOfAddressSpace(); - } - - } - - /* Public API. */ - Result GetProcessAddressSpaceInfo(AddressSpaceInfo *out, os::NativeHandle process_h) { - /* Clear output. */ - std::memset(out, 0, sizeof(*out)); - - /* Retrieve info from kernel. */ - R_TRY(svc::GetInfo(&out->heap_base, svc::InfoType_HeapRegionAddress, process_h, 0)); - R_TRY(svc::GetInfo(&out->heap_size, svc::InfoType_HeapRegionSize, process_h, 0)); - R_TRY(svc::GetInfo(&out->alias_base, svc::InfoType_AliasRegionAddress, process_h, 0)); - R_TRY(svc::GetInfo(&out->alias_size, svc::InfoType_AliasRegionSize, process_h, 0)); - R_TRY(svc::GetInfo(&out->aslr_base, svc::InfoType_AslrRegionAddress, process_h, 0)); - R_TRY(svc::GetInfo(&out->aslr_size, svc::InfoType_AslrRegionSize, process_h, 0)); - - out->heap_end = out->heap_base + out->heap_size; - out->alias_end = out->alias_base + out->alias_size; - out->aslr_end = out->aslr_base + out->aslr_size; - return ResultSuccess(); - } - - Result LocateMappableSpace(uintptr_t *out_address, size_t size) { - if (hos::GetVersion() >= hos::Version_2_0_0) { - return LocateMappableSpaceModern(out_address, size); - } else { - return LocateMappableSpaceDeprecated(out_address, size); - } - } - - Result MapCodeMemoryInProcess(MappedCodeMemory &out_mcm, os::NativeHandle process_handle, uintptr_t base_address, size_t size) { - if (hos::GetVersion() >= hos::Version_2_0_0) { - return MapCodeMemoryInProcessModern(out_mcm, process_handle, base_address, size); - } else { - return MapCodeMemoryInProcessDeprecated(out_mcm, process_handle, base_address, size); - } - } - - bool CanAddGuardRegionsInProcess(os::NativeHandle process_handle, uintptr_t address, size_t size) { - svc::MemoryInfo mem_info; - svc::PageInfo page_info; - - /* Nintendo doesn't validate SVC return values at all. */ - /* TODO: Should we allow these to fail? */ - R_ABORT_UNLESS(svc::QueryProcessMemory(&mem_info, &page_info, process_handle, address - 1)); - if (mem_info.state == svc::MemoryState_Free && address - GuardRegionSize >= mem_info.addr) { - R_ABORT_UNLESS(svc::QueryProcessMemory(&mem_info, &page_info, process_handle, address + size)); - return mem_info.state == svc::MemoryState_Free && address + size + GuardRegionSize <= mem_info.addr + mem_info.size; - } - - return false; - } - -} diff --git a/libraries/libvapours/include/vapours/svc.hpp b/libraries/libvapours/include/vapours/svc.hpp index 59862a07c..6bb7003e2 100644 --- a/libraries/libvapours/include/vapours/svc.hpp +++ b/libraries/libvapours/include/vapours/svc.hpp @@ -21,5 +21,6 @@ #include #include +#include #include #include diff --git a/libraries/libvapours/include/vapours/svc/svc_memory_map.hpp b/libraries/libvapours/include/vapours/svc/svc_memory_map.hpp new file mode 100644 index 000000000..72059e29f --- /dev/null +++ b/libraries/libvapours/include/vapours/svc/svc_memory_map.hpp @@ -0,0 +1,100 @@ +/* + * 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::svc { + + #if defined(ATMOSPHERE_ARCH_ARM64) + + constexpr inline size_t AddressMemoryRegionSmall32Size = 1_GB; + constexpr inline size_t AddressMemoryRegionLarge32Size = 4_GB - AddressMemoryRegionSmall32Size; + constexpr inline size_t AddressMemoryRegionHeap32Size = 1_GB; + constexpr inline size_t AddressMemoryRegionAlias32Size = 1_GB; + + constexpr inline size_t AddressMemoryRegionSmall36Size = 2_GB; + constexpr inline size_t AddressMemoryRegionLarge36Size = 64_GB - AddressMemoryRegionSmall36Size; + constexpr inline size_t AddressMemoryRegionHeap36Size = 6_GB; + constexpr inline size_t AddressMemoryRegionAlias36Size = 6_GB; + + constexpr inline size_t AddressMemoryRegionSmall39Size = 64_GB; + constexpr inline size_t AddressMemoryRegionHeap39Size = 6_GB; + constexpr inline size_t AddressMemoryRegionAlias39Size = 64_GB; + constexpr inline size_t AddressMemoryRegionStack39Size = 2_GB; + + constexpr inline size_t AddressMemoryRegion39Size = 512_GB; + + #elif defined(ATMOSPHERE_ARCH_ARM) + + constexpr inline size_t AddressMemoryRegionSmall32Size = 512_MB; + constexpr inline size_t AddressMemoryRegionLarge32Size = 2_GB - AddressMemoryRegionSmall32Size; + constexpr inline size_t AddressMemoryRegionHeap32Size = 1_GB; + constexpr inline size_t AddressMemoryRegionAlias32Size = 512_MB; + + constexpr inline size_t AddressMemoryRegionSmall36Size = 0; + constexpr inline size_t AddressMemoryRegionLarge36Size = 0; + constexpr inline size_t AddressMemoryRegionHeap36Size = 0; + constexpr inline size_t AddressMemoryRegionAlias36Size = 0; + + constexpr inline size_t AddressMemoryRegionSmall39Size = 0; + constexpr inline size_t AddressMemoryRegionHeap39Size = 0; + constexpr inline size_t AddressMemoryRegionAlias39Size = 0; + constexpr inline size_t AddressMemoryRegionStack39Size = 0; + + constexpr inline size_t AddressMemoryRegion39Size = 0; + + #else + + #error "Unknown architecture for svc::AddressMemoryRegion*Size" + + #endif + + constexpr inline size_t AddressNullGuard32Size = 2_MB; + constexpr inline size_t AddressNullGuard64Size = 128_MB; + + constexpr inline uintptr_t AddressMemoryRegionSmall32Start = 0; + constexpr inline uintptr_t AddressMemoryRegionSmall32End = AddressMemoryRegionSmall32Start + AddressMemoryRegionSmall32Size; + + constexpr inline uintptr_t AddressMemoryRegionLarge32Start = AddressMemoryRegionSmall32End; + constexpr inline uintptr_t AddressMemoryRegionLarge32End = AddressMemoryRegionLarge32Start + AddressMemoryRegionLarge32Size; + + constexpr inline uintptr_t AddressSmallMap32Start = AddressMemoryRegionSmall32Start + AddressNullGuard32Size; + constexpr inline uintptr_t AddressSmallMap32End = AddressMemoryRegionSmall32End; + constexpr inline size_t AddressSmallMap32Size = AddressSmallMap32End - AddressSmallMap32Start; + + constexpr inline uintptr_t AddressLargeMap32Start = AddressMemoryRegionLarge32Start; + constexpr inline uintptr_t AddressLargeMap32End = AddressMemoryRegionLarge32End; + constexpr inline size_t AddressLargeMap32Size = AddressLargeMap32End - AddressLargeMap32Start; + + constexpr inline uintptr_t AddressMemoryRegionSmall36Start = 0; + constexpr inline uintptr_t AddressMemoryRegionSmall36End = AddressMemoryRegionSmall36Start + AddressMemoryRegionSmall36Size; + + constexpr inline uintptr_t AddressMemoryRegionLarge36Start = AddressMemoryRegionSmall36End; + constexpr inline uintptr_t AddressMemoryRegionLarge36End = AddressMemoryRegionLarge36Start + AddressMemoryRegionLarge36Size; + + constexpr inline uintptr_t AddressSmallMap36Start = AddressMemoryRegionSmall36Start + AddressNullGuard64Size; + constexpr inline uintptr_t AddressSmallMap36End = AddressMemoryRegionSmall36End; + constexpr inline size_t AddressSmallMap36Size = AddressSmallMap36End - AddressSmallMap36Start; + + constexpr inline uintptr_t AddressLargeMap36Start = AddressMemoryRegionLarge36Start; + constexpr inline uintptr_t AddressLargeMap36End = AddressMemoryRegionLarge36End; + constexpr inline size_t AddressLargeMap36Size = AddressLargeMap36End - AddressLargeMap36Start; + + constexpr inline uintptr_t AddressMap39Start = 0 + AddressNullGuard64Size; + constexpr inline uintptr_t AddressMap39End = AddressMemoryRegion39Size; + constexpr inline size_t AddressMap39Size = AddressMap39End - AddressMap39Start; + +} diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp index 367bcc64c..956540255 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp @@ -810,16 +810,12 @@ namespace ams::dmnt::cheat::impl { R_ABORT_UNLESS_IF_NEW_PROCESS(pm::dmnt::AtmosphereGetProcessInfo(&proc_h, &loc, &status, this->cheat_process_metadata.process_id)); this->cheat_process_metadata.program_id = loc.program_id; - { - map::AddressSpaceInfo as_info; - R_ABORT_UNLESS(map::GetProcessAddressSpaceInfo(&as_info, proc_h)); - this->cheat_process_metadata.heap_extents.base = as_info.heap_base; - this->cheat_process_metadata.heap_extents.size = as_info.heap_size; - this->cheat_process_metadata.alias_extents.base = as_info.alias_base; - this->cheat_process_metadata.alias_extents.size = as_info.alias_size; - this->cheat_process_metadata.aslr_extents.base = as_info.aslr_base; - this->cheat_process_metadata.aslr_extents.size = as_info.aslr_size; - } + R_ABORT_UNLESS(svc::GetInfo(std::addressof(this->cheat_process_metadata.heap_extents.base), svc::InfoType_HeapRegionAddress, svc::PseudoHandle::CurrentProcess, 0)); + R_ABORT_UNLESS(svc::GetInfo(std::addressof(this->cheat_process_metadata.heap_extents.size), svc::InfoType_HeapRegionSize, svc::PseudoHandle::CurrentProcess, 0)); + R_ABORT_UNLESS(svc::GetInfo(std::addressof(this->cheat_process_metadata.alias_extents.base), svc::InfoType_AliasRegionAddress, svc::PseudoHandle::CurrentProcess, 0)); + R_ABORT_UNLESS(svc::GetInfo(std::addressof(this->cheat_process_metadata.alias_extents.size), svc::InfoType_AliasRegionSize, svc::PseudoHandle::CurrentProcess, 0)); + R_ABORT_UNLESS(svc::GetInfo(std::addressof(this->cheat_process_metadata.aslr_extents.base), svc::InfoType_AslrRegionAddress, svc::PseudoHandle::CurrentProcess, 0)); + R_ABORT_UNLESS(svc::GetInfo(std::addressof(this->cheat_process_metadata.aslr_extents.size), svc::InfoType_AslrRegionSize, svc::PseudoHandle::CurrentProcess, 0)); /* If new process launch, we may not want to actually attach. */ if (on_process_launch) { diff --git a/stratosphere/loader/source/ldr_auto_close.hpp b/stratosphere/loader/source/ldr_auto_close.hpp new file mode 100644 index 000000000..4c67004fc --- /dev/null +++ b/stratosphere/loader/source/ldr_auto_close.hpp @@ -0,0 +1,54 @@ +/* + * 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::ldr { + + class AutoCloseMap { + NON_COPYABLE(AutoCloseMap); + NON_MOVEABLE(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; + } + }; + +} diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index 3269396f4..e89897a5c 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #include +#include "ldr_auto_close.hpp" #include "ldr_capabilities.hpp" #include "ldr_content_management.hpp" #include "ldr_development_manager.hpp" @@ -367,6 +368,63 @@ namespace ams::ldr { return ResultSuccess(); } + 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, svc::ResultOutOfMemory()); + + /* 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, svc::ResultOutOfMemory()); + 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, svc::ResultOutOfMemory()); + 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.addr + mem_info.size) - address)) { + *out = address; + return ResultSuccess(); + } + + /* Check that we can advance. */ + R_UNLESS(address < mem_info.addr + mem_info.size, svc::ResultOutOfMemory()); + R_UNLESS(mem_info.addr + mem_info.size - 1 < aslr_start + aslr_size - 1, svc::ResultOutOfMemory()); + + /* Advance. */ + address = mem_info.addr + mem_info.size; + } + } + Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const bool *has_nso, const args::ArgumentInfo *arg_info) { /* Clear output. */ out->args_address = 0; @@ -401,39 +459,39 @@ namespace ams::ldr { /* Calculate ASLR. */ uintptr_t aslr_start = 0; - uintptr_t aslr_size = 0; + size_t aslr_size = 0; if (hos::GetVersion() >= hos::Version_2_0_0) { switch (out_param->flags & svc::CreateProcessFlag_AddressSpaceMask) { case svc::CreateProcessFlag_AddressSpace32Bit: case svc::CreateProcessFlag_AddressSpace32BitWithoutAlias: - aslr_start = map::AslrBase32Bit; - aslr_size = map::AslrSize32Bit; + aslr_start = svc::AddressSmallMap32Start; + aslr_size = svc::AddressSmallMap32Size; break; case svc::CreateProcessFlag_AddressSpace64BitDeprecated: - aslr_start = map::AslrBase64BitDeprecated; - aslr_size = map::AslrSize64BitDeprecated; + aslr_start = svc::AddressSmallMap36Start; + aslr_size = svc::AddressSmallMap36Size; break; case svc::CreateProcessFlag_AddressSpace64Bit: - aslr_start = map::AslrBase64Bit; - aslr_size = map::AslrSize64Bit; + aslr_start = svc::AddressMap39Start; + aslr_size = svc::AddressMap39Size; break; AMS_UNREACHABLE_DEFAULT_CASE(); } } else { /* On 1.0.0, only 2 address space types existed. */ if (out_param->flags & svc::CreateProcessFlag_AddressSpace64BitDeprecated) { - aslr_start = map::AslrBase64BitDeprecated; - aslr_size = map::AslrSize64BitDeprecated; + aslr_start = svc::AddressSmallMap36Start; + aslr_size = svc::AddressSmallMap36Size; } else { - aslr_start = map::AslrBase32Bit; - aslr_size = map::AslrSize32Bit; + aslr_start = svc::AddressSmallMap32Start; + aslr_size = svc::AddressSmallMap32Size; } } R_UNLESS(total_size <= aslr_size, svc::ResultOutOfMemory()); /* Set Create Process output. */ uintptr_t aslr_slide = 0; - uintptr_t free_size = (aslr_size - total_size); + size_t free_size = (aslr_size - total_size); if (out_param->flags & svc::CreateProcessFlag_EnableAslr) { /* Nintendo uses MT19937 (not os::GenerateRandomBytes), but we'll just use TinyMT for now. */ aslr_slide = os::GenerateRandomU64(free_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize; @@ -450,7 +508,7 @@ namespace ams::ldr { out->args_address += aslr_start; } - out_param->code_address = aslr_start; + out_param->code_address = aslr_start; out_param->code_num_pages = total_size >> 12; return ResultSuccess(); @@ -510,8 +568,8 @@ namespace ams::ldr { Result LoadNsoIntoProcessMemory(os::NativeHandle process_handle, fs::FileHandle file, uintptr_t map_address, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) { /* Map and read data from file. */ { - map::AutoCloseMap mapper(map_address, process_handle, nso_address, nso_size); - R_TRY(mapper.GetResult()); + AutoCloseMap map(map_address, process_handle, nso_address, nso_size); + R_TRY(map.GetResult()); /* Load NSO segments. */ R_TRY(LoadNsoSegment(file, &nso_header->segments[NsoHeader::Segment_Text], nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0, @@ -562,8 +620,8 @@ namespace ams::ldr { R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read)); ON_SCOPE_EXIT { fs::CloseFile(file); }; - uintptr_t map_address = 0; - R_TRY(map::LocateMappableSpace(&map_address, process_info->nso_size[i])); + uintptr_t map_address; + R_TRY(SearchFreeRegion(std::addressof(map_address), process_info->nso_size[i])); R_TRY(LoadNsoIntoProcessMemory(process_info->process_handle, file, map_address, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i])); } @@ -573,11 +631,11 @@ namespace ams::ldr { if (arg_info != nullptr) { /* Write argument data into memory. */ { - uintptr_t map_address = 0; - R_TRY(map::LocateMappableSpace(&map_address, process_info->args_size)); + uintptr_t map_address; + R_TRY(SearchFreeRegion(std::addressof(map_address), process_info->args_size)); - map::AutoCloseMap mapper(map_address, process_info->process_handle, process_info->args_address, process_info->args_size); - R_TRY(mapper.GetResult()); + AutoCloseMap map(map_address, process_info->process_handle, process_info->args_address, process_info->args_size); + R_TRY(map.GetResult()); ProgramArguments *args = reinterpret_cast(map_address); std::memset(args, 0, sizeof(*args)); diff --git a/libraries/libstratosphere/include/stratosphere/map.hpp b/stratosphere/ro/source/impl/ro_auto_close.hpp similarity index 88% rename from libraries/libstratosphere/include/stratosphere/map.hpp rename to stratosphere/ro/source/impl/ro_auto_close.hpp index 5487caa80..e81abca72 100644 --- a/libraries/libstratosphere/include/stratosphere/map.hpp +++ b/stratosphere/ro/source/impl/ro_auto_close.hpp @@ -13,8 +13,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once +#include -#include -#include +namespace ams::ro::impl { + +} diff --git a/stratosphere/ro/source/impl/ro_map_utils.cpp b/stratosphere/ro/source/impl/ro_map_utils.cpp new file mode 100644 index 000000000..335e62211 --- /dev/null +++ b/stratosphere/ro/source/impl/ro_map_utils.cpp @@ -0,0 +1,83 @@ +/* + * 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.addr + mem_info.size) - address)) { + *out = address; + return ResultSuccess(); + } + + /* Check that we can advance. */ + R_UNLESS(address < mem_info.addr + mem_info.size, ro::ResultOutOfAddressSpace()); + R_UNLESS(mem_info.addr + mem_info.size - 1 < aslr_start + aslr_size - 1, ro::ResultOutOfAddressSpace()); + + /* Advance. */ + address = mem_info.addr + mem_info.size; + } + } + +} diff --git a/stratosphere/ro/source/impl/ro_map_utils.hpp b/stratosphere/ro/source/impl/ro_map_utils.hpp new file mode 100644 index 000000000..290fb4364 --- /dev/null +++ b/stratosphere/ro/source/impl/ro_map_utils.hpp @@ -0,0 +1,176 @@ +/* + * 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, svc::PseudoHandle::CurrentProcess, 0)); + R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_heap_size), svc::InfoType_HeapRegionSize, svc::PseudoHandle::CurrentProcess, 0)); + R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_alias_start), svc::InfoType_AliasRegionAddress, svc::PseudoHandle::CurrentProcess, 0)); + R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_alias_size), svc::InfoType_AliasRegionSize, svc::PseudoHandle::CurrentProcess, 0)); + R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_aslr_start), svc::InfoType_AslrRegionAddress, svc::PseudoHandle::CurrentProcess, 0)); + R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_aslr_size), svc::InfoType_AslrRegionSize, svc::PseudoHandle::CurrentProcess, 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. */ + } + } + + /* 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.addr <= 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.addr + 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(svc::Handle 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 7be09d809..e8dd2689b 100644 --- a/stratosphere/ro/source/impl/ro_nro_utils.cpp +++ b/stratosphere/ro/source/impl/ro_nro_utils.cpp @@ -15,53 +15,63 @@ */ #include #include "ro_nro_utils.hpp" +#include "ro_map_utils.hpp" namespace ams::ro::impl { - namespace { - - constexpr size_t MaxMapRetries = 0x200; - - } - 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) { - map::MappedCodeMemory nro_mcm(ResultInternalError{}); - map::MappedCodeMemory bss_mcm(ResultInternalError{}); + /* 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()); - /* Map the NRO, and map the BSS immediately after it. */ - size_t i; - for (i = 0; i < MaxMapRetries; i++) { - map::MappedCodeMemory tmp_nro_mcm(ResultInternalError{}); - R_TRY(map::MapCodeMemoryInProcess(tmp_nro_mcm, process_handle, nro_heap_address, nro_heap_size)); - base_address = tmp_nro_mcm.GetDstAddress(); - - if (bss_heap_size > 0) { - map::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; - } + /* 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; - if (!map::CanAddGuardRegionsInProcess(process_handle, base_address, nro_heap_size + bss_heap_size)) { - continue; + /* 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; + + /* 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; + } } - bss_mcm = std::move(tmp_bss_mcm); - } else { - if (!map::CanAddGuardRegionsInProcess(process_handle, base_address, nro_heap_size)) { - continue; - } + /* We succeeded, so save the code memory. */ + nro_mcm = std::move(tmp_nro_mcm); + break; } - nro_mcm = std::move(tmp_nro_mcm); - break; - } - R_UNLESS(i < MaxMapRetries, ResultOutOfAddressSpace()); - /* Invalidation here actually prevents them from unmapping at scope exit. */ - nro_mcm.Invalidate(); - bss_mcm.Invalidate(); + R_UNLESS(i != RetrySearchCount, ro::ResultOutOfAddressSpace()); + } + + /* Cancel the automatic closing of our mappings. */ + nro_mcm.Cancel(); + bss_mcm.Cancel(); *out_base_address = base_address; return ResultSuccess(); diff --git a/stratosphere/ro/source/impl/ro_nrr_utils.cpp b/stratosphere/ro/source/impl/ro_nrr_utils.cpp index d305e31a5..1f5685e76 100644 --- a/stratosphere/ro/source/impl/ro_nrr_utils.cpp +++ b/stratosphere/ro/source/impl/ro_nrr_utils.cpp @@ -15,6 +15,7 @@ */ #include #include "ro_nrr_utils.hpp" +#include "ro_map_utils.hpp" #include "ro_service_impl.hpp" namespace ams::ro::impl { @@ -135,7 +136,7 @@ namespace ams::ro::impl { R_UNLESS(crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size), ro::ResultNotAuthorized()); /* Check ProgramId pattern is valid. */ - R_UNLESS(header->IsProgramIdValid(), ResultNotAuthorized()); + R_UNLESS(header->IsProgramIdValid(), ro::ResultNotAuthorized()); return ResultSuccess(); } @@ -159,10 +160,10 @@ namespace ams::ro::impl { Result ValidateNrr(const NrrHeader *header, u64 size, ncm::ProgramId program_id, NrrKind nrr_kind, bool enforce_nrr_kind) { /* Check magic. */ - R_UNLESS(header->IsMagicValid(), ResultInvalidNrr()); + R_UNLESS(header->IsMagicValid(), ro::ResultInvalidNrr()); /* Check size. */ - R_UNLESS(header->GetSize() == size, ResultInvalidSize()); + R_UNLESS(header->GetSize() == size, ro::ResultInvalidSize()); /* Only perform checks if we must. */ const bool ease_nro_restriction = ShouldEaseNroRestriction(); @@ -180,11 +181,11 @@ namespace ams::ro::impl { R_TRY(ValidateNrrSignature(header)); /* Check program id. */ - R_UNLESS(header->GetProgramId() == program_id, ResultInvalidNrr()); + R_UNLESS(header->GetProgramId() == program_id, ro::ResultInvalidNrr()); /* Check nrr kind. */ if (hos::GetVersion() >= hos::Version_7_0_0 && enforce_nrr_kind) { - R_UNLESS(header->GetNrrKind() == nrr_kind, ResultInvalidNrrKind()); + R_UNLESS(header->GetNrrKind() == nrr_kind, ro::ResultInvalidNrrKind()); } } @@ -195,30 +196,55 @@ 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) { - map::MappedCodeMemory nrr_mcm(ResultInternalError{}); + /* 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()); - /* First, map the NRR. */ - R_TRY(map::MapCodeMemoryInProcess(nrr_mcm, process_handle, nrr_heap_address, nrr_heap_size)); + /* 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; - const u64 code_address = nrr_mcm.GetDstAddress(); + /* Check that we can have guard spaces. */ + if (!region_info.CanEmplaceGuardSpaces(process_handle, code_address, nrr_heap_size)) { + continue; + } + + /* We succeeded, so save the code memory. */ + nrr_mcm = std::move(tmp_mcm); + break; + } + + R_UNLESS(i != RetrySearchCount, ro::ResultOutOfAddressSpace()); + } + + /* Decide where to map the NRR in our process. */ uintptr_t map_address; - R_UNLESS(R_SUCCEEDED(map::LocateMappableSpace(&map_address, nrr_heap_size)), ResultOutOfAddressSpace()); + R_UNLESS(R_SUCCEEDED(SearchFreeRegion(std::addressof(map_address), nrr_heap_size)), ro::ResultOutOfAddressSpace()); - /* Nintendo...does not check the return value of this map. We will check, instead of aborting if it fails. */ - map::AutoCloseMap nrr_map(map_address, process_handle, code_address, nrr_heap_size); + /* 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); R_TRY(ValidateNrr(nrr_header, nrr_heap_size, program_id, nrr_kind, enforce_nrr_kind)); - /* Invalidation here actually prevents them from unmapping at scope exit. */ - nrr_map.Invalidate(); - nrr_mcm.Invalidate(); + /* Cancel the automatic closing of our mappings. */ + nrr_map.Cancel(); + nrr_mcm.Cancel(); /* Save a copy of the hash that we verified. */ crypto::GenerateSha256Hash(out_hash, out_hash_size, nrr_header->GetSignedArea(), nrr_header->GetSignedAreaSize()); - *out_header = nrr_header; + *out_header = nrr_header; *out_mapped_code_address = code_address; return ResultSuccess(); } diff --git a/stratosphere/ro/source/impl/ro_service_impl.cpp b/stratosphere/ro/source/impl/ro_service_impl.cpp index cb3f5a085..3371fdeae 100644 --- a/stratosphere/ro/source/impl/ro_service_impl.cpp +++ b/stratosphere/ro/source/impl/ro_service_impl.cpp @@ -14,6 +14,7 @@ * 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" @@ -99,7 +100,7 @@ namespace ams::ro::impl { return ResultSuccess(); } } - return ResultNotRegistered(); + return ro::ResultNotRegistered(); } Result GetFreeNrrInfo(NrrInfo **out) { @@ -111,7 +112,7 @@ namespace ams::ro::impl { return ResultSuccess(); } } - return ResultTooManyNrr(); + return ro::ResultTooManyNrr(); } Result GetNroInfoByAddress(NroInfo **out, u64 nro_address) { @@ -123,7 +124,7 @@ namespace ams::ro::impl { return ResultSuccess(); } } - return ResultNotLoaded(); + return ro::ResultNotLoaded(); } Result GetNroInfoByModuleId(NroInfo **out, const ModuleId *module_id) { @@ -135,7 +136,7 @@ namespace ams::ro::impl { return ResultSuccess(); } } - return ResultNotLoaded(); + return ro::ResultNotLoaded(); } Result GetFreeNroInfo(NroInfo **out) { @@ -147,7 +148,7 @@ namespace ams::ro::impl { return ResultSuccess(); } } - return ResultTooManyNro(); + return ro::ResultTooManyNro(); } Result ValidateHasNroHash(const NroHeader *nro_header) const { @@ -192,21 +193,21 @@ namespace ams::ro::impl { return ResultSuccess(); } - return ResultNotAuthorized(); + return ro::ResultNotAuthorized(); } 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(map::LocateMappableSpace(&map_address, expected_nro_size)), ResultOutOfAddressSpace()); + R_UNLESS(R_SUCCEEDED(SearchFreeRegion(std::addressof(map_address), expected_nro_size)), ro::ResultOutOfAddressSpace()); /* Actually map the NRO. */ - map::AutoCloseMap nro_map(map_address, this->process_handle, base_address, expected_nro_size); + AutoCloseMap nro_map(map_address, this->process_handle, base_address, expected_nro_size); R_TRY(nro_map.GetResult()); /* Validate header. */ const NroHeader *header = reinterpret_cast(map_address); - R_UNLESS(header->IsMagicValid(), ResultInvalidNro()); + R_UNLESS(header->IsMagicValid(), ro::ResultInvalidNro()); /* Read sizes from header. */ const u64 nro_size = header->GetSize(); @@ -219,31 +220,31 @@ namespace ams::ro::impl { const u64 bss_size = header->GetBssSize(); /* Validate sizes meet expected. */ - R_UNLESS(nro_size == expected_nro_size, ResultInvalidNro()); - R_UNLESS(bss_size == expected_bss_size, ResultInvalidNro()); + R_UNLESS(nro_size == expected_nro_size, ro::ResultInvalidNro()); + R_UNLESS(bss_size == expected_bss_size, ro::ResultInvalidNro()); /* Validate all sizes are aligned. */ - R_UNLESS(util::IsAligned(text_size, os::MemoryPageSize), ResultInvalidNro()); - R_UNLESS(util::IsAligned(ro_size, os::MemoryPageSize), ResultInvalidNro()); - R_UNLESS(util::IsAligned(rw_size, os::MemoryPageSize), ResultInvalidNro()); - R_UNLESS(util::IsAligned(bss_size, os::MemoryPageSize), ResultInvalidNro()); + R_UNLESS(util::IsAligned(text_size, os::MemoryPageSize), ro::ResultInvalidNro()); + R_UNLESS(util::IsAligned(ro_size, os::MemoryPageSize), ro::ResultInvalidNro()); + R_UNLESS(util::IsAligned(rw_size, os::MemoryPageSize), ro::ResultInvalidNro()); + R_UNLESS(util::IsAligned(bss_size, os::MemoryPageSize), ro::ResultInvalidNro()); /* Validate sections are in order. */ - R_UNLESS(text_ofs <= ro_ofs, ResultInvalidNro()); - R_UNLESS(ro_ofs <= rw_ofs, ResultInvalidNro()); + R_UNLESS(text_ofs <= ro_ofs, ro::ResultInvalidNro()); + R_UNLESS(ro_ofs <= rw_ofs, ro::ResultInvalidNro()); /* Validate sections are sequential and contiguous. */ - R_UNLESS(text_ofs == 0, ResultInvalidNro()); - R_UNLESS(text_ofs + text_size == ro_ofs, ResultInvalidNro()); - R_UNLESS(ro_ofs + ro_size == rw_ofs, ResultInvalidNro()); - R_UNLESS(rw_ofs + rw_size == nro_size, ResultInvalidNro()); + R_UNLESS(text_ofs == 0, ro::ResultInvalidNro()); + R_UNLESS(text_ofs + text_size == ro_ofs, ro::ResultInvalidNro()); + R_UNLESS(ro_ofs + ro_size == rw_ofs, ro::ResultInvalidNro()); + R_UNLESS(rw_ofs + rw_size == nro_size, ro::ResultInvalidNro()); /* Verify NRO hash. */ R_TRY(this->ValidateHasNroHash(header)); /* Check if NRO has already been loaded. */ const ModuleId *module_id = header->GetModuleId(); - R_UNLESS(R_FAILED(this->GetNroInfoByModuleId(nullptr, module_id)), ResultAlreadyLoaded()); + R_UNLESS(R_FAILED(this->GetNroInfoByModuleId(nullptr, module_id)), ro::ResultAlreadyLoaded()); /* Apply patches to NRO. */ LocateAndApplyIpsPatchesToModule(module_id, reinterpret_cast(map_address), nro_size); @@ -327,17 +328,17 @@ namespace ams::ro::impl { } constexpr inline Result ValidateAddressAndNonZeroSize(u64 address, u64 size) { - R_UNLESS(util::IsAligned(address, os::MemoryPageSize), ResultInvalidAddress()); - R_UNLESS(size != 0, ResultInvalidSize()); - R_UNLESS(util::IsAligned(size, os::MemoryPageSize), ResultInvalidSize()); - R_UNLESS(address < address + size, ResultInvalidSize()); + R_UNLESS(util::IsAligned(address, os::MemoryPageSize), ro::ResultInvalidAddress()); + R_UNLESS(size != 0, ro::ResultInvalidSize()); + R_UNLESS(util::IsAligned(size, os::MemoryPageSize), ro::ResultInvalidSize()); + R_UNLESS(address < address + size, ro::ResultInvalidSize()); return ResultSuccess(); } constexpr inline Result ValidateAddressAndSize(u64 address, u64 size) { - R_UNLESS(util::IsAligned(address, os::MemoryPageSize), ResultInvalidAddress()); - R_UNLESS(util::IsAligned(size, os::MemoryPageSize), ResultInvalidSize()); - R_UNLESS(size == 0 || address < address + size, ResultInvalidSize()); + R_UNLESS(util::IsAligned(address, os::MemoryPageSize), ro::ResultInvalidAddress()); + R_UNLESS(util::IsAligned(size, os::MemoryPageSize), ro::ResultInvalidSize()); + R_UNLESS(size == 0 || address < address + size, ro::ResultInvalidSize()); return ResultSuccess(); } @@ -378,14 +379,14 @@ namespace ams::ro::impl { { /* Validate handle is a valid process handle. */ os::ProcessId handle_pid; - R_UNLESS(R_SUCCEEDED(os::GetProcessId(&handle_pid, process_handle.GetOsHandle())), ResultInvalidProcess()); + R_UNLESS(R_SUCCEEDED(os::GetProcessId(&handle_pid, process_handle.GetOsHandle())), ro::ResultInvalidProcess()); /* Validate process id. */ - R_UNLESS(handle_pid == process_id, ResultInvalidProcess()); + R_UNLESS(handle_pid == process_id, ro::ResultInvalidProcess()); } /* Check if a process context already exists. */ - R_UNLESS(GetContextByProcessId(process_id) == nullptr, ResultInvalidSession()); + R_UNLESS(GetContextByProcessId(process_id) == nullptr, ro::ResultInvalidSession()); /* Allocate a context to manage the process handle. */ *out_context_id = AllocateContext(process_handle.GetOsHandle(), process_id); @@ -396,8 +397,8 @@ namespace ams::ro::impl { Result ValidateProcess(size_t context_id, os::ProcessId process_id) { const ProcessContext *ctx = GetContextById(context_id); - R_UNLESS(ctx != nullptr, ResultInvalidProcess()); - R_UNLESS(ctx->process_id == process_id, ResultInvalidProcess()); + R_UNLESS(ctx != nullptr, ro::ResultInvalidProcess()); + R_UNLESS(ctx->process_id == process_id, ro::ResultInvalidProcess()); return ResultSuccess(); } @@ -453,7 +454,7 @@ namespace ams::ro::impl { AMS_ABORT_UNLESS(context != nullptr); /* Validate address. */ - R_UNLESS(util::IsAligned(nrr_address, os::MemoryPageSize), ResultInvalidAddress()); + R_UNLESS(util::IsAligned(nrr_address, os::MemoryPageSize), ro::ResultInvalidAddress()); /* Check the NRR is loaded. */ NrrInfo *nrr_info = nullptr; @@ -479,8 +480,8 @@ namespace ams::ro::impl { R_TRY(ValidateAddressAndSize(bss_address, bss_size)); const u64 total_size = nro_size + bss_size; - R_UNLESS(total_size >= nro_size, ResultInvalidSize()); - R_UNLESS(total_size >= bss_size, ResultInvalidSize()); + R_UNLESS(total_size >= nro_size, ro::ResultInvalidSize()); + R_UNLESS(total_size >= bss_size, ro::ResultInvalidSize()); /* Check we have space for a new NRO. */ NroInfo *nro_info = nullptr; @@ -521,7 +522,7 @@ namespace ams::ro::impl { AMS_ABORT_UNLESS(context != nullptr); /* Validate address. */ - R_UNLESS(util::IsAligned(nro_address, os::MemoryPageSize), ResultInvalidAddress()); + R_UNLESS(util::IsAligned(nro_address, os::MemoryPageSize), ro::ResultInvalidAddress()); /* Check the NRO is loaded. */ NroInfo *nro_info = nullptr; diff --git a/stratosphere/ro/source/ro_debug_monitor_service.cpp b/stratosphere/ro/source/ro_debug_monitor_service.cpp index 3325381bc..7f2bc42e4 100644 --- a/stratosphere/ro/source/ro_debug_monitor_service.cpp +++ b/stratosphere/ro/source/ro_debug_monitor_service.cpp @@ -20,7 +20,7 @@ namespace ams::ro { Result DebugMonitorService::GetProcessModuleInfo(sf::Out out_count, const sf::OutArray &out_infos, os::ProcessId process_id) { - R_UNLESS(out_infos.GetSize() <= std::numeric_limits::max(), ResultInvalidSize()); + R_UNLESS(out_infos.GetSize() <= std::numeric_limits::max(), ro::ResultInvalidSize()); return impl::GetProcessModuleInfo(out_count.GetPointer(), out_infos.GetPointer(), out_infos.GetSize(), process_id); }