From 772e1f1c4ffe9d52e8d9506cb0d3572c692f3ba4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 18 Feb 2020 01:44:40 -0800 Subject: [PATCH] kern: implement IsValidPageGroup --- .../include/mesosphere/kern_k_page_group.hpp | 1 + .../mesosphere/kern_k_page_table_base.hpp | 5 +- .../source/arch/arm64/kern_k_page_table.cpp | 1 - .../source/kern_k_page_table_base.cpp | 88 ++++++++++++++++++- 4 files changed, 89 insertions(+), 6 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_group.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_group.hpp index cf98a9cbd..f62841eed 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_group.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_group.hpp @@ -90,6 +90,7 @@ namespace ams::kern { iterator begin() const { return this->block_list.begin(); } iterator end() const { return this->block_list.end(); } + bool empty() const { return this->block_list.empty(); } Result AddBlock(KVirtualAddress addr, size_t num_pages); void Open() const; 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 7f2bd3bff..006017426 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -35,6 +35,9 @@ namespace ams::kern { class KPageTableBase { NON_COPYABLE(KPageTableBase); NON_MOVEABLE(KPageTableBase); + public: + using TraversalEntry = KPageTableImpl::TraversalEntry; + using TraversalContext = KPageTableImpl::TraversalContext; protected: enum MemoryFillValue { MemoryFillValue_Zero = 0, @@ -214,7 +217,7 @@ namespace ams::kern { Result AllocateAndMapPagesImpl(PageLinkedList *page_list, KProcessAddress address, size_t num_pages, const KPageProperties properties); Result MapPageGroupImpl(PageLinkedList *page_list, KProcessAddress address, const KPageGroup &pg, const KPageProperties properties, bool reuse_ll); - bool IsValidPageGroup(const KPageGroup &pg, KProcessAddress addr, size_t num_pages) const; + bool IsValidPageGroup(const KPageGroup &pg, KProcessAddress addr, size_t num_pages); NOINLINE Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm); public: 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 21635b129..41dd67942 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp @@ -224,7 +224,6 @@ namespace ams::kern::arch::arm64 { next_valid = impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr); MESOSPHERE_ASSERT(next_valid); } - //MESOSPHERE_LOG("Unmap: Acting on %08zx %08zx (%p %p %p)\n", GetInteger(next_entry.phys_addr), next_entry.block_size, context.l1_entry, context.l2_entry, context.l3_entry); /* Check that our state is coherent. */ MESOSPHERE_ASSERT((next_entry.block_size / PageSize) <= remaining_pages); diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 390963fd8..fd10073f6 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -337,9 +337,89 @@ namespace ams::kern { return ResultSuccess(); } - bool KPageTableBase::IsValidPageGroup(const KPageGroup &pg, KProcessAddress addr, size_t num_pages) const { - /* TODO */ - return true; + bool KPageTableBase::IsValidPageGroup(const KPageGroup &pg, KProcessAddress addr, size_t num_pages) { + /* Empty groups are necessarily invalid. */ + if (pg.empty()) { + return false; + } + + auto &impl = this->GetImpl(); + + /* We're going to validate that the group we'd expect is the group we see. */ + auto cur_it = pg.begin(); + KVirtualAddress cur_block_address = cur_it->GetAddress(); + size_t cur_block_pages = cur_it->GetNumPages(); + + auto UpdateCurrentIterator = [&]() ALWAYS_INLINE_LAMBDA { + if (cur_block_pages == 0) { + if ((++cur_it) == pg.end()) { + return false; + } + + cur_block_address = cur_it->GetAddress(); + cur_block_pages = cur_it->GetNumPages(); + } + return true; + }; + + /* Begin traversal. */ + TraversalContext context; + TraversalEntry next_entry; + if (!impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr)) { + return false; + } + + /* Prepare tracking variables. */ + KPhysicalAddress cur_addr = next_entry.phys_addr; + size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1)); + size_t tot_size = cur_size; + + /* Iterate, comparing expected to actual. */ + while (tot_size < num_pages * PageSize) { + if (!impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))) { + return false; + } + + if (next_entry.phys_addr != (cur_addr + cur_size)) { + const size_t cur_pages = cur_size / PageSize; + + if (!IsHeapPhysicalAddress(cur_addr)) { + return false; + } + + if (!UpdateCurrentIterator()) { + return false; + } + + if (cur_block_address != GetHeapVirtualAddress(cur_addr) || cur_block_pages < cur_pages) { + return false; + } + + cur_block_address += cur_size; + cur_block_pages -= cur_pages; + cur_addr = next_entry.phys_addr; + cur_size = next_entry.block_size; + } else { + cur_size += next_entry.block_size; + } + + tot_size += next_entry.block_size; + } + + /* Ensure we compare the right amount for the last block. */ + if (tot_size > num_pages * PageSize) { + cur_size -= (tot_size - num_pages * PageSize); + } + + if (!IsHeapPhysicalAddress(cur_addr)) { + return false; + } + + if (!UpdateCurrentIterator()) { + return false; + } + + return cur_block_address == GetHeapVirtualAddress(cur_addr) && cur_block_pages == (cur_size / PageSize); } Result KPageTableBase::MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm) { @@ -437,7 +517,7 @@ namespace ams::kern { R_TRY(this->CheckMemoryState(address, size, KMemoryState_All, state, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All, KMemoryAttribute_None)); /* Check that the page group is valid. */ - R_UNLESS(this->IsValidPageGroup(pg, address, size), svc::ResultInvalidCurrentMemory()); + R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), svc::ResultInvalidCurrentMemory()); /* Create an update allocator. */ KMemoryBlockManagerUpdateAllocator allocator(this->memory_block_slab_manager);