From 131bf48f4bb83d2ca2e5b9918ba2a00cf76375c2 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Wed, 6 Dec 2023 00:57:37 -0800 Subject: [PATCH] Linux: Adjust when clone allocates stack memory clone needs to allocate memory before the fork locks are held, otherwise they will hang forever waiting on a locked mutex. This wasn't previously seen since nothing was using fork with clone3. --- .../FEXLoader/LinuxSyscalls/Syscalls.cpp | 30 ++++++++----------- .../Tools/FEXLoader/LinuxSyscalls/Syscalls.h | 4 +++ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/Syscalls.cpp b/Source/Tools/FEXLoader/LinuxSyscalls/Syscalls.cpp index 1a2849670..402f2df76 100644 --- a/Source/Tools/FEXLoader/LinuxSyscalls/Syscalls.cpp +++ b/Source/Tools/FEXLoader/LinuxSyscalls/Syscalls.cpp @@ -385,7 +385,6 @@ struct StackFrameData { FEXCore::Context::Context *CTX{}; FEXCore::Core::CpuStateFrame NewFrame{}; FEX::HLE::clone3_args GuestArgs{}; - void *NewStack; size_t StackSize; }; @@ -399,7 +398,7 @@ struct StackFramePlusRet { 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->NewStack); + 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; @@ -408,7 +407,7 @@ static void Clone3HandlerRet() { 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->NewStack); + FEX::LinuxEmulation::Threads::DeallocateStackObject(Data->GuestArgs.NewStack); FEXCore::Allocator::free(arg); return Result; } @@ -461,10 +460,6 @@ static uint64_t Clone2Handler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clo Data->CTX = Frame->Thread->CTX; Data->GuestArgs = *args; - // In the case of thread, we need a new stack - Data->StackSize = FEX::LinuxEmulation::Threads::STACK_SIZE; - Data->NewStack = FEX::LinuxEmulation::Threads::AllocateStackObject(); - // Create a copy of the parent frame memcpy(&Data->NewFrame, Frame, sizeof(FEXCore::Core::CpuStateFrame)); @@ -474,7 +469,7 @@ static uint64_t Clone2Handler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clo uint64_t Flags = args->args.flags & ~INVALID_FOR_HOST; uint64_t Result = ::clone( Clone2HandlerRet, // To be called function - (void*)((uint64_t)Data->NewStack + Data->StackSize), // Stack + (void*)((uint64_t)args->NewStack + Data->StackSize), // Stack Flags, //Flags Data, //Argument (pid_t*)args->args.parent_tid, // parent_tid @@ -486,20 +481,13 @@ static uint64_t Clone2Handler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clo } static uint64_t Clone3Handler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args) { - // In the case of thread, we need a new stack - const uint64_t StackSize = FEX::LinuxEmulation::Threads::STACK_SIZE; - void *NewStack = FEX::LinuxEmulation::Threads::AllocateStackObject(); - constexpr size_t Offset = sizeof(StackFramePlusRet); - StackFramePlusRet *Data = (StackFramePlusRet*)(reinterpret_cast(NewStack) + StackSize - Offset); + StackFramePlusRet *Data = (StackFramePlusRet*)(reinterpret_cast(args->NewStack) + args->StackSize - Offset); Data->Ret = (uint64_t)Clone3HandlerRet; Data->Data.Thread = Frame->Thread; Data->Data.CTX = Frame->Thread->CTX; Data->Data.GuestArgs = *args; - Data->Data.StackSize = StackSize; - Data->Data.NewStack = NewStack; - FEX::HLE::kernel_clone3_args HostArgs{}; HostArgs.flags = args->args.flags; HostArgs.pidfd = args->args.pidfd; @@ -507,8 +495,8 @@ static uint64_t Clone3Handler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clo HostArgs.parent_tid = args->args.parent_tid; HostArgs.exit_signal = args->args.exit_signal; // Host stack is always created - HostArgs.stack = reinterpret_cast(NewStack); - HostArgs.stack_size = StackSize - Offset; // Needs to be 16 byte aligned + HostArgs.stack = reinterpret_cast(args->NewStack); + HostArgs.stack_size = args->StackSize - Offset; // Needs to be 16 byte aligned HostArgs.tls = 0; // XXX: What is correct for this? HostArgs.set_tid = args->args.set_tid; HostArgs.set_tid_size= args->args.set_tid_size; @@ -597,6 +585,12 @@ uint64_t CloneHandler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args args->SignalMask = ~0ULL; ::syscall(SYS_rt_sigprocmask, SIG_SETMASK, &args->SignalMask, &args->SignalMask, sizeof(args->SignalMask)); + + // Need to create a stack for the host thread. + // LockBeforeFork grabs the allocator mutex to block allocations temporarily, so this must be allocated before + args->StackSize = FEX::LinuxEmulation::Threads::STACK_SIZE; + args->NewStack = FEX::LinuxEmulation::Threads::AllocateStackObject(); + Thread->CTX->LockBeforeFork(Frame->Thread); FEX::HLE::_SyscallHandler->LockBeforeFork(); diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/Syscalls.h b/Source/Tools/FEXLoader/LinuxSyscalls/Syscalls.h index 11de00ee8..c122e24bd 100644 --- a/Source/Tools/FEXLoader/LinuxSyscalls/Syscalls.h +++ b/Source/Tools/FEXLoader/LinuxSyscalls/Syscalls.h @@ -432,6 +432,10 @@ enum TypeOfClone { struct clone3_args { TypeOfClone Type; uint64_t SignalMask; + + uint64_t StackSize; + void *NewStack; + kernel_clone3_args args; };