diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp index 253ade476..e175f118a 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp @@ -116,7 +116,7 @@ namespace ams::kern { }; enum class RegionType : u32 { - None = 0, + NoMapping = 0, KernelTraceBuffer = 1, OnMemoryBootImage = 2, DTB = 3, @@ -219,6 +219,10 @@ namespace ams::kern { Result SetHandleTableCapability(const util::BitPack32 cap); Result SetDebugFlagsCapability(const util::BitPack32 cap); + template + static Result ProcessMapRegionCapability(const util::BitPack32 cap, F f); + static Result CheckMapRegion(const util::BitPack32 cap); + Result SetCapability(const util::BitPack32 cap, u32 &set_flags, u32 &set_svc, KProcessPageTable *page_table); Result SetCapabilities(const u32 *caps, s32 num_caps, KProcessPageTable *page_table); Result SetCapabilities(svc::KUserPointer user_caps, s32 num_caps, KProcessPageTable *page_table); @@ -229,6 +233,8 @@ namespace ams::kern { Result Initialize(const u32 *caps, s32 num_caps, KProcessPageTable *page_table); Result Initialize(svc::KUserPointer user_caps, s32 num_caps, KProcessPageTable *page_table); + static Result CheckCapabilities(svc::KUserPointer user_caps, s32 num_caps); + constexpr u64 GetCoreMask() const { return m_core_mask; } constexpr u64 GetPriorityMask() const { return m_priority_mask; } constexpr s32 GetHandleTableSize() const { return m_handle_table_size; } diff --git a/libraries/libmesosphere/source/kern_k_capabilities.cpp b/libraries/libmesosphere/source/kern_k_capabilities.cpp index d68d902eb..c86809bec 100644 --- a/libraries/libmesosphere/source/kern_k_capabilities.cpp +++ b/libraries/libmesosphere/source/kern_k_capabilities.cpp @@ -156,9 +156,10 @@ namespace ams::kern { R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite)); } - Result KCapabilities::MapRegion(const util::BitPack32 cap, KProcessPageTable *page_table) { + template + ALWAYS_INLINE Result KCapabilities::ProcessMapRegionCapability(const util::BitPack32 cap, F f) { /* Define the allowed memory regions. */ - constexpr KMemoryRegionType MemoryRegions[] = { + constexpr const KMemoryRegionType MemoryRegions[] = { KMemoryRegionType_None, KMemoryRegionType_KernelTraceBuffer, KMemoryRegionType_OnMemoryBootImage, @@ -173,12 +174,12 @@ namespace ams::kern { const auto type = types[i]; const auto perm = ro[i] ? KMemoryPermission_UserRead : KMemoryPermission_UserReadWrite; switch (type) { - case RegionType::None: + case RegionType::NoMapping: break; case RegionType::KernelTraceBuffer: case RegionType::OnMemoryBootImage: case RegionType::DTB: - R_TRY(page_table->MapRegion(MemoryRegions[static_cast(type)], perm)); + R_TRY(f(MemoryRegions[static_cast(type)], perm)); break; default: R_THROW(svc::ResultNotFound()); @@ -188,6 +189,22 @@ namespace ams::kern { R_SUCCEED(); } + Result KCapabilities::MapRegion(const util::BitPack32 cap, KProcessPageTable *page_table) { + /* Map each region into the process's page table. */ + R_RETURN(ProcessMapRegionCapability(cap, [page_table] ALWAYS_INLINE_LAMBDA (KMemoryRegionType region_type, KMemoryPermission perm) -> Result { + R_RETURN(page_table->MapRegion(region_type, perm)); + })); + } + + Result KCapabilities::CheckMapRegion(const util::BitPack32 cap) { + /* Check that each region has a physical backing store. */ + R_RETURN(ProcessMapRegionCapability(cap, [] ALWAYS_INLINE_LAMBDA (KMemoryRegionType region_type, KMemoryPermission perm) -> Result { + MESOSPHERE_UNUSED(perm); + R_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().FindFirstDerived(region_type) != nullptr, svc::ResultOutOfRange()); + R_SUCCEED(); + })); + } + Result KCapabilities::SetInterruptPairCapability(const util::BitPack32 cap) { /* Extract interrupts. */ const u32 ids[2] = { cap.Get(), cap.Get(), }; @@ -320,4 +337,21 @@ namespace ams::kern { R_SUCCEED(); } + Result KCapabilities::CheckCapabilities(svc::KUserPointer user_caps, s32 num_caps) { + for (s32 i = 0; i < num_caps; ++i) { + /* Read the cap from userspace. */ + u32 cap0; + R_TRY(user_caps.CopyArrayElementTo(std::addressof(cap0), i)); + + /* Check the capability refers to a valid region. */ + + const util::BitPack32 cap = { cap0 }; + if (GetCapabilityType(cap) == CapabilityType::MapRegion) { + R_TRY(CheckMapRegion(cap)); + } + } + + R_SUCCEED(); + } + } diff --git a/libraries/libmesosphere/source/svc/kern_svc_process.cpp b/libraries/libmesosphere/source/svc/kern_svc_process.cpp index aa240dff7..1bd8c66b8 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_process.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_process.cpp @@ -192,6 +192,9 @@ namespace ams::kern::svc { const bool is_application = (params.flags & ams::svc::CreateProcessFlag_IsApplication) != 0; R_UNLESS(!optimize_allocs || is_application, svc::ResultBusy()); + /* Check that the user-provided capabilities are accessible and refer to valid regions. */ + R_TRY(KCapabilities::CheckCapabilities(user_caps, num_caps)); + /* Get the current handle table. */ auto &handle_table = GetCurrentProcess().GetHandleTable();