mirror of
https://github.com/FEX-Emu/FEX.git
synced 2025-03-05 13:08:23 +00:00
Merge pull request #3424 from Sonicadvance1/safer_clone_stack_handling
Linux: More safe stack cleanup for clone
This commit is contained in:
commit
9687ac51f0
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user