diff --git a/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp b/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp index 370211b2..2ccd4b72 100644 --- a/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp +++ b/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp @@ -109,6 +109,9 @@ namespace ams::kern::arch::arm64 { KPageTableManager &GetPageTableManager() const { return *m_manager; } private: constexpr PageTableEntry GetEntryTemplate(const KPageProperties properties) const { + /* Check that the property is not kernel execute. */ + MESOSPHERE_ABORT_UNLESS((properties.perm & KMemoryPermission_KernelExecute) == 0); + /* Set basic attributes. */ PageTableEntry entry{PageTableEntry::ExtensionFlag_Valid}; entry.SetPrivilegedExecuteNever(true); @@ -122,22 +125,24 @@ namespace ams::kern::arch::arm64 { /* Set page attribute. */ if (properties.io) { MESOSPHERE_ABORT_UNLESS(!properties.uncached); - MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0); + MESOSPHERE_ABORT_UNLESS((properties.perm & KMemoryPermission_UserExecute) == 0); entry.SetPageAttribute(PageTableEntry::PageAttribute_Device_nGnRnE) .SetUserExecuteNever(true); } else if (properties.uncached) { - MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0); + MESOSPHERE_ABORT_UNLESS((properties.perm & KMemoryPermission_UserExecute) == 0); - entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemoryNotCacheable); + entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemoryNotCacheable) + .SetUserExecuteNever(true); } else { entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemory); - } - /* Set user execute never bit. */ - if (properties.perm != KMemoryPermission_UserReadExecute) { - MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0); - entry.SetUserExecuteNever(true); + if ((properties.perm & KMemoryPermission_UserExecute) != 0) { + /* Check that the permission is either r--/--x or r--/r-x. */ + MESOSPHERE_ABORT_UNLESS((properties.perm & ~ams::svc::MemoryPermission_Read) == (KMemoryPermission_KernelRead | KMemoryPermission_UserExecute)); + } else { + entry.SetUserExecuteNever(true); + } } /* Set AP[1] based on perm. */ diff --git a/libmesosphere/include/mesosphere/arch/arm64/kern_userspace_memory_access.hpp b/libmesosphere/include/mesosphere/arch/arm64/kern_userspace_memory_access.hpp index 9e0c1844..6afabe51 100644 --- a/libmesosphere/include/mesosphere/arch/arm64/kern_userspace_memory_access.hpp +++ b/libmesosphere/include/mesosphere/arch/arm64/kern_userspace_memory_access.hpp @@ -21,7 +21,18 @@ namespace ams::kern::arch::arm64 { void UserspaceAccessFunctionAreaBegin(); class UserspaceAccess { + private: + static bool CopyMemoryFromUserSize32BitWithSupervisorAccessImpl(void *dst, const void *src); public: + static bool CopyMemoryFromUserSize32BitWithSupervisorAccess(void *dst, const void *src) { + /* Check that the address is within the valid userspace range. */ + if (const uintptr_t src_uptr = reinterpret_cast(src); src_uptr < ams::svc::AddressNullGuard32Size || (src_uptr + sizeof(u32) - 1) >= ams::svc::AddressMemoryRegion39Size) { + return false; + } + + return CopyMemoryFromUserSize32BitWithSupervisorAccessImpl(dst, src); + } + static bool CopyMemoryFromUser(void *dst, const void *src, size_t size); static bool CopyMemoryFromUserAligned32Bit(void *dst, const void *src, size_t size); static bool CopyMemoryFromUserAligned64Bit(void *dst, const void *src, size_t size); diff --git a/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp b/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp index d55e217e..0fc25c74 100644 --- a/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp +++ b/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp @@ -100,6 +100,8 @@ namespace ams::kern::arch::arm64 { u32 insn_value = 0; if (UserspaceAccess::CopyMemoryFromUser(std::addressof(insn_value), reinterpret_cast(context->pc), sizeof(insn_value))) { insn = insn_value; + } else if (KTargetSystem::IsDebugMode() && (context->pc & 3) == 0 && UserspaceAccess::CopyMemoryFromUserSize32BitWithSupervisorAccess(std::addressof(insn_value), reinterpret_cast(context->pc))) { + insn = insn_value; } else { insn = 0; } @@ -112,33 +114,6 @@ namespace ams::kern::arch::arm64 { bool should_process_user_exception = KTargetSystem::IsUserExceptionHandlersEnabled(); const u64 ec = (esr >> 26) & 0x3F; - switch (ec) { - case EsrEc_Unknown: - case EsrEc_IllegalExecution: - case EsrEc_Svc32: - case EsrEc_Svc64: - case EsrEc_PcAlignmentFault: - case EsrEc_SpAlignmentFault: - case EsrEc_SErrorInterrupt: - case EsrEc_BreakPointEl0: - case EsrEc_SoftwareStepEl0: - case EsrEc_WatchPointEl0: - case EsrEc_BkptInstruction: - case EsrEc_BrkInstruction: - break; - default: - { - /* If the fault address's state is KMemoryState_Code and the user can't read the address, force processing exception. */ - KMemoryInfo info; - ams::svc::PageInfo pi; - if (R_SUCCEEDED(cur_process.GetPageTable().QueryInfo(std::addressof(info), std::addressof(pi), far))) { - if (info.GetState() == KMemoryState_Code && ((info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead)) { - should_process_user_exception = true; - } - } - } - break; - } /* In the event that we return from this exception, we want SPSR.SS set so that we advance an instruction if single-stepping. */ #if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP) diff --git a/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s b/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s index e697fde3..01104deb 100644 --- a/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s +++ b/libmesosphere/source/arch/arm64/kern_userspace_memory_access_asm.s @@ -154,6 +154,21 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess27CopyMemoryFromUserSize32BitEPvPKv: mov x0, #1 ret +/* ams::kern::arch::arm64::UserspaceAccess::CopyMemoryFromUserSize32BitWithSupervisorAccessImpl(void *dst, const void *src) */ +.section .text._ZN3ams4kern4arch5arm6415UserspaceAccess51CopyMemoryFromUserSize32BitWithSupervisorAccessImplEPvPKv, "ax", %progbits +.global _ZN3ams4kern4arch5arm6415UserspaceAccess51CopyMemoryFromUserSize32BitWithSupervisorAccessImplEPvPKv +.type _ZN3ams4kern4arch5arm6415UserspaceAccess51CopyMemoryFromUserSize32BitWithSupervisorAccessImplEPvPKv, %function +.balign 0x10 +_ZN3ams4kern4arch5arm6415UserspaceAccess51CopyMemoryFromUserSize32BitWithSupervisorAccessImplEPvPKv: + /* Just load and store a u32. */ + /* NOTE: This is done with supervisor access permissions. */ + ldr w2, [x1] + str w2, [x0] + + /* We're done. */ + mov x0, #1 + ret + /* ams::kern::arch::arm64::UserspaceAccess::CopyStringFromUser(void *dst, const void *src, size_t size) */ .section .text._ZN3ams4kern4arch5arm6415UserspaceAccess18CopyStringFromUserEPvPKvm, "ax", %progbits .global _ZN3ams4kern4arch5arm6415UserspaceAccess18CopyStringFromUserEPvPKvm diff --git a/libmesosphere/source/kern_k_page_table_base.cpp b/libmesosphere/source/kern_k_page_table_base.cpp index 2a9402ec..d141490e 100644 --- a/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libmesosphere/source/kern_k_page_table_base.cpp @@ -1787,6 +1787,11 @@ namespace ams::kern { /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); + /* If we're creating an executable mapping, take and immediately release the scheduler lock. This will force a reschedule. */ + if (is_x) { + KScopedSchedulerLock sl; + } + /* Perform mapping operation. */ const KPageProperties properties = { new_perm, false, false, DisableMergeAttribute_None }; const auto operation = was_x ? OperationType_ChangePermissionsAndRefreshAndFlush : OperationType_ChangePermissions; diff --git a/libmesosphere/source/svc/kern_svc_process_memory.cpp b/libmesosphere/source/svc/kern_svc_process_memory.cpp index 91d799d7..41420ca0 100644 --- a/libmesosphere/source/svc/kern_svc_process_memory.cpp +++ b/libmesosphere/source/svc/kern_svc_process_memory.cpp @@ -27,6 +27,7 @@ namespace ams::kern::svc { case ams::svc::MemoryPermission_Read: case ams::svc::MemoryPermission_ReadWrite: case ams::svc::MemoryPermission_ReadExecute: + case ams::svc::MemoryPermission_Execute: return true; default: return false; diff --git a/libstratosphere/include/stratosphere/ldr/ldr_types.hpp b/libstratosphere/include/stratosphere/ldr/ldr_types.hpp index 325d2a35..e81be6b7 100644 --- a/libstratosphere/include/stratosphere/ldr/ldr_types.hpp +++ b/libstratosphere/include/stratosphere/ldr/ldr_types.hpp @@ -226,6 +226,7 @@ namespace ams::ldr { MetaFlag_OptimizeMemoryAllocation = (1 << 4), MetaFlag_DisableDeviceAddressSpaceMerge = (1 << 5), MetaFlag_EnableAliasRegionExtraSize = (1 << 6), + MetaFlag_PreventCodeReads = (1 << 7), }; enum AddressSpaceType { diff --git a/libstratosphere/source/os/impl/os_process_memory_impl.os.horizon.cpp b/libstratosphere/source/os/impl/os_process_memory_impl.os.horizon.cpp index de7b241a..686cc699 100644 --- a/libstratosphere/source/os/impl/os_process_memory_impl.os.horizon.cpp +++ b/libstratosphere/source/os/impl/os_process_memory_impl.os.horizon.cpp @@ -27,6 +27,7 @@ namespace ams::os::impl { case os::MemoryPermission_ReadOnly: return svc::MemoryPermission_Read; case os::MemoryPermission_ReadWrite: return svc::MemoryPermission_ReadWrite; case os::MemoryPermission_ReadExecute: return svc::MemoryPermission_ReadExecute; + case os::MemoryPermission_ExecuteOnly: return svc::MemoryPermission_Execute; AMS_UNREACHABLE_DEFAULT_CASE(); } }