From 42e962a8176d8a9ce20da38d4d7498ee1f9cb36a Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 16 Aug 2024 13:57:36 -0700 Subject: [PATCH] Linux: Add support for using set_robust_list2 Let FEX use this for both 32-bit and 64-bit robust futex lists, also wire it up so guest applications can use it. Currently no kernel version check wired up, and syscall number is subject to change. --- .../LinuxSyscalls/Arm64/SyscallsEnum.h | 1 + .../LinuxEmulation/LinuxSyscalls/Syscalls.cpp | 4 ++ .../LinuxEmulation/LinuxSyscalls/Syscalls.h | 9 +++ .../LinuxSyscalls/Syscalls/Passthrough.cpp | 19 ++++-- .../LinuxSyscalls/ThreadManager.h | 10 +++- .../LinuxSyscalls/x32/SyscallsEnum.h | 1 + .../LinuxSyscalls/x32/Thread.cpp | 59 ++++++++++++++----- .../LinuxSyscalls/x64/SyscallsEnum.h | 1 + .../LinuxSyscalls/x64/Thread.cpp | 37 ++++++++++++ 9 files changed, 120 insertions(+), 21 deletions(-) diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Arm64/SyscallsEnum.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Arm64/SyscallsEnum.h index 55a53d83e5..ce70dbe3fb 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Arm64/SyscallsEnum.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Arm64/SyscallsEnum.h @@ -349,6 +349,7 @@ enum Syscalls_Arm64 { SYSCALL_Arm64_lsm_set_self_attr = 460, SYSCALL_Arm64_lsm_list_modules = 461, SYSCALL_Arm64_mseal = 462, + SYSCALL_Arm64_set_robust_list2 = 468, // Subject to change SYSCALL_Arm64_MAX = 512, // Unsupported syscalls on this host diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp index 68ef77714c..5f70477c05 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp @@ -61,6 +61,10 @@ desc: Glue logic, brk allocations #include namespace FEX::HLE { +int set_robust_list2(struct robust_list_head* head, int index, uint32_t flags) { + return ::syscall(SYSCALL_DEF(set_robust_list2), head, index, flags); +} + class SignalDelegator; SyscallHandler* _SyscallHandler {}; diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h index d864f37cd1..a96e87578d 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h @@ -64,6 +64,15 @@ namespace Core { namespace FEX::HLE { +enum robust_list_type { + ROBUST_LIST_32BIT = 0, + ROBUST_LIST_64BIT = 1, +}; + +// TODO: Switch to a kernel version check where used. +constexpr bool ENABLE_ROBUST_LIST2 = true; +int set_robust_list2(struct robust_list_head* head, int index, uint32_t flags); + class SyscallHandler; class SignalDelegator; void RegisterEpoll(FEX::HLE::SyscallHandler* Handler); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Passthrough.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Passthrough.cpp index 553b10fd38..109e0130c5 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Passthrough.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Passthrough.cpp @@ -624,6 +624,14 @@ void RegisterCommon(FEX::HLE::SyscallHandler* Handler) { } else { REGISTER_SYSCALL_IMPL(mseal, UnimplementedSyscallSafe); } + + // Kernel version subject to change + if (Handler->IsHostKernelVersionAtLeast(6, 11, 0)) { + REGISTER_SYSCALL_IMPL_PASS_FLAGS(set_robust_list2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, + SyscallPassthrough3); + } else { + REGISTER_SYSCALL_IMPL(set_robust_list2, UnimplementedSyscallSafe); + } } namespace x64 { @@ -730,10 +738,13 @@ namespace x64 { SyscallPassthrough6); REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(ppoll, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, SyscallPassthrough5); - REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(set_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, - SyscallPassthrough2); - REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(get_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, - SyscallPassthrough3); + if (!ENABLE_ROBUST_LIST2) { + // Gets handled in x64/Thread.cpp + REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(set_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, + SyscallPassthrough2); + REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(get_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, + SyscallPassthrough3); + } REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sync_file_range, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, SyscallPassthrough4); REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(vmsplice, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.h index ebc02c80f2..6e24e4820d 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.h @@ -31,7 +31,15 @@ struct ThreadStateObject : public FEXCore::Allocator::FEXAllocOperators { std::atomic TID; int32_t* set_child_tid {0}; int32_t* clear_child_tid {0}; - uint64_t robust_list_head {0}; + + uint64_t robust_list_head_x64 {0}; + uint64_t robust_list_head_x32 {0}; + + // Robust list index mappings. + // Start off as -1 to allow dynamic slot allocation from the kernel. + int robust_list_index_x64 {-1}; + int robust_list_index_x32 {-1}; + } ThreadInfo {}; struct { diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/SyscallsEnum.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/SyscallsEnum.h index 6397535ad2..47d898ea8d 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/SyscallsEnum.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/SyscallsEnum.h @@ -480,6 +480,7 @@ enum Syscalls_x86 { SYSCALL_x86_lsm_set_self_attr = 460, SYSCALL_x86_lsm_list_modules = 461, SYSCALL_x86_mseal = 462, + SYSCALL_x86_set_robust_list2 = 468, // Subject to change SYSCALL_x86_MAX = 512, }; } // namespace FEX::HLE::x32 diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Thread.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Thread.cpp index 332bb1a346..86f6adadea 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Thread.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Thread.cpp @@ -179,18 +179,38 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) { return 0; }); - REGISTER_SYSCALL_IMPL_X32(set_robust_list, [](FEXCore::Core::CpuStateFrame* Frame, struct robust_list_head* head, size_t len) -> uint64_t { - if (len != 12) { - // Return invalid if the passed in length doesn't match what's expected. - return -EINVAL; - } + if (ENABLE_ROBUST_LIST2) { + REGISTER_SYSCALL_IMPL_X32(set_robust_list, [](FEXCore::Core::CpuStateFrame* Frame, struct robust_list_head* head, size_t len) -> uint64_t { + if (len != 12) { + // Return invalid if the passed in length doesn't match what's expected. + return -EINVAL; + } - auto ThreadObject = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); - // Retain the robust list head but don't give it to the kernel - // The kernel would break if it tried parsing a 32bit robust list from a 64bit process - ThreadObject->ThreadInfo.robust_list_head = reinterpret_cast(head); - return 0; - }); + auto ThreadObject = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); + int Result = FEX::HLE::set_robust_list2(head, ThreadObject->ThreadInfo.robust_list_index_x32, FEX::HLE::ROBUST_LIST_32BIT); + if (Result != -1) { + // Index is returned back to us. + ThreadObject->ThreadInfo.robust_list_index_x32 = Result; + ThreadObject->ThreadInfo.robust_list_head_x32 = reinterpret_cast(head); + Result = 0; + } + + SYSCALL_ERRNO(); + }); + } else { + REGISTER_SYSCALL_IMPL_X32(set_robust_list, [](FEXCore::Core::CpuStateFrame* Frame, struct robust_list_head* head, size_t len) -> uint64_t { + if (len != 12) { + // Return invalid if the passed in length doesn't match what's expected. + return -EINVAL; + } + + auto ThreadObject = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); + // Retain the robust list head but don't give it to the kernel + // The kernel would break if it tried parsing a 32bit robust list from a 64bit process + ThreadObject->ThreadInfo.robust_list_head_x32 = static_cast(reinterpret_cast(head)); + return 0; + }); + } REGISTER_SYSCALL_IMPL_X32( get_robust_list, [](FEXCore::Core::CpuStateFrame* Frame, int pid, struct robust_list_head** head, uint32_t* len_ptr) -> uint64_t { @@ -198,11 +218,18 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) { FaultSafeUserMemAccess::VerifyIsWritable(len_ptr, sizeof(*len_ptr)); auto ThreadObject = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); - // Give the robust list back to the application - // Steam specifically checks to make sure the robust list is set - *(uint32_t*)head = (uint32_t)ThreadObject->ThreadInfo.robust_list_head; - *len_ptr = 12; - return 0; + + if (pid == 0 || pid == ThreadObject->ThreadInfo.PID) { + FaultSafeUserMemAccess::VerifyIsWritable(head, sizeof(uint32_t)); + + // Give the robust list back to the application + // Steam specifically checks to make sure the robust list is set + *(uint32_t*)head = (uint32_t)ThreadObject->ThreadInfo.robust_list_head_x32; + *len_ptr = 12; + return 0; + } + + return -EPERM; }); REGISTER_SYSCALL_IMPL_X32( diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/SyscallsEnum.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/SyscallsEnum.h index 99cec523ea..036761a748 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/SyscallsEnum.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/SyscallsEnum.h @@ -399,6 +399,7 @@ enum Syscalls_x64 { SYSCALL_x64_lsm_set_self_attr = 460, SYSCALL_x64_lsm_list_modules = 461, SYSCALL_x64_mseal = 462, + SYSCALL_x64_set_robust_list2 = 468, // Subject to change SYSCALL_x64_MAX = 512, // Unsupported syscalls on this host diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Thread.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Thread.cpp index 3d57912708..ed4bf59bc5 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Thread.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Thread.cpp @@ -137,5 +137,42 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) { auto* const* EnvpPtr = envp ? const_cast(Envp.data()) : nullptr; return FEX::HLE::ExecveHandler(Frame, pathname, ArgsPtr, EnvpPtr, AtArgs); })); + + if (ENABLE_ROBUST_LIST2) { + REGISTER_SYSCALL_IMPL_X64(set_robust_list, [](FEXCore::Core::CpuStateFrame* Frame, struct robust_list_head* head, size_t len) -> uint64_t { + if (len != sizeof(struct robust_list_head)) { + // Return invalid if the passed in length doesn't match what's expected. + return -EINVAL; + } + + auto ThreadObject = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); + int Result = FEX::HLE::set_robust_list2(head, ThreadObject->ThreadInfo.robust_list_index_x64, FEX::HLE::ROBUST_LIST_64BIT); + if (Result != -1) { + // Index is returned back to us. + ThreadObject->ThreadInfo.robust_list_index_x64 = Result; + ThreadObject->ThreadInfo.robust_list_head_x64 = reinterpret_cast(head); + Result = 0; + } + + SYSCALL_ERRNO(); + }); + + REGISTER_SYSCALL_IMPL_X64( + get_robust_list, [](FEXCore::Core::CpuStateFrame* Frame, int pid, struct robust_list_head** head, uint32_t* len_ptr) -> uint64_t { + auto ThreadObject = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); + + if (pid == 0 || pid == ThreadObject->ThreadInfo.PID) { + FaultSafeUserMemAccess::VerifyIsWritable(head, sizeof(uint64_t)); + + // Give the robust list back to the application + // Steam specifically checks to make sure the robust list is set + *(uint64_t*)head = ThreadObject->ThreadInfo.robust_list_head_x64; + *len_ptr = sizeof(struct robust_list_head); + return 0; + } + + return -EPERM; + }); + } } } // namespace FEX::HLE::x64