kern: finish shadow stack region, 64k page process segregation implementation, fix some long-standing issues

This commit is contained in:
Alula
2026-09-15 17:03:11 +02:00
parent 289ba7104e
commit 7e58f5fbae
9 changed files with 132 additions and 188 deletions

View File

@@ -19,6 +19,7 @@
#include <mesosphere/kern_k_page_table_base.hpp>
#include <mesosphere/kern_k_page_group.hpp>
#include <mesosphere/kern_k_page_table_manager.hpp>
#include <mesosphere/init/kern_init_slab_setup.hpp>
namespace ams::kern::arch::arm64 {

View File

@@ -32,6 +32,11 @@ namespace ams::kern::arch::arm64 {
R_RETURN(m_page_table.InitializeForProcess(flags, from_back, pool, code_address, code_size, system_resource, resource_limit, process_index));
}
static ALWAYS_INLINE u64 GetProcessTcrEl1(size_t process_index) {
/* The kernel's root table is entry zero; processes are indexed from one. */
return KPageTable::GetTcrEL1Entry(process_index + 1);
}
void Finalize() { m_page_table.Finalize(); }
ALWAYS_INLINE KScopedLightLock AcquireDeviceMapLock() {
@@ -302,6 +307,10 @@ namespace ams::kern::arch::arm64 {
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { return m_page_table.CanContain(addr, size, state); }
bool CanContain(KProcessAddress addr, size_t size, ams::svc::MemoryState state) const { return m_page_table.CanContain(addr, size, state); }
bool IsInShadowStackRegion(KProcessAddress addr, size_t size) const { return m_page_table.IsInShadowStackRegion(addr, size); }
bool IsInShadowStackRegion(KProcessAddress addr) const { return m_page_table.IsInShadowStackRegion(addr); }
bool IsSafeUserPointer(KProcessAddress addr, size_t size) const { return m_page_table.IsSafeUserPointer(addr, size); }
KProcessAddress GetAddressSpaceStart() const { return m_page_table.GetAddressSpaceStart(); }
KProcessAddress GetShadowStackRegionStart() const { return m_page_table.GetShadowStackRegionStart(); }
KProcessAddress GetHeapRegionStart() const { return m_page_table.GetHeapRegionStart(); }

View File

@@ -77,7 +77,7 @@ namespace ams::kern::arch::arm64 {
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<uintptr_t>(src); src_uptr < ams::svc::AddressNullGuard32Size || (src_uptr + sizeof(u32) - 1) >= ams::svc::AddressMemoryRegion39Size) {
if (const uintptr_t src_uptr = reinterpret_cast<uintptr_t>(src); src_uptr < ams::svc::AddressNullGuard32Size || (src_uptr + sizeof(u32) - 1) >= ams::svc::AddressMemoryRegion42Size) {
return false;
}

View File

@@ -249,7 +249,15 @@ namespace ams::kern {
}
constexpr bool IsInShadowStackRegion(KProcessAddress addr, size_t size) const {
return this->Contains(addr, size) && m_region_starts[RegionType_ShadowStack] <= addr && addr + size - 1 <= m_region_ends[RegionType_ShadowStack] - 1;
const auto start = m_region_starts[RegionType_ShadowStack];
const auto end = m_region_ends[RegionType_ShadowStack];
return start != end && addr < end && start < addr + size;
}
constexpr bool IsInShadowStackRegion(KProcessAddress addr) const {
const auto start = m_region_starts[RegionType_ShadowStack];
const auto end = m_region_ends[RegionType_ShadowStack];
return start <= addr && addr < end;
}
constexpr bool IsSafeUserPointer(KProcessAddress addr, size_t size) const {

View File

@@ -353,182 +353,36 @@ namespace ams::kern {
const KProcessAddress process_code_last = process_code_end - 1;
auto IsInAddressSpace = [&](KProcessAddress addr) ALWAYS_INLINE_LAMBDA { return m_address_space_start <= addr && addr <= m_address_space_end; };
/* Ensure that the KernelMap region is valid. */
for (size_t k = 0; k < num_regions; ++k) {
if (const auto &kmap_region = region_layouts[k]; kmap_region.type == RegionType_KernelMap) {
/* If there's no kmap region, we have nothing to check. */
if (kmap_region.size == 0) {
break;
}
/* Check that the kmap region is within our address space. */
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_starts[RegionType_KernelMap]));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_ends[RegionType_KernelMap]));
/* Check for overlap with process code. */
const KProcessAddress kmap_start = m_region_starts[RegionType_KernelMap];
const KProcessAddress kmap_last = m_region_ends[RegionType_KernelMap] - 1;
MESOSPHERE_ABORT_UNLESS(kernel_map_region_size == 0 || kmap_last < process_code_start || process_code_last < kmap_start);
/* Check for overlap with stack. */
for (size_t s = 0; s < num_regions; ++s) {
if (const auto &stack_region = region_layouts[s]; stack_region.type == RegionType_Stack) {
if (stack_region.size != 0) {
const KProcessAddress stack_start = m_region_starts[RegionType_Stack];
const KProcessAddress stack_last = m_region_ends[RegionType_Stack] - 1;
MESOSPHERE_ABORT_UNLESS((kernel_map_region_size == 0 && stack_region_size == 0) || kmap_last < stack_start || stack_last < kmap_start);
}
break;
}
}
/* Check for overlap with alias. */
for (size_t a = 0; a < num_regions; ++a) {
if (const auto &alias_region = region_layouts[a]; alias_region.type == RegionType_Alias) {
if (alias_region.size != 0) {
const KProcessAddress alias_start = m_region_starts[RegionType_Alias];
const KProcessAddress alias_last = m_region_ends[RegionType_Alias] - 1;
MESOSPHERE_ABORT_UNLESS(kmap_last < alias_start || alias_last < kmap_start);
}
break;
}
}
/* Check for overlap with heap. */
for (size_t h = 0; h < num_regions; ++h) {
if (const auto &heap_region = region_layouts[h]; heap_region.type == RegionType_Heap) {
if (heap_region.size != 0) {
const KProcessAddress heap_start = m_region_starts[RegionType_Heap];
const KProcessAddress heap_last = m_region_ends[RegionType_Heap] - 1;
MESOSPHERE_ABORT_UNLESS(kmap_last < heap_start || heap_last < kmap_start);
}
break;
}
}
/* Check for overlap with shadow stack. */
for (size_t ss = 0; ss < num_regions; ++ss) {
if (const auto &shadow_stack_region = region_layouts[ss]; shadow_stack_region.type == RegionType_ShadowStack) {
if (shadow_stack_region.size != 0) {
const KProcessAddress shadow_stack_start = m_region_starts[RegionType_ShadowStack];
const KProcessAddress shadow_stack_last = m_region_ends[RegionType_ShadowStack] - 1;
MESOSPHERE_ABORT_UNLESS(kmap_last < shadow_stack_start || shadow_stack_last < kmap_start);
}
break;
}
}
}
/* Map each region type to its layout entry. */
const RegionLayoutInfo *region_layouts_by_type[RegionType_Count] = {};
for (size_t i = 0; i < num_regions; ++i) {
region_layouts_by_type[region_layouts[i].type] = std::addressof(region_layouts[i]);
}
/* Check that the Stack region is valid. */
for (size_t s = 0; s < num_regions; ++s) {
if (const auto &stack_region = region_layouts[s]; stack_region.type == RegionType_Stack) {
/* If there's no stack region, we have nothing to check. */
if (stack_region.size == 0) {
break;
}
/* Check that the stack region is within our address space. */
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_starts[RegionType_Stack]));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_ends[RegionType_Stack]));
/* Check for overlap with process code. */
const KProcessAddress stack_start = m_region_starts[RegionType_Stack];
const KProcessAddress stack_last = m_region_ends[RegionType_Stack] - 1;
MESOSPHERE_ABORT_UNLESS(stack_region_size == 0 || stack_last < process_code_start || process_code_last < stack_start);
/* Check for overlap with alias. */
for (size_t a = 0; a < num_regions; ++a) {
if (const auto &alias_region = region_layouts[a]; alias_region.type == RegionType_Alias) {
if (alias_region.size != 0) {
const KProcessAddress alias_start = m_region_starts[RegionType_Alias];
const KProcessAddress alias_last = m_region_ends[RegionType_Alias] - 1;
MESOSPHERE_ABORT_UNLESS(stack_last < alias_start || alias_last < stack_start);
}
break;
}
}
/* Check for overlap with heap. */
for (size_t h = 0; h < num_regions; ++h) {
if (const auto &heap_region = region_layouts[h]; heap_region.type == RegionType_Heap) {
if (heap_region.size != 0) {
const KProcessAddress heap_start = m_region_starts[RegionType_Heap];
const KProcessAddress heap_last = m_region_ends[RegionType_Heap] - 1;
MESOSPHERE_ABORT_UNLESS(stack_last < heap_start || heap_last < stack_start);
}
break;
}
}
/* Validate each region. */
for (size_t i = 0; i < RegionType_Count; ++i) {
/* If the region isn't present, there's nothing to check. */
const RegionLayoutInfo * const region = region_layouts_by_type[i];
if (region == nullptr || region->size == 0) {
continue;
}
}
/* Check that the Alias region is valid. */
for (size_t a = 0; a < num_regions; ++a) {
if (const auto &alias_region = region_layouts[a]; alias_region.type == RegionType_Alias) {
/* If there's no alias region, we have nothing to check. */
if (alias_region.size == 0) {
break;
/* Check that the region is within our address space. */
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_starts[i]));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_ends[i]));
/* Check for overlap with process code. */
MESOSPHERE_ABORT_UNLESS(m_region_ends[i] - 1 < process_code_start || process_code_last < m_region_starts[i]);
/* Check for overlap with every later region. */
for (size_t j = i; j < RegionType_ShadowStack; ++j) {
/* If the other region isn't present, there's nothing to check. */
const RegionLayoutInfo * const other = region_layouts_by_type[j + 1];
if (other == nullptr || other->size == 0) {
continue;
}
/* Check that the alias region is within our address space. */
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_starts[RegionType_Alias]));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_ends[RegionType_Alias]));
/* Check for overlap with process code. */
const KProcessAddress alias_start = m_region_starts[RegionType_Alias];
const KProcessAddress alias_last = m_region_ends[RegionType_Alias] - 1;
MESOSPHERE_ABORT_UNLESS(alias_last < process_code_start || process_code_last < alias_start);
/* Check for overlap with heap. */
for (size_t h = 0; h < num_regions; ++h) {
if (const auto &heap_region = region_layouts[h]; heap_region.type == RegionType_Heap) {
if (heap_region.size != 0) {
const KProcessAddress heap_start = m_region_starts[RegionType_Heap];
const KProcessAddress heap_last = m_region_ends[RegionType_Heap] - 1;
MESOSPHERE_ABORT_UNLESS(alias_last < heap_start || heap_last < alias_start);
}
break;
}
}
}
}
/* Check that the Heap region is valid. */
for (size_t h = 0; h < num_regions; ++h) {
if (const auto &heap_region = region_layouts[h]; heap_region.type == RegionType_Heap) {
/* If there's no heap region, we have nothing to check. */
if (heap_region.size == 0) {
break;
}
/* Check that the heap region is within our address space. */
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_starts[RegionType_Heap]));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_ends[RegionType_Heap]));
/* Check for overlap with process code. */
const KProcessAddress heap_start = m_region_starts[RegionType_Heap];
const KProcessAddress heap_last = m_region_ends[RegionType_Heap] - 1;
MESOSPHERE_ABORT_UNLESS(heap_last < process_code_start || process_code_last < heap_start);
}
}
/* Check that the ShadowStack region is valid. */
for (size_t ss = 0; ss < num_regions; ++ss) {
if (const auto &shadow_stack_region = region_layouts[ss]; shadow_stack_region.type == RegionType_ShadowStack) {
/* If there's no shadow stack region, we have nothing to check. */
if (shadow_stack_region.size == 0) {
break;
}
/* Check that the shadow stack region is within our address space. */
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_starts[RegionType_ShadowStack]));
MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(m_region_ends[RegionType_ShadowStack]));
/* Check for overlap with process code. */
const KProcessAddress shadow_stack_start = m_region_starts[RegionType_ShadowStack];
const KProcessAddress shadow_stack_last = m_region_ends[RegionType_ShadowStack] - 1;
MESOSPHERE_ABORT_UNLESS(shadow_stack_last < process_code_start || process_code_last < shadow_stack_start);
MESOSPHERE_ABORT_UNLESS(m_region_ends[i] - 1 < m_region_starts[j + 1] || m_region_ends[j + 1] - 1 < m_region_starts[i]);
}
}
}
@@ -598,6 +452,8 @@ namespace ams::kern {
case ams::svc::MemoryState_Static:
case ams::svc::MemoryState_ThreadLocal:
return m_region_starts[RegionType_KernelMap];
case ams::svc::MemoryState_ShadowStack:
return m_region_starts[RegionType_ShadowStack];
case ams::svc::MemoryState_Io:
case ams::svc::MemoryState_Shared:
case ams::svc::MemoryState_AliasCode:
@@ -633,6 +489,8 @@ namespace ams::kern {
case ams::svc::MemoryState_Static:
case ams::svc::MemoryState_ThreadLocal:
return m_region_ends[RegionType_KernelMap] - m_region_starts[RegionType_KernelMap];
case ams::svc::MemoryState_ShadowStack:
return m_region_ends[RegionType_ShadowStack] - m_region_starts[RegionType_ShadowStack];
case ams::svc::MemoryState_Io:
case ams::svc::MemoryState_Shared:
case ams::svc::MemoryState_AliasCode:
@@ -659,12 +517,20 @@ namespace ams::kern {
const KProcessAddress region_start = this->GetRegionAddress(state);
const size_t region_size = this->GetRegionSize(state);
if (region_size == 0) {
return false;
}
const bool is_in_region = region_start <= addr && addr < end && last <= region_start + region_size - 1;
const bool is_in_heap = !(end <= m_region_starts[RegionType_Heap] || m_region_ends[RegionType_Heap] <= addr || m_region_starts[RegionType_Heap] == m_region_ends[RegionType_Heap]);
const bool is_in_alias = !(end <= m_region_starts[RegionType_Alias] || m_region_ends[RegionType_Alias] <= addr || m_region_starts[RegionType_Alias] == m_region_ends[RegionType_Alias]);
switch (state) {
case ams::svc::MemoryState_Free:
case ams::svc::MemoryState_Normal:
case ams::svc::MemoryState_Ipc:
case ams::svc::MemoryState_NonSecureIpc:
case ams::svc::MemoryState_NonDeviceIpc:
case ams::svc::MemoryState_Kernel:
case ams::svc::MemoryState_ShadowStack:
return is_in_region;
case ams::svc::MemoryState_Io:
case ams::svc::MemoryState_Static:
@@ -682,17 +548,32 @@ namespace ams::kern {
case ams::svc::MemoryState_CodeOut:
case ams::svc::MemoryState_Coverage:
case ams::svc::MemoryState_Insecure:
return is_in_region && !is_in_heap && !is_in_alias;
case ams::svc::MemoryState_Normal:
MESOSPHERE_ASSERT(is_in_heap);
return is_in_region && !is_in_alias;
case ams::svc::MemoryState_Ipc:
case ams::svc::MemoryState_NonSecureIpc:
case ams::svc::MemoryState_NonDeviceIpc:
MESOSPHERE_ASSERT(is_in_alias);
return is_in_region && !is_in_heap;
default:
return false;
{
if (!is_in_region) {
return false;
}
const auto heap_start = m_region_starts[RegionType_Heap];
const auto heap_end = m_region_ends[RegionType_Heap];
if (heap_start != heap_end && addr < heap_end && heap_start < end) {
return false;
}
const auto alias_start = m_region_starts[RegionType_Alias];
const auto alias_end = m_region_ends[RegionType_Alias];
if (alias_start != alias_end && addr < alias_end && alias_start < end) {
return false;
}
const auto shadow_start = m_region_starts[RegionType_ShadowStack];
const auto shadow_end = m_region_ends[RegionType_ShadowStack];
if (shadow_start != shadow_end && addr < shadow_end && shadow_start < end) {
return false;
}
return true;
}
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
}

View File

@@ -170,6 +170,13 @@ namespace ams::kern {
KAddressSpaceInfo::SetAddressSpaceSize(39, KAddressSpaceInfo::Type_Alias, alias_size);
}
/* Update 42-bit address space infos. */
{
/* Heap should be equal to the total memory size, maximum 128 GB. */
/* Alias/Stack/MapSmall are left at their static defaults. */
KAddressSpaceInfo::SetAddressSpaceSize(42, KAddressSpaceInfo::Type_Heap, std::min(util::AlignUp(total_memory_size, 1_GB), 128_GB));
}
const auto &slab_counts = init::GetSlabResourceCounts();
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_PhysicalMemoryMax, total_memory_size));
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_ThreadCountMax, slab_counts.num_KThread));

View File

@@ -86,6 +86,40 @@ namespace ams::kern::svc {
R_RETURN(KProcess::GetProcessList(out_num_processes, out_process_ids, max_out_count));
}
constexpr size_t GetProcessSlotT0Sz(ams::svc::CreateProcessFlag flags) {
if ((flags & ams::svc::CreateProcessFlag_AddressSpaceMask) == ams::svc::CreateProcessFlag_AddressSpace64Bit64KPage) {
return (64 - 42);
} else {
return (64 - 39);
}
}
KProcess *CreateProcessForAddressSpace(ams::svc::CreateProcessFlag flags) {
/* Find a process slab slot whose root page table is meant for the requested address space. */
const size_t desired_t0sz = GetProcessSlotT0Sz(flags);
KProcess *process = nullptr;
KProcess *rejected[init::SlabCountKProcess];
size_t num_rejected = 0;
while ((process = KProcess::Create()) != nullptr) {
/* Stop once we find a slot whose T0SZ matches. */
if ((KProcessPageTable::GetProcessTcrEl1(process->GetSlabIndex()) & 0x3F) == desired_t0sz) {
break;
}
/* Otherwise, park the process and try the next slot. */
rejected[num_rejected++] = process;
process = nullptr;
}
/* Release every process we parked while searching. */
for (size_t i = 0; i < num_rejected; ++i) {
rejected[i]->Close();
}
return process;
}
Result CreateProcess(ams::svc::Handle *out, const ams::svc::CreateProcessParameter &params, KUserPointer<const uint32_t *> user_caps, int32_t num_caps) {
/* Validate the capabilities pointer. */
R_UNLESS(num_caps >= 0, svc::ResultInvalidPointer());
@@ -219,8 +253,8 @@ namespace ams::kern::svc {
/* Get the current handle table. */
auto &handle_table = GetCurrentProcess().GetHandleTable();
/* Create the new process. */
KProcess *process = KProcess::Create();
/* Create the new process, ensuring its page table slot matches the requested address space. */
KProcess *process = CreateProcessForAddressSpace(static_cast<ams::svc::CreateProcessFlag>(params.flags));
R_UNLESS(process != nullptr, svc::ResultOutOfResource());
/* Ensure that the only reference to the process is in the handle table when we're done. */

View File

@@ -96,6 +96,9 @@ namespace ams::svc {
MemoryState_CodeOut = 0x15,
MemoryState_Coverage = 0x16,
MemoryState_Insecure = 0x17,
MemoryState_Remote = 0x18,
MemoryState_Protected = 0x19,
MemoryState_ShadowStack = 0x1A,
};
enum MemoryPermission : u32 {
@@ -483,6 +486,7 @@ namespace ams::svc {
DebugThreadParam_IdealCore = 2,
DebugThreadParam_CurrentCore = 3,
DebugThreadParam_AffinityMask = 4,
DebugThreadParam_Unknown5 = 5,
};
enum DebugException : u32 {