Merge pull request #3424 from Sonicadvance1/safer_clone_stack_handling

Linux: More safe stack cleanup for clone
This commit is contained in:
Ryan Houdek 2024-02-26 06:59:27 -08:00 committed by GitHub
commit 9687ac51f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 83 additions and 29 deletions

View File

@ -393,22 +393,27 @@ struct StackFramePlusRet {
uint64_t Pad;
};
[[noreturn]]
static void CloneBody(StackFrameData *Data, bool NeedsDataFree) {
uint64_t Result = FEX::HLE::HandleNewClone(Data->Thread, Data->CTX, &Data->NewFrame, &Data->GuestArgs);
auto Stack = Data->GuestArgs.NewStack;
if (NeedsDataFree) {
FEXCore::Allocator::free(Data);
}
FEX::LinuxEmulation::Threads::DeallocateStackObjectAndExit(Stack, Result);
FEX_UNREACHABLE;
}
[[noreturn]]
static void Clone3HandlerRet() {
StackFrameData *Data = (StackFrameData*)alloca(0);
uint64_t Result = FEX::HLE::HandleNewClone(Data->Thread, Data->CTX, &Data->NewFrame, &Data->GuestArgs);
FEX::LinuxEmulation::Threads::DeallocateStackObject(Data->GuestArgs.NewStack);
// To behave like a real clone, we now just need to call exit here
exit(Result);
FEX_UNREACHABLE;
CloneBody(Data, false);
}
static int Clone2HandlerRet(void *arg) {
StackFrameData *Data = (StackFrameData*)arg;
uint64_t Result = FEX::HLE::HandleNewClone(Data->Thread, Data->CTX, &Data->NewFrame, &Data->GuestArgs);
FEX::LinuxEmulation::Threads::DeallocateStackObject(Data->GuestArgs.NewStack);
FEXCore::Allocator::free(arg);
return Result;
CloneBody(Data, true);
}
// Clone3 flags

View File

@ -38,9 +38,11 @@ $end_info$
#define SYSCALL_ARCH_NAME Arm64
#endif
#include "LinuxSyscalls/x64/SyscallsEnum.h"
#define CONCAT_(a, b) a ## b
#define CONCAT(a, b) CONCAT_(a, b)
#define SYSCALL_DEF(name) ( SYSCALL_ARCH_NAME::CONCAT(CONCAT(SYSCALL_, SYSCALL_ARCH_NAME), _##name))
#define SYSCALL_DEF(name) ( HLE::SYSCALL_ARCH_NAME::CONCAT(CONCAT(SYSCALL_, SYSCALL_ARCH_NAME), _##name))
// #define DEBUG_STRACE

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
#include "LinuxSyscalls/Utils/Threads.h"
#include "LinuxSyscalls/Syscalls.h"
#include <FEXCore/Core/Context.h>
#include <FEXCore/Utils/Threads.h>
@ -12,33 +13,53 @@ namespace FEX::LinuxEmulation::Threads {
void *Ptr;
size_t Size;
};
struct DeadStackPoolItem {
void *Ptr;
size_t Size;
bool ReadyToBeReaped;
};
std::mutex DeadStackPoolMutex{};
std::mutex LiveStackPoolMutex{};
static fextl::deque<StackPoolItem> DeadStackPool{};
static fextl::deque<DeadStackPoolItem> DeadStackPool{};
static fextl::deque<StackPoolItem> LiveStackPool{};
void *AllocateStackObject() {
std::lock_guard lk{DeadStackPoolMutex};
if (DeadStackPool.size() == 0) {
// Nothing in the pool, just allocate
return FEXCore::Allocator::mmap(nullptr, FEX::LinuxEmulation::Threads::STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}
// Keep the first item in the stack pool
auto Result = DeadStackPool.front().Ptr;
DeadStackPool.pop_front();
void *Ptr{};
// Erase the rest as a garbage collection step
for (auto &Item : DeadStackPool) {
FEXCore::Allocator::munmap(Item.Ptr, Item.Size);
for (auto it = DeadStackPool.begin(); it != DeadStackPool.end();) {
auto Ready = std::atomic_ref<bool>(it->ReadyToBeReaped);
bool ReadyToBeReaped = Ready.load();
if (Ptr == nullptr && ReadyToBeReaped) {
Ptr = it->Ptr;
it = DeadStackPool.erase(it);
continue;
}
if (ReadyToBeReaped) {
FEXCore::Allocator::munmap(it->Ptr, it->Size);
it = DeadStackPool.erase(it);
continue;
}
++it;
}
return Result;
if (Ptr == nullptr) {
Ptr = FEXCore::Allocator::mmap(nullptr, FEX::LinuxEmulation::Threads::STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}
return Ptr;
}
void AddStackToDeadPool(void *Ptr) {
bool *AddStackToDeadPool(void *Ptr) {
std::lock_guard lk{DeadStackPoolMutex};
DeadStackPool.emplace_back(StackPoolItem{Ptr, FEX::LinuxEmulation::Threads::STACK_SIZE});
auto &it = DeadStackPool.emplace_back(DeadStackPoolItem{Ptr, FEX::LinuxEmulation::Threads::STACK_SIZE, false});
return &it.ReadyToBeReaped;
}
void AddStackToLivePool(void *Ptr) {
@ -61,6 +82,31 @@ namespace FEX::LinuxEmulation::Threads {
AddStackToDeadPool(Ptr);
}
[[noreturn]]
void DeallocateStackObjectAndExit(void *Ptr, int Status) {
RemoveStackFromLivePool(Ptr);
auto ReadyToBeReaped = AddStackToDeadPool(Ptr);
*ReadyToBeReaped = true;
#ifdef _M_ARM_64
__asm volatile(
"mov x8, %[SyscallNum];"
"mov w0, %w[Result];"
"svc #0;"
:: [SyscallNum] "i" (SYSCALL_DEF(exit))
, [Result] "r" (Status)
: "memory", "x0", "x8");
#else
__asm volatile(
"mov %[Result], %%edi;"
"syscall;"
:: "a" (SYSCALL_DEF(exit))
, [Result] "r" (Status)
: "memory", "rdi");
#endif
FEX_UNREACHABLE;
}
namespace PThreads {
void *InitializeThread(void *Ptr);
@ -154,7 +200,7 @@ namespace FEX::LinuxEmulation::Threads {
auto ClearStackPool = [&](auto &StackPool) {
for (auto it = StackPool.begin(); it != StackPool.end(); ) {
StackPoolItem &Item = *it;
auto &Item = *it;
uintptr_t ItemStack = reinterpret_cast<uintptr_t>(Item.Ptr);
if (ItemStack <= StackLocation && (ItemStack + Item.Size) > StackLocation) {
// This is our stack item, skip it

View File

@ -20,8 +20,10 @@ namespace FEX::LinuxEmulation::Threads {
* Will not free the memory immediately, instead saving for reuse temporarily to solve race conditions on stack usage while stack tears down.
*
* @param Ptr The stack base from `AllocateStackObject`
* @param Status The status to pass to the exit syscall.
*/
void DeallocateStackObject(void *Ptr);
[[noreturn]]
void DeallocateStackObjectAndExit(void *Ptr, int Status);
/**
* @brief Registers thread creation handlers with FEXCore.

View File

@ -30,8 +30,6 @@ struct InternalThreadState;
}
namespace FEX::HLE::x64 {
#include "SyscallsEnum.h"
class x64SyscallHandler final : public FEX::HLE::SyscallHandler {
public:
x64SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalDelegator *_SignalDelegation);

View File

@ -6,6 +6,7 @@ $end_info$
*/
#pragma once
namespace FEX::HLE::x64 {
///< Enum containing all x86-64 linux syscalls for the guest kernel version
enum Syscalls_x64 {
SYSCALL_x64_read = 0,
@ -479,4 +480,4 @@ enum Syscalls_x64 {
SYSCALL_x64_futex_time64 = ~0,
SYSCALL_x64_sched_rr_get_interval_time64 = ~0,
};
}