fix(libkernel): release only the main thread stack when moving it off the arena

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
Morten HolmandClaude Sonnet 5.5 committed 2026-10-28 12:43:40 +01:00
1 parent 3b1697cf1c
commit 11ca3cb744
2 files changed
+44

No files matched your search

@@ -91,6 +91,12 @@ HostStack FindHostStack() {
if (!RegionOf(stack.low - 1, begin, end, tag) || tag != VM_MEMORY_STACK || end != stack.low) break;
stack.low = begin;
}
const pthread_t self = pthread_self();
const auto top = reinterpret_cast<std::uintptr_t>(pthread_get_stackaddr_np(self));
const std::uintptr_t size = pthread_get_stacksize_np(self);
stack.low = std::max(stack.low, top - std::min(top, size));
stack.high = std::min(stack.high, top);
if (stack.low >= stack.high) return {};
return stack;
}
+38
View File
@@ -1,5 +1,7 @@
#include "prx/libc/include/GuestArena.hpp"
#include <crt_externs.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <pthread.h>
#include <cstdint>
#include <cstdio>
@@ -14,10 +16,40 @@ namespace {
constexpr std::uintptr_t MappingAlignment = 0x10000;
constexpr std::size_t MappingBytes = 0x10000;
constexpr std::size_t NeighbourBytes = std::size_t {1} << 20;
std::uintptr_t originalStack = 0;
std::string originalArgument;
bool originalInArena = false;
mach_vm_address_t neighbour = 0;
bool RegionOf(mach_vm_address_t address, mach_vm_address_t& begin, mach_vm_address_t& end, std::uint32_t& tag) {
mach_vm_address_t base = address;
mach_vm_size_t size = 0;
natural_t depth = 0;
vm_region_submap_info_data_64_t info {};
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
if (mach_vm_region_recurse(mach_task_self(), &base, &size, &depth, reinterpret_cast<vm_region_recurse_info_t>(&info), &count) != KERN_SUCCESS || base > address) return false;
begin = base;
end = base + size;
tag = info.user_tag;
return true;
}
mach_vm_address_t MapStackNeighbour(std::uintptr_t stack) {
mach_vm_address_t low = 0, end = 0;
std::uint32_t tag = 0;
if (!RegionOf(stack, low, end, tag) || tag != VM_MEMORY_STACK) return 0;
for (;;) {
mach_vm_address_t begin = 0, previousEnd = 0;
if (!RegionOf(low - 1, begin, previousEnd, tag) || tag != VM_MEMORY_STACK || previousEnd != low) break;
low = begin;
}
mach_vm_address_t at = low - NeighbourBytes;
if (mach_vm_deallocate(mach_task_self(), at, NeighbourBytes) != KERN_SUCCESS) return 0;
if (mach_vm_allocate(mach_task_self(), &at, NeighbourBytes, VM_MAKE_TAG(VM_MEMORY_STACK)) != KERN_SUCCESS) return 0;
return at;
}
void Require(bool value, const char* what) {
if (value) return;
@@ -47,6 +79,11 @@ void CheckMainThread(void*) {
}
GuestArena::GuestArenaRelease_nid_postfix(old, MappingBytes);
}
if (neighbour != 0) {
mach_vm_address_t begin = 0, end = 0;
std::uint32_t tag = 0;
Require(RegionOf(neighbour, begin, end, tag) && tag == VM_MEMORY_STACK, "a stack region next to the main stack survived the move");
}
Require(originalArgument == (*_NSGetArgv())[0], "argv survived the move");
Require(std::getenv("PATH") != nullptr, "the environment survived the move");
}
@@ -63,5 +100,6 @@ int main(int, char** argv) {
originalStack = reinterpret_cast<std::uintptr_t>(&local);
originalInArena = InArena(originalStack);
originalArgument = argv[0];
if (originalInArena) neighbour = MapStackNeighbour(originalStack);
Aps5StartGuest_nid_no_patch(Guest, nullptr);
}