From 1ea49bdae3573c03d316c9918390c455c3018ef8 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 20 Nov 2020 17:48:58 -0800 Subject: [PATCH] os: do not use deprecated libnx ::virtmemReserve api --- .../os/impl/os_address_space_allocator.cpp | 86 +++++++++++++++++++ ...ddress_space_allocator_impl.os.horizon.hpp | 31 +++++++ .../impl/os_address_space_allocator_types.hpp | 44 ++++++++++ .../source/os/impl/os_aslr_space_manager.hpp | 27 ++++++ .../os_aslr_space_manager_impl.os.horizon.hpp | 85 ++++++++++++++++++ .../os/impl/os_aslr_space_manager_types.hpp | 54 ++++++++++++ .../source/os/impl/os_resource_manager.hpp | 3 + .../source/os/os_transfer_memory_api.cpp | 13 ++- 8 files changed, 340 insertions(+), 3 deletions(-) create mode 100644 libraries/libstratosphere/source/os/impl/os_address_space_allocator.cpp create mode 100644 libraries/libstratosphere/source/os/impl/os_address_space_allocator_impl.os.horizon.hpp create mode 100644 libraries/libstratosphere/source/os/impl/os_address_space_allocator_types.hpp create mode 100644 libraries/libstratosphere/source/os/impl/os_aslr_space_manager.hpp create mode 100644 libraries/libstratosphere/source/os/impl/os_aslr_space_manager_impl.os.horizon.hpp create mode 100644 libraries/libstratosphere/source/os/impl/os_aslr_space_manager_types.hpp diff --git a/libraries/libstratosphere/source/os/impl/os_address_space_allocator.cpp b/libraries/libstratosphere/source/os/impl/os_address_space_allocator.cpp new file mode 100644 index 000000000..7209bc96b --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_address_space_allocator.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018-2020 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_address_space_allocator_types.hpp" +#include "os_rng_manager.hpp" + +namespace ams::os::impl { + + AddressSpaceAllocator::AddressSpaceAllocator(uintptr_t sa, uintptr_t ea, size_t gsz) : cs() { + /* Check preconditions. */ + AMS_ASSERT(sa >= gsz); + AMS_ASSERT(ea + gsz >= ea); + + this->guard_page_count = util::DivideUp(gsz, MemoryPageSize); + this->start_page = sa / MemoryPageSize; + this->end_page = (ea / MemoryPageSize) + this->guard_page_count; + } + + bool AddressSpaceAllocator::GetNextNonOverlappedNodeUnsafe(uintptr_t page, size_t page_num) { + AMS_ASSERT(page < (page + page_num)); + + return CheckFreeSpace(page * MemoryPageSize, page_num * MemoryPageSize); + } + + void *AddressSpaceAllocator::AllocateSpace(size_t size, size_t align) { + /* Check pre-conditions. */ + AMS_ASSERT(align > 0); + + /* Determine the page count. */ + const size_t page_count = util::DivideUp(size, MemoryPageSize); + + /* Determine the alignment pages. */ + AMS_ASSERT(util::IsAligned(align, MemoryPageSize)); + const size_t align_page_count = align / MemoryPageSize; + + /* Check the page count. */ + if (page_count > this->end_page - this->guard_page_count) { + return nullptr; + } + + /* Determine the range to look in. */ + const uintptr_t rand_start = util::DivideUp(this->start_page + this->guard_page_count, align_page_count); + const uintptr_t rand_end = (this->end_page - page_count - this->guard_page_count) / align_page_count; + + /* Check that we can find a space. */ + if (rand_start > rand_end) { + return nullptr; + } + + /* Try to find space up to 512 times. */ + for (size_t i = 0; i < 512; ++i) { + /* Acquire exclusive access before doing calculations. */ + std::scoped_lock lk(this->cs); + + /* Determine a random page. */ + const uintptr_t target = ((GetRngManager().GenerateRandomU64() % (rand_end - rand_start + 1)) + rand_start) * align_page_count; + AMS_ASSERT(this->start_page <= target - this->guard_page_count && target + page_count + this->guard_page_count <= this->end_page); + + /* If the page is valid, use it. */ + if (GetNextNonOverlappedNodeUnsafe(target - this->guard_page_count, page_count + 2 * this->guard_page_count)) { + return reinterpret_cast(target * MemoryPageSize); + } + } + + /* We failed to find space. */ + return nullptr; + } + + bool AddressSpaceAllocator::CheckGuardSpace(uintptr_t address, size_t size, size_t guard_size) { + return CheckFreeSpace(address - guard_size, guard_size) && CheckFreeSpace(address + size, guard_size); + } + +} 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 new file mode 100644 index 000000000..925fc17fe --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_address_space_allocator_impl.os.horizon.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 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::os::impl { + + inline bool CheckFreeSpace(uintptr_t address, size_t size) { + /* Query the memory. */ + svc::MemoryInfo memory_info; + svc::PageInfo page_info; + const auto result = svc::QueryMemory(std::addressof(memory_info), std::addressof(page_info), address); + AMS_ASSERT(R_SUCCEEDED(result)); + + return memory_info.state == svc::MemoryState_Free && address + size <= memory_info.addr + memory_info.size; + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_address_space_allocator_types.hpp b/libraries/libstratosphere/source/os/impl/os_address_space_allocator_types.hpp new file mode 100644 index 000000000..ff602b115 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_address_space_allocator_types.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 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 + +#ifdef ATMOSPHERE_OS_HORIZON + #include "os_address_space_allocator_impl.os.horizon.hpp" +#else + #error "Unknown OS for AddressSpaceAllocatorImpl" +#endif + +namespace ams::os::impl { + + class AddressSpaceAllocator { + NON_COPYABLE(AddressSpaceAllocator); + NON_MOVEABLE(AddressSpaceAllocator); + private: + InternalCriticalSection cs; + uintptr_t start_page; + uintptr_t end_page; + size_t guard_page_count; + private: + bool GetNextNonOverlappedNodeUnsafe(uintptr_t page, size_t page_num); + public: + AddressSpaceAllocator(uintptr_t sa, uintptr_t ea, size_t gsz); + + void *AllocateSpace(size_t size, size_t align); + bool CheckGuardSpace(uintptr_t address, size_t size, size_t guard_size); + }; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_aslr_space_manager.hpp b/libraries/libstratosphere/source/os/impl/os_aslr_space_manager.hpp new file mode 100644 index 000000000..224b0107f --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_aslr_space_manager.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 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 "os_aslr_space_manager_types.hpp" +#include "os_resource_manager.hpp" + +namespace ams::os::impl { + + ALWAYS_INLINE AslrSpaceManager &GetAslrSpaceManager() { + return GetResourceManager().GetAslrSpaceManager(); + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_impl.os.horizon.hpp b/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_impl.os.horizon.hpp new file mode 100644 index 000000000..ca1a7a4cd --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_impl.os.horizon.hpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018-2020 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::os::impl { + + class AslrSpaceManagerHorizonImpl { + NON_COPYABLE(AslrSpaceManagerHorizonImpl); + NON_MOVEABLE(AslrSpaceManagerHorizonImpl); + private: + static constexpr u64 AslrBase32Bit = 0x0000200000ul; + static constexpr u64 AslrSize32Bit = 0x003FE00000ul; + static constexpr u64 AslrBase64BitDeprecated = 0x0008000000ul; + static constexpr u64 AslrSize64BitDeprecated = 0x0078000000ul; + static constexpr u64 AslrBase64Bit = 0x0008000000ul; + static constexpr u64 AslrSize64Bit = 0x7FF8000000ul; + private: + static u64 GetAslrInfo(svc::InfoType type) { + u64 value; + R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), type, svc::PseudoHandle::CurrentProcess, 0)); + static_assert(std::same_as); + AMS_ASSERT(value <= std::numeric_limits::max()); + return static_cast(value & std::numeric_limits::max()); + } + public: + constexpr AslrSpaceManagerHorizonImpl() = default; + + static u64 GetHeapSpaceBeginAddress() { + return GetAslrInfo(svc::InfoType_HeapRegionAddress); + } + + static u64 GetHeapSpaceBeginSize() { + return GetAslrInfo(svc::InfoType_HeapRegionSize); + } + + static u64 GetAliasSpaceBeginAddress() { + return GetAslrInfo(svc::InfoType_AliasRegionAddress); + } + + static u64 GetAliasSpaceBeginSize() { + return GetAslrInfo(svc::InfoType_AliasRegionSize); + } + + static u64 GetAslrSpaceBeginAddress() { + if (hos::GetVersion() >= hos::Version_2_0_0 || svc::IsKernelMesosphere()) { + return GetAslrInfo(svc::InfoType_AslrRegionAddress); + } else { + if (GetHeapSpaceBeginAddress() < AslrBase64BitDeprecated || GetAliasSpaceBeginAddress() < AslrBase64BitDeprecated) { + return AslrBase32Bit; + } else { + return AslrBase64BitDeprecated; + } + } + } + + static u64 GetAslrSpaceEndAddress() { + if (hos::GetVersion() >= hos::Version_2_0_0 || svc::IsKernelMesosphere()) { + return GetAslrInfo(svc::InfoType_AslrRegionAddress) + GetAslrInfo(svc::InfoType_AslrRegionSize); + } else { + if (GetHeapSpaceBeginAddress() < AslrBase64BitDeprecated || GetAliasSpaceBeginAddress() < AslrBase64BitDeprecated) { + return AslrBase32Bit + AslrSize32Bit; + } else { + return AslrBase64BitDeprecated + AslrSize64BitDeprecated; + } + } + } + }; + + using AslrSpaceManagerImpl = AslrSpaceManagerHorizonImpl; + +} 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 new file mode 100644 index 000000000..19a51a9f2 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_aslr_space_manager_types.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018-2020 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 "os_address_space_allocator_types.hpp" + +#ifdef ATMOSPHERE_OS_HORIZON + #include "os_aslr_space_manager_impl.os.horizon.hpp" +#else + #error "Unknown OS for AslrSpaceManagerImpl" +#endif + +namespace ams::os::impl { + + constexpr inline size_t AslrSpaceLargeAlign = 2_MB; + constexpr inline size_t AslrSpaceGuardSize = 4 * MemoryPageSize; + + class AslrSpaceManager { + NON_COPYABLE(AslrSpaceManager); + NON_MOVEABLE(AslrSpaceManager); + private: + AddressSpaceAllocator allocator; + AslrSpaceManagerImpl impl; + public: + AslrSpaceManager() : allocator(impl.GetAslrSpaceBeginAddress(), impl.GetAslrSpaceEndAddress(), AslrSpaceGuardSize) { /* ... */ } + + void *AllocateSpace(size_t size) { + void *address = this->allocator.AllocateSpace(size, AslrSpaceLargeAlign); + if (address == nullptr) { + address = this->allocator.AllocateSpace(size, MemoryPageSize); + } + return address; + } + + bool CheckGuardSpace(uintptr_t address, size_t size) { + return this->allocator.CheckGuardSpace(address, size, AslrSpaceGuardSize); + } + }; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp index 473b3ddb6..b79babfbf 100644 --- a/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp +++ b/libraries/libstratosphere/source/os/impl/os_resource_manager.hpp @@ -18,12 +18,14 @@ #include "os_rng_manager_impl.hpp" #include "os_thread_manager_types.hpp" #include "os_tick_manager_impl.hpp" +#include "os_aslr_space_manager_types.hpp" namespace ams::os::impl { class OsResourceManager { private: RngManager rng_manager{}; + AslrSpaceManager aslr_space_manager; /* TODO */ ThreadManager thread_manager{}; /* TODO */ @@ -33,6 +35,7 @@ namespace ams::os::impl { OsResourceManager() = default; constexpr ALWAYS_INLINE RngManager &GetRngManager() { return this->rng_manager; } + constexpr ALWAYS_INLINE AslrSpaceManager &GetAslrSpaceManager() { return this->aslr_space_manager; } constexpr ALWAYS_INLINE ThreadManager &GetThreadManager() { return this->thread_manager; } constexpr ALWAYS_INLINE TickManager &GetTickManager() { return this->tick_manager; } }; diff --git a/libraries/libstratosphere/source/os/os_transfer_memory_api.cpp b/libraries/libstratosphere/source/os/os_transfer_memory_api.cpp index 747e1492e..e2b438bcc 100644 --- a/libraries/libstratosphere/source/os/os_transfer_memory_api.cpp +++ b/libraries/libstratosphere/source/os/os_transfer_memory_api.cpp @@ -16,6 +16,8 @@ #include #include "impl/os_thread_manager.hpp" #include "impl/os_transfer_memory_impl.hpp" +#include "impl/os_aslr_space_manager_types.hpp" +#include "impl/os_aslr_space_manager.hpp" namespace ams::os { @@ -131,7 +133,7 @@ namespace ams::os { for (int i = 0; i < 64; ++i) { /* Reserve space to map the memory. */ /* TODO: os::AslrSpaceManager */ - void *map_address = ::virtmemReserve(tmem->size); + void *map_address = impl::GetAslrSpaceManager().AllocateSpace(tmem->size); R_UNLESS(map_address != nullptr, os::ResultOutOfAddressSpace()); /* Mark allocated. */ @@ -144,8 +146,13 @@ namespace ams::os { R_CATCH(os::ResultInvalidCurrentMemoryState) { continue; } } R_END_TRY_CATCH; - /* TODO: Check guard space via aslr manager. */ - if (false /* !impl::GetAslrSpaceManager()->CheckGuardSpace(reinterpret_cast(tmem->address), tmem->size) */) { + /* Check guard space via aslr manager. */ + if (!impl::GetAslrSpaceManager().CheckGuardSpace(reinterpret_cast(tmem->address), tmem->size)) { + /* NOTE: Nintendo bug here. If this case occurs, they will return ResultSuccess() without actually mapping the transfer memory. */ + /* This is because they basically do if (!os::ResultInvalidCurrentMemoryState::Includes(result)) { return result; }, and */ + /* ResultSuccess() is not included by ResultInvalidCurrentMemoryState. */ + + /* We will do better than them, and will not falsely return ResultSuccess(). */ impl::TransferMemoryImpl::Unmap(tmem->handle, tmem->address, tmem->size); continue; }