From 60b5bd73b7546c38a49b81ac7b4e6f40c0ec2acc Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 09:48:25 -0700 Subject: [PATCH] kern: track mapped ipc server memory in page table --- .../arch/arm64/kern_k_page_table.hpp | 2 +- .../arch/arm64/kern_k_process_page_table.hpp | 8 ++--- .../mesosphere/kern_k_page_table_base.hpp | 8 +++-- .../source/arch/arm64/kern_k_page_table.cpp | 4 +-- .../source/kern_k_page_table_base.cpp | 35 +++++++++++-------- .../libmesosphere/source/kern_k_process.cpp | 4 +-- .../source/kern_k_server_session.cpp | 8 ++--- 7 files changed, 40 insertions(+), 29 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp index 6cd5008b3..a12533207 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp @@ -176,7 +176,7 @@ namespace ams::kern::arch::arm64 { } NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end); - NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager); + NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit); Result Finalize(); private: Result MapL1Blocks(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, PageLinkedList *page_list, bool reuse_ll); diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp index 9ca342116..cf603b14f 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp @@ -30,8 +30,8 @@ namespace ams::kern::arch::arm64 { m_page_table.Activate(id); } - Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) { - return m_page_table.InitializeForProcess(id, as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager); + Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit) { + return m_page_table.InitializeForProcess(id, as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager, resource_limit); } void Finalize() { m_page_table.Finalize(); } @@ -216,8 +216,8 @@ namespace ams::kern::arch::arm64 { return m_page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.m_page_table, test_perm, dst_state, send); } - Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process) { - return m_page_table.CleanupForIpcServer(address, size, dst_state, server_process); + Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) { + return m_page_table.CleanupForIpcServer(address, size, dst_state); } Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index b63a9a4ee..538b98c44 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -47,6 +47,8 @@ namespace ams::kern { static_assert(std::is_trivial::value); static_assert(sizeof(KPageProperties) == sizeof(u32)); + class KResourceLimit; + class KPageTableBase { NON_COPYABLE(KPageTableBase); NON_MOVEABLE(KPageTableBase); @@ -150,6 +152,7 @@ namespace ams::kern { size_t m_max_heap_size{}; size_t m_mapped_physical_memory_size{}; size_t m_mapped_unsafe_physical_memory{}; + size_t m_mapped_ipc_server_memory{}; mutable KLightLock m_general_lock{}; mutable KLightLock m_map_physical_memory_lock{}; KPageTableImpl m_impl{}; @@ -161,6 +164,7 @@ namespace ams::kern { bool m_enable_device_address_space_merge{}; KMemoryBlockSlabManager *m_memory_block_slab_manager{}; KBlockInfoManager *m_block_info_manager{}; + KResourceLimit *m_resource_limit{}; const KMemoryRegion *m_cached_physical_linear_region{}; const KMemoryRegion *m_cached_physical_heap_region{}; const KMemoryRegion *m_cached_virtual_heap_region{}; @@ -171,7 +175,7 @@ namespace ams::kern { constexpr KPageTableBase() { /* ... */ } NOINLINE Result InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end); - NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_device_address_space_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager); + NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_device_address_space_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KResourceLimit *resource_limit); void Finalize(); @@ -372,7 +376,7 @@ namespace ams::kern { Result CopyMemoryFromHeapToHeapWithoutCheckDestination(KPageTableBase &dst_page_table, KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr); Result SetupForIpc(KProcessAddress *out_dst_addr, size_t size, KProcessAddress src_addr, KPageTableBase &src_page_table, KMemoryPermission test_perm, KMemoryState dst_state, bool send); - Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process); + Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state); Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); Result MapPhysicalMemory(KProcessAddress address, size_t size); diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp index 188475ec0..53539e925 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp @@ -181,7 +181,7 @@ namespace ams::kern::arch::arm64 { return ResultSuccess(); } - Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) { + Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit) { /* The input ID isn't actually used. */ MESOSPHERE_UNUSED(id); @@ -202,7 +202,7 @@ namespace ams::kern::arch::arm64 { const size_t as_width = GetAddressSpaceWidth(as_type); const KProcessAddress as_start = 0; const KProcessAddress as_end = (1ul << as_width); - R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, enable_das_merge, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, mem_block_slab_manager, block_info_manager)); + R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, enable_das_merge, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, mem_block_slab_manager, block_info_manager, resource_limit)); /* We succeeded! */ table_guard.Cancel(); diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index e503a3a37..12e4d196e 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -43,9 +43,11 @@ namespace ams::kern { m_max_heap_size = 0; m_mapped_physical_memory_size = 0; m_mapped_unsafe_physical_memory = 0; + m_mapped_ipc_server_memory = 0; m_memory_block_slab_manager = std::addressof(Kernel::GetSystemMemoryBlockManager()); m_block_info_manager = std::addressof(Kernel::GetBlockInfoManager()); + m_resource_limit = std::addressof(Kernel::GetSystemResourceLimit()); m_allocate_option = KMemoryManager::EncodeOption(KMemoryManager::Pool_System, KMemoryManager::Direction_FromFront); m_heap_fill_value = MemoryFillValue_Zero; @@ -65,7 +67,7 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager) { + Result KPageTableBase::InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KResourceLimit *resource_limit) { /* Validate the region. */ MESOSPHERE_ABORT_UNLESS(start <= code_address); MESOSPHERE_ABORT_UNLESS(code_address < code_address + code_size); @@ -131,6 +133,7 @@ namespace ams::kern { m_is_kernel = false; m_memory_block_slab_manager = mem_block_slab_manager; m_block_info_manager = block_info_manager; + m_resource_limit = resource_limit; /* Determine the region we can place our undetermineds in. */ KProcessAddress alloc_start; @@ -227,8 +230,9 @@ namespace ams::kern { /* Set heap and fill members. */ m_current_heap_end = m_heap_region_start; m_max_heap_size = 0; - m_mapped_physical_memory_size = 0; + m_mapped_physical_memory_size = 0; m_mapped_unsafe_physical_memory = 0; + m_mapped_ipc_server_memory = 0; const bool fill_memory = KTargetSystem::IsDebugMemoryFillEnabled(); m_heap_fill_value = fill_memory ? MemoryFillValue_Heap : MemoryFillValue_Zero; @@ -283,6 +287,11 @@ namespace ams::kern { Kernel::GetUnsafeMemory().Release(m_mapped_unsafe_physical_memory); } + /* Release any ipc server memory. */ + if (m_mapped_ipc_server_memory) { + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, m_mapped_ipc_server_memory); + } + /* Invalidate the entire instruction cache. */ cpu::InvalidateEntireInstructionCache(); } @@ -1507,7 +1516,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), m_heap_region_start + size, num_pages, Null, false, unmap_properties, OperationType_Unmap, false)); /* Release the memory from the resource limit. */ - GetCurrentProcess().ReleaseResource(ams::svc::LimitableResource_PhysicalMemoryMax, num_pages * PageSize); + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, num_pages * PageSize); /* Apply the memory block update. */ m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, size == 0 ? KMemoryBlockDisableMergeAttribute_Normal : KMemoryBlockDisableMergeAttribute_None); @@ -1530,7 +1539,7 @@ namespace ams::kern { } /* Reserve memory for the heap extension. */ - KScopedResourceReservation memory_reservation(GetCurrentProcess().GetResourceLimit(), ams::svc::LimitableResource_PhysicalMemoryMax, allocation_size); + KScopedResourceReservation memory_reservation(m_resource_limit, ams::svc::LimitableResource_PhysicalMemoryMax, allocation_size); R_UNLESS(memory_reservation.Succeeded(), svc::ResultLimitReached()); /* Allocate pages for the heap extension. */ @@ -3464,7 +3473,7 @@ namespace ams::kern { /* Reserve space for any partial pages we allocate. */ const size_t unmapped_size = aligned_src_size - mapping_src_size; - KScopedResourceReservation memory_reservation(GetCurrentProcess().GetResourceLimit(), ams::svc::LimitableResource_PhysicalMemoryMax, unmapped_size); + KScopedResourceReservation memory_reservation(m_resource_limit, ams::svc::LimitableResource_PhysicalMemoryMax, unmapped_size); R_UNLESS(memory_reservation.Succeeded(), svc::ResultLimitReached()); /* Ensure that we manage page references correctly. */ @@ -3688,7 +3697,7 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process) { + Result KPageTableBase::CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) { /* Validate the address. */ R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); @@ -3721,12 +3730,10 @@ namespace ams::kern { m_memory_block_manager.Update(std::addressof(allocator), aligned_start, aligned_num_pages, KMemoryState_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); /* Release from the resource limit as relevant. */ - if (auto *resource_limit = server_process->GetResourceLimit(); resource_limit != nullptr) { - const KProcessAddress mapping_start = util::AlignUp(GetInteger(address), PageSize); - const KProcessAddress mapping_end = util::AlignDown(GetInteger(address) + size, PageSize); - const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; - resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, aligned_size - mapping_size); - } + const KProcessAddress mapping_start = util::AlignUp(GetInteger(address), PageSize); + const KProcessAddress mapping_end = util::AlignDown(GetInteger(address) + size, PageSize); + const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, aligned_size - mapping_size); return ResultSuccess(); } @@ -4031,7 +4038,7 @@ namespace ams::kern { /* Allocate and map the memory. */ { /* Reserve the memory from the process resource limit. */ - KScopedResourceReservation memory_reservation(GetCurrentProcess().GetResourceLimit(), ams::svc::LimitableResource_PhysicalMemoryMax, size - mapped_size); + KScopedResourceReservation memory_reservation(m_resource_limit, ams::svc::LimitableResource_PhysicalMemoryMax, size - mapped_size); R_UNLESS(memory_reservation.Succeeded(), svc::ResultLimitReached()); /* Allocate pages for the new memory. */ @@ -4439,7 +4446,7 @@ namespace ams::kern { /* Release the memory resource. */ m_mapped_physical_memory_size -= mapped_size; - GetCurrentProcess().ReleaseResource(ams::svc::LimitableResource_PhysicalMemoryMax, mapped_size); + m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, mapped_size); /* Update memory blocks. */ m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 474f08c7f..b72c84bda 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -254,7 +254,7 @@ namespace ams::kern { auto *mem_block_manager = std::addressof(is_app ? Kernel::GetApplicationMemoryBlockManager() : Kernel::GetSystemMemoryBlockManager()); auto *block_info_manager = std::addressof(Kernel::GetBlockInfoManager()); auto *pt_manager = std::addressof(Kernel::GetPageTableManager()); - R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, params.code_num_pages * PageSize, mem_block_manager, block_info_manager, pt_manager)); + R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, params.code_num_pages * PageSize, mem_block_manager, block_info_manager, pt_manager, res_limit)); } auto pt_guard = SCOPE_GUARD { m_page_table.Finalize(); }; @@ -359,7 +359,7 @@ namespace ams::kern { const auto as_type = static_cast(params.flags & ams::svc::CreateProcessFlag_AddressSpaceMask); const bool enable_aslr = (params.flags & ams::svc::CreateProcessFlag_EnableAslr) != 0; const bool enable_das_merge = (params.flags & ams::svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge) == 0; - R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, code_size, mem_block_manager, block_info_manager, pt_manager)); + R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, code_size, mem_block_manager, block_info_manager, pt_manager, res_limit)); } auto pt_guard = SCOPE_GUARD { m_page_table.Finalize(); }; diff --git a/libraries/libmesosphere/source/kern_k_server_session.cpp b/libraries/libmesosphere/source/kern_k_server_session.cpp index e49f78ebb..1cd5a1a8d 100644 --- a/libraries/libmesosphere/source/kern_k_server_session.cpp +++ b/libraries/libmesosphere/source/kern_k_server_session.cpp @@ -398,17 +398,17 @@ namespace ams::kern { /* Cleanup Send mappings. */ for (size_t i = 0; i < request->GetSendCount(); ++i) { - R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i), request->GetSendSize(i), request->GetSendMemoryState(i), server_process)); + R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i), request->GetSendSize(i), request->GetSendMemoryState(i))); } /* Cleanup Receive mappings. */ for (size_t i = 0; i < request->GetReceiveCount(); ++i) { - R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i), request->GetReceiveSize(i), request->GetReceiveMemoryState(i), server_process)); + R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i), request->GetReceiveSize(i), request->GetReceiveMemoryState(i))); } /* Cleanup Exchange mappings. */ for (size_t i = 0; i < request->GetExchangeCount(); ++i) { - R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i), request->GetExchangeSize(i), request->GetExchangeMemoryState(i), server_process)); + R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i), request->GetExchangeSize(i), request->GetExchangeMemoryState(i))); } return ResultSuccess(); @@ -470,7 +470,7 @@ namespace ams::kern { /* Ensure that we clean up on failure. */ auto setup_guard = SCOPE_GUARD { - dst_page_table.CleanupForIpcServer(dst_address, size, dst_state, request->GetServerProcess()); + dst_page_table.CleanupForIpcServer(dst_address, size, dst_state); src_page_table.CleanupForIpcClient(src_address, size, dst_state); };