diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp index 9e9e9c7a4..7e95a98cb 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp @@ -119,6 +119,8 @@ namespace ams::kern { KMemoryState_Insecure = ams::svc::MemoryState_Insecure | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagLinearMapped | KMemoryState_FlagCanChangeAttribute | KMemoryState_FlagCanDeviceMap | KMemoryState_FlagCanAlignedDeviceMap | KMemoryState_FlagCanQueryPhysical | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc, + + KMemoryState_ShadowStack = ams::svc::MemoryState_ShadowStack, }; #if 1 @@ -147,6 +149,7 @@ namespace ams::kern { static_assert(KMemoryState_CodeOut == 0x04402015); static_assert(KMemoryState_Coverage == 0x00002016); /* TODO: Is this correct? */ static_assert(KMemoryState_Insecure == 0x055C3817); + static_assert(KMemoryState_ShadowStack == 0x0000001A); #endif enum KMemoryPermission : u8 { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index f6193dd71..429af04ec 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -29,6 +29,7 @@ namespace ams::kern { class KProcess; class KConditionVariable; class KAddressArbiter; + class KPageBuffer; using KThreadFunction = void (*)(uintptr_t); @@ -305,6 +306,8 @@ namespace ams::kern { u32 *m_light_ipc_data; KProcessAddress m_tls_address; void *m_tls_heap_address; + KProcessAddress m_shadow_stack_address; + KPageBuffer *m_shadow_stack_page; KLightLock m_activity_pause_lock; SyncObjectBuffer m_sync_object_buffer; s64 m_schedule_count; @@ -347,7 +350,7 @@ namespace ams::kern { : KAutoObjectWithSlabHeapAndContainer(util::ConstantInitialize), KTimerTask(util::ConstantInitialize), m_process_list_node{}, m_condvar_arbiter_tree_node{util::ConstantInitialize}, m_priority{-1}, m_condvar_tree{}, m_condvar_key{}, m_caller_save_fpu_registers{}, m_virtual_affinity_mask{}, m_physical_affinity_mask{}, m_thread_id{}, m_cpu_time{0}, m_address_key{Null}, m_parent{}, - m_kernel_stack_top{}, m_light_ipc_data{}, m_tls_address{Null}, m_tls_heap_address{}, m_activity_pause_lock{}, m_sync_object_buffer{util::ConstantInitialize}, + m_kernel_stack_top{}, m_light_ipc_data{}, m_tls_address{Null}, m_tls_heap_address{}, m_shadow_stack_address{Null}, m_shadow_stack_page{}, m_activity_pause_lock{}, m_sync_object_buffer{util::ConstantInitialize}, m_schedule_count{}, m_last_scheduled_tick{}, m_per_core_priority_queue_entry{}, m_wait_queue{}, m_held_lock_info_list{}, m_waiting_lock_info{}, m_pinned_waiter_list{}, m_debug_params{}, m_closed_object{}, m_address_key_value{}, m_suspend_request_flags{}, m_suspend_allowed_flags{}, m_synced_index{}, m_wait_result{svc::ResultNoSynchronizationObject()}, m_debug_exception_result{ResultSuccess()}, m_base_priority{}, m_base_priority_on_unpin{}, diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index 9cf5ea862..374640710 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -214,6 +214,10 @@ namespace ams::kern { m_tls_address = 0; m_tls_heap_address = 0; + /* Set shadow stack address and page. */ + m_shadow_stack_address = Null; + m_shadow_stack_page = nullptr; + /* Set parent and condvar tree. */ m_parent = nullptr; m_condvar_tree = nullptr; @@ -281,6 +285,29 @@ namespace ams::kern { R_TRY(owner->CreateThreadLocalRegion(std::addressof(m_tls_address))); m_tls_heap_address = owner->GetThreadLocalRegionPointer(m_tls_address); std::memset(m_tls_heap_address, 0, ams::svc::ThreadLocalRegionSize); + + /* If the owning process has a shadow stack, allocate and map a page for this thread. */ + if (owner->GetCreateProcessFlags() & ams::svc::CreateProcessFlag_EnableShadowStack) { + /* Allocate the backing page. */ + KPageBuffer *page = KPageBuffer::AllocateChecked(); + if (page == nullptr) { + owner->DeleteThreadLocalRegion(m_tls_address); + m_tls_address = Null; + R_THROW(svc::ResultOutOfMemory()); + } + + /* If we fail to map the page, free it and undo the TLS region we just created. */ + ON_RESULT_FAILURE { + KPageBuffer::Free(page); + owner->DeleteThreadLocalRegion(m_tls_address); + m_tls_address = Null; + }; + + /* Map the page into the process's shadow stack region. */ + R_TRY(owner->GetPageTable().MapPages(std::addressof(m_shadow_stack_address), 1, PageSize, page->GetPhysicalAddress(), KMemoryState_ShadowStack, KMemoryPermission_UserReadWrite)); + + m_shadow_stack_page = page; + } } /* Set parent, if relevant. */ @@ -390,6 +417,12 @@ namespace ams::kern { m_parent->UnregisterThread(this); } + /* If the thread has a shadow stack, unmap and free its page. */ + if (m_shadow_stack_address != Null) { + MESOSPHERE_R_ABORT_UNLESS(m_parent->GetPageTable().UnmapPages(m_shadow_stack_address, 1, KMemoryState_ShadowStack)); + KPageBuffer::Free(m_shadow_stack_page); + } + /* If the thread has a local region, delete it. */ if (m_tls_address != Null) { m_parent->DeleteThreadLocalRegion(m_tls_address);