Skip to content

Commit

Permalink
Linux: Add support for using set_robust_list2
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Sonicadvance1 committed Oct 11, 2024
1 parent 389ad73 commit a8ec5b0
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,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
Expand Down
4 changes: 4 additions & 0 deletions Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ desc: Glue logic, brk allocations
#include <unistd.h>

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 {};

Expand Down
9 changes: 9 additions & 0 deletions Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,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;
class ThunkHandler;
Expand Down
19 changes: 15 additions & 4 deletions Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Passthrough.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SYSCALL_DEF(set_robust_list2)>);
} else {
REGISTER_SYSCALL_IMPL(set_robust_list2, UnimplementedSyscallSafe);
}
}

namespace x64 {
Expand Down Expand Up @@ -730,10 +738,13 @@ namespace x64 {
SyscallPassthrough6<SYSCALL_DEF(pselect6)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(ppoll, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(ppoll)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(set_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(set_robust_list)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(get_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(get_robust_list)>);
if (!ENABLE_ROBUST_LIST2) {
// Gets handled in x64/Thread.cpp
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(set_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(set_robust_list)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(get_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(get_robust_list)>);
}
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sync_file_range, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(sync_file_range)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(vmsplice, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
Expand Down
10 changes: 9 additions & 1 deletion Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ struct ThreadStateObject : public FEXCore::Allocator::FEXAllocOperators {
std::atomic<uint32_t> 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
59 changes: 43 additions & 16 deletions Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,30 +179,57 @@ 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<uint64_t>(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<uint64_t>(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<uint32_t>(reinterpret_cast<uint64_t>(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 {
FaultSafeUserMemAccess::VerifyIsWritable(head, sizeof(uint32_t));
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,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
Expand Down
37 changes: 37 additions & 0 deletions Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,42 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) {
auto* const* EnvpPtr = envp ? const_cast<char* const*>(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<uint64_t>(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

0 comments on commit a8ec5b0

Please sign in to comment.