// SPDX-License-Identifier: MIT /* $info$ tags: LinuxSyscalls|syscalls-shared $end_info$ */ #include "LinuxSyscalls/Seccomp/BPFEmitter.h" #include "LinuxSyscalls/Seccomp/SeccompEmulator.h" #include "LinuxSyscalls/x32/Syscalls.h" #include "LinuxSyscalls/x64/Syscalls.h" #include "LinuxSyscalls/SignalDelegator.h" #include #include #include #include #include #include #include #include #include #include #include // seccomp // // global // - kcmp - pass // - mode_strict_support - pass // - mode_strict_cannot_call_prctl - pass // - no_new_privs_support - pass // - mode_filter_support - pass // - mode_filter_without_nnp - pass // - filter_size_limits - pass // - filter_chain_limits - pass // - mode_filter_cannot_move_to_strict - pass // - mode_filter_get_seccomp - pass // - ALLOW_all - pass // - empty_prog - pass // - log_all - pass // - unknown_ret_is_kill_inside - pass // - unknown_ret_is_kill_above_allow - pass // - KILL_all - pass // - KILL_one - pass // - KILL_one_arg_one - pass // - KILL_one_arg_six - pass // - KILL_thread - FAIL (unrelated to bpf) // - KILL_process - FAIL (unrelated to bpf) // - KILL_unknown - FAIL (unrelated to bpf) // - arg_out_of_range - pass // - ERRNO_valid - pass // - ERRNO_zero - pass // - ERRNO_capped - pass // - ERRNO_order - pass // - seccomp_syscall - pass // - seccomp_syscall_mode_lock - pass // - detect_seccomp_filter_flags - pass // - TSYNC_first - pass // - syscall_restart - FAIL (PTRACE) // - filter_flag_log - pass // - get_action_avail - FAIL (ptrace and user-notif) // TSYNC // - siblings_fail_prctl - pass // - two_siblings_with_ancestor - FAIL (kill-thread not working quite right) // - two_sibling_want_nnp - pass // - two_siblings_with_one_divergence - pass // - two_siblings_with_one_divergence_no_tid_in_err - pass // - two_siblings_not_under_filter - FAIL (kill-thread not working quite right) // - two_siblings_with_no_filter - FAIL (kill-thread not working quite right) // // user-notif stuff // - get_metadata - SKIP (Needs root) // - user_notification_basic - FAIL (user-notif) // - user_notification_with_tsync - FAIL (user-notif) // - user_notification_kill_in_middle - FAIL (user-notif) // - user_notification_signal - FAIL (user-notif) // - user_notification_closed_listener - FAIL (user-notif) // - user_notification_child_pid_ns - FAIL (user-notif) // - user_notification_sibling_pid_ns - FAIL (user-notif) // - user_notification_fault_recv - FAIL (user-notif) // - seccomp_get_notif_sizes - pass // - user_notification_continue - FAIL (user-notif) // - user_notification_filter_empty - FAIL (user-notif) // - user_notification_filter_empty_threaded - FAIL (user-notif) // - user_notification_addfd - FAIL (user-notif) // - user_notification_addfd_rlimit - FAIL (user-notif) // - user_notification_sync - FAIL (user-notif) // - user_notification_fifo - FAIL (user-notif) // - user_notification_wait_killable_pre_notification - FAIL (user-notif) // - user_notification_wait_killable - FAIL (user-notif) // - user_notification_wait_killable_fatal - FAIL (user-notif) // // O_SUSPEND_SECCOMP // - setoptions - FAIL (ptrace) // - seize - FAIL (ptrace) // TRAP // - dfl - pass // - ign - pass // - handler - pass // // precedence // - allow_ok - pass // - kill_is_highest - pass // - kill_is_highest_in_any_order - pass // - trap_is_second - pass // - trap_is_second_in_any_order - pass // - errno_is_third - pass // - errno_is_third_in_any_order - pass // - trace_is_fourth - pass // - trace_is_fourth_in_any_order - pass // - log_is_fifth - pass // - log_is_fifth_in_any_order - pass // // TRACE_poke // - ptrace unsupported // TRACE_syscall // - ptrace unsupported namespace FEX::HLE { uint64_t SeccompEmulator::Handle(FEXCore::Core::CpuStateFrame* Frame, uint32_t Op, uint32_t flags, void* arg) { // If seccomp isn't enabled then say so. if (!NeedsSeccomp) { return -EINVAL; } switch (Op) { case SECCOMP_SET_MODE_STRICT: return SetModeStrict(Frame, flags, arg); case SECCOMP_SET_MODE_FILTER: return SetModeFilter(Frame, flags, static_cast(arg)); case SECCOMP_GET_ACTION_AVAIL: return GetActionAvail(flags, static_cast(arg)); case SECCOMP_GET_NOTIF_SIZES: return GetNotifSizes(flags, static_cast(arg)); default: // operation is unknown or is not supported by this kernel version or configuration. return -EINVAL; } } // Equivalent to prctl(PR_GET_SECCOMP) uint64_t SeccompEmulator::GetSeccomp(FEXCore::Core::CpuStateFrame* Frame) { // If seccomp isn't enabled then say so. if (!NeedsSeccomp) { return -EINVAL; } auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); return Thread->SeccompMode; } void SeccompEmulator::InheritSeccompFilters(FEX::HLE::ThreadStateObject* Parent, FEX::HLE::ThreadStateObject* Child) { // Don't interrupt me while I'm copying. auto lk = FEXCore::MaskSignalsAndLockMutex(FilterMutex); Child->Filters.resize(Parent->Filters.size()); for (size_t i = 0; i < Child->Filters.size(); ++i) { auto& ParentFilter = Parent->Filters[i]; auto& ChildFilter = Child->Filters[i]; ChildFilter = ParentFilter; std::atomic_ref(ParentFilter->RefCount)++; } // Copy the operating mode. Child->SeccompMode = Parent->SeccompMode; } void SeccompEmulator::FreeSeccompFilters(FEX::HLE::ThreadStateObject* Thread) { // Don't talk to me when I'm busy deleting myself. auto lk = FEXCore::MaskSignalsAndLockMutex(FilterMutex); bool HasFiltersToDelete {}; for (auto& Filter : Thread->Filters) { auto RefCount = std::atomic_ref(Filter->RefCount).fetch_sub(1); if (RefCount == 1) { HasFiltersToDelete = true; } } Thread->Filters.clear(); if (HasFiltersToDelete) { // Garbage collect filters std::erase_if(Filters, [](auto& Filter) { if (std::atomic_ref(Filter.RefCount).load(std::memory_order_relaxed) != 0) { return false; } FEXCore::Allocator::munmap(reinterpret_cast(Filter.Func), Filter.MappedSize); return true; }); } } struct SerializedFilter { size_t CodeSize; uint32_t FilterInstructions; bool ShouldLog; char Code[]; }; struct SerializationHeader { size_t NumberOfFilters; uint32_t SeccompMode; SerializedFilter Filters[]; }; std::optional SeccompEmulator::SerializeFilters(FEXCore::Core::CpuStateFrame* Frame) { auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); if (Thread->SeccompMode == SECCOMP_MODE_DISABLED) { // Didn't have seccomp enabled. return std::nullopt; } int FD = memfd_create("seccomp_filters", MFD_ALLOW_SEALING); if (FD == -1) { // Couldn't create memfd LogMan::Msg::EFmt("Couldn't create seccomp filter FD!"); return -1; } SerializationHeader Header { .NumberOfFilters = Thread->Filters.size(), .SeccompMode = Thread->SeccompMode, }; int Res = write(FD, &Header, sizeof(Header)); if (Res == -1) { LogMan::Msg::EFmt("Couldn't write header!"); close(FD); return -1; } for (auto& Filter : Thread->Filters) { SerializedFilter SFilter { .CodeSize = Filter->MappedSize, .FilterInstructions = Filter->FilterInstructions, .ShouldLog = Filter->ShouldLog, }; Res = write(FD, &SFilter, sizeof(SFilter)); if (Res == -1) { LogMan::Msg::EFmt("Couldn't write filter header!"); close(FD); return -1; } Res = write(FD, (const void*)Filter->Func, Filter->MappedSize); if (Res == -1) { LogMan::Msg::EFmt("Couldn't write filter!"); close(FD); return -1; } } // Reset FD to start. lseek(FD, 0, SEEK_SET); // Seal everything about this FD. if (fcntl(FD, F_ADD_SEALS, F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_FUTURE_WRITE) == -1) { LogMan::Msg::IFmt("Couldn't seal seccomp serialize FD. Nefarious code could modify"); } return FD; } void SeccompEmulator::DeserializeFilters(FEXCore::Core::CpuStateFrame* Frame, int FD) { auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); SerializationHeader Header; int Res = read(FD, &Header, sizeof(Header)); if (Res == -1 || Res != sizeof(Header)) { LogMan::Msg::EFmt("Couldn't read Seccomp header!"); close(FD); return; } for (size_t i = 0; i < Header.NumberOfFilters; ++i) { SerializedFilter SFilter; Res = read(FD, &SFilter, sizeof(SFilter)); if (Res == -1 || Res != sizeof(SFilter)) { LogMan::Msg::EFmt("Couldn't read Seccomp Filter header!"); close(FD); return; } auto Ptr = FEXCore::Allocator::mmap(nullptr, SFilter.CodeSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (Ptr == (void*)~0ULL) { LogMan::Msg::EFmt("Couldn't allocate ptr for filter!"); close(FD); return; } Res = read(FD, Ptr, SFilter.CodeSize); if (Res == -1 || Res != SFilter.CodeSize) { LogMan::Msg::EFmt("Couldn't read Seccomp Filter code!"); close(FD); return; } ::mprotect(Ptr, SFilter.CodeSize, PROT_READ | PROT_EXEC); auto& it = Filters.emplace_back(SeccompFilterInfo {(SeccompFilterFunc)Ptr, 1, SFilter.CodeSize, SFilter.FilterInstructions, SFilter.ShouldLog}); TotalFilterInstructions += SFilter.FilterInstructions; // Append the filter to the thread. Thread->Filters.emplace_back(&it); } Thread->SeccompMode = Header.SeccompMode; close(FD); } SeccompEmulator::ExecuteFilterResult SeccompEmulator::ExecuteFilter(FEXCore::Core::CpuStateFrame* Frame, uint64_t JITPC, FEXCore::HLE::SyscallArguments* Args) { auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); if (Thread->Filters.empty()) { // Seccomp not installed. Allow it. return {false, 0}; } // Reconstruct the RIP from the JITPC. const uint64_t RIP = Thread->Thread->CTX->RestoreRIPFromHostPC(Frame->Thread, JITPC); const auto Arch = Is64BitMode() ? AUDIT_ARCH_X86_64 : AUDIT_ARCH_I386; bool ShouldLog {}; uint32_t SeccompResult {}; { BPFEmitter::WorkingBuffer Data { .Data = { .nr = static_cast(Args->Argument[0]), .arch = Arch, .instruction_pointer = RIP, .args = { Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Args->Argument[5], Args->Argument[6], }, }, }; bool HasResult {}; // seccomp filters are executed from latest added to oldest. for (auto it = Thread->Filters.rbegin(); it != Thread->Filters.rend(); ++it) { // Explicitly zero scratch memory. memset(&Data.ScratchMemory, 0, sizeof(Data.ScratchMemory)); uint32_t CurrentResult = (*it)->Func(0, 0, 0, 0, &Data); if (!HasResult) { SeccompResult = CurrentResult; ShouldLog = (*it)->ShouldLog; HasResult = true; continue; } const int16_t CurrentAction = (CurrentResult & SECCOMP_RET_ACTION_FULL) >> 16; const int16_t Action = (SeccompResult & SECCOMP_RET_ACTION_FULL) >> 16; // All actions are executed but the first highest precendent result is returned. // Precedent order from highest priority to lowest: // - SECCOMP_RET_KILL_PROCESS (0x8000, -32768) // - SECCOMP_RET_KILL_THREAD (0x0000, 0) // - SECCOMP_RET_TRAP (0x0003, 3) // - SECCOMP_RET_ERRNO (0x0005, 5) // - SECCOMP_RET_USER_NOTIF (0x7fc0, 32704) // - SECCOMP_RET_TRACE (0x7ff0, 32752) // - SECCOMP_RET_LOG (0x7ffc, 32764) // - SECCOMP_RET_ALLOW (0x7fff, 32767) if (CurrentAction < Action) { SeccompResult = CurrentResult; ShouldLog = (*it)->ShouldLog; } } } const auto ActionMasked = SeccompResult & SECCOMP_RET_ACTION_FULL; const auto DataMasked = SeccompResult & SECCOMP_RET_DATA; // Logging rules // - Log if explicitly returning SECCOMP_RET_LOG // - Log if the filter enabled the logging flag and the action is something other than SECCOMP_RET_ALLOW. if ((ShouldLog && ActionMasked != SECCOMP_RET_ALLOW) || ActionMasked == SECCOMP_RET_LOG) { int Signal = 0; switch (ActionMasked) { case SECCOMP_RET_KILL_PROCESS: case SECCOMP_RET_KILL_THREAD: Signal = GetKillSignal(); break; case SECCOMP_RET_TRAP: Signal = SIGSYS; break; default: break; } // With real secommp the logs go to dmesg. log through FEX since we can't use dmesg. // ex: `[13572.669277] audit: type=1326 audit(1715469332.533:62): auid=1000 uid=1000 gid=1000 ses=2 subj=unconfined pid=52546 comm="seccomp_bpf" // exe="/mnt/Work/Projects/work/linux/tools/testing/selftests/seccomp/seccomp_bpf" sig=0 arch=c000003e syscall=39 compat=0 ip=0x7d789352725d code=0x7ffc0000` timespec tp {}; clock_gettime(CLOCK_MONOTONIC, &tp); LogMan::Msg::IFmt("audit: type={} audit({}.{:03}:{}): uid={} gid={} pid={} comm={} sig={} arch={:x} syscall={} ip=0x{:x} code=0x{:x}", AUDIT_SECCOMP, tp.tv_sec, tp.tv_nsec / 1'000'000, AuditSerialIncrement(), ::getuid(), ::getgid(), ::getpid(), Filename(), Signal, Arch, Args->Argument[0], RIP, SeccompResult); } switch (ActionMasked) { // Unknown actions behave like RET_KILL_PROCESS. default: case SECCOMP_RET_KILL_PROCESS: { const int KillSignal = GetKillSignal(); // Ignores signal handler and sigmask uint64_t Mask = 1ULL << (KillSignal - 1); SignalDelegation->GuestSigProcMask(Thread, SIG_UNBLOCK, &Mask, nullptr); SignalDelegation->UninstallHostHandler(KillSignal); kill(0, KillSignal); break; } case SECCOMP_RET_KILL_THREAD: { // Ignores signal handler and sigmask uint64_t Mask = 1 << (SIGSYS - 1); SignalDelegation->GuestSigProcMask(Thread, SIG_UNBLOCK, &Mask, nullptr); SignalDelegation->UninstallHostHandler(SIGSYS); tgkill(::getpid(), ::gettid(), SIGSYS); break; } case SECCOMP_RET_TRAP: { siginfo_t Info { .si_signo = SIGSYS, .si_errno = static_cast(DataMasked), .si_code = 1, // SYS_SECCOMP }; Info.si_call_addr = reinterpret_cast(RIP); Info.si_syscall = Args->Argument[0]; Info.si_arch = Arch; SignalDelegation->QueueSignal(::getpid(), ::gettid(), SIGSYS, &Info, true); break; } case SECCOMP_RET_ERRNO: { // errno return is clamped. return {true, -(std::min(DataMasked, 4095))}; } case SECCOMP_RET_TRACE: { // When no tracer attached, behave like RET_ERRNO returning ENOSYS. // TODO: Implement once FEX supports tracing. return {true, static_cast(-ENOSYS)}; } case SECCOMP_RET_USER_NOTIF: case SECCOMP_RET_LOG: case SECCOMP_RET_ALLOW: break; } return {false, 0}; } // Equivalent to seccomp(SECCOMP_SET_MODE_STRICT, ...); uint64_t SeccompEmulator::SetModeStrict(FEXCore::Core::CpuStateFrame* Frame, uint32_t flags, const void* arg) { const auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); if (::prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 0) { // The caller did not have the CAP_SYS_ADMIN capability in its user namespace, or had not set no_new_privs before using SECCOMP_SET_MODE_FILTER. return -EACCES; } if (flags != 0) { // The specified flags are invalid for the given operation. return -EINVAL; } if (arg != nullptr) { // The specified arg are invalid for the given operation. return -EINVAL; } if (Thread->SeccompMode == SECCOMP_MODE_FILTER) { // Filter mode cannot move to strict return -EINVAL; } #define syscall_nr (offsetof(struct seccomp_data, nr)) #define ALLOW_SYSCALL(name) \ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, FEX::HLE::x64::SYSCALL_x64_##name, 0, 1), BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) #define ALLOW_SYSCALL_x32(name) \ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, FEX::HLE::x32::SYSCALL_x86_##name, 0, 1), BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) constexpr static struct sock_filter strict_filter_x64[] = { // Load syscall number BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr), // Allow read, write, exit, exit_group, and sigreturn ALLOW_SYSCALL(read), ALLOW_SYSCALL(write), ALLOW_SYSCALL(exit), ALLOW_SYSCALL(exit_group), ALLOW_SYSCALL(rt_sigreturn), BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS), }; constexpr static struct sock_filter strict_filter_x32[] = { // Load syscall number BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr), // Allow read, write, exit, exit_group, and sigreturn ALLOW_SYSCALL_x32(read), ALLOW_SYSCALL_x32(write), ALLOW_SYSCALL_x32(exit), ALLOW_SYSCALL_x32(exit_group), ALLOW_SYSCALL_x32(rt_sigreturn), ALLOW_SYSCALL_x32(sigreturn), BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS), }; const sock_fprog prog_x64 { .len = (unsigned short)(sizeof(strict_filter_x64) / sizeof(strict_filter_x64[0])), .filter = const_cast(strict_filter_x64), }; const sock_fprog prog_x32 { .len = (unsigned short)(sizeof(strict_filter_x32) / sizeof(strict_filter_x32[0])), .filter = const_cast(strict_filter_x32), }; CurrentKillSignal = SIGKILL; const sock_fprog* prog = Is64BitMode() ? &prog_x64 : &prog_x32; SetModeFilter(Frame, 0, prog); Thread->SeccompMode = SECCOMP_MODE_STRICT; return 0; } uint64_t SeccompEmulator::CanDoTSync(FEXCore::Core::CpuStateFrame* Frame) { auto ParentThread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); auto Threads = SyscallHandler->TM.GetThreads(); for (auto& Thread : *Threads) { if (Thread == ParentThread) { // Skip same thread. continue; } if (Thread->SeccompMode == SECCOMP_MODE_DISABLED) { // Threads which have seccomp disabled are safe to TSync continue; } if (Thread->SeccompMode != ParentThread->SeccompMode) { /// If the seccomp mode differs between threads then it can't tsync. /// Strict versus filter mode aren't tsync compatible. return Thread->ThreadInfo.TID; } if (Thread->Filters.size() != ParentThread->Filters.size()) { // If the filter count doesn't even match then it can't tsync. return Thread->ThreadInfo.TID; } // Walk each filter and ensure the entry points are the same and in the same order. for (size_t i = 0; i < ParentThread->Filters.size(); ++i) { if (Thread->Filters[i]->Func != ParentThread->Filters[i]->Func) { /// Entry point mismatch, not the same filter. /// Not tsync compatible. return Thread->ThreadInfo.TID; } } } // Everything matched. tsync compatible! return 0; } void SeccompEmulator::TSyncFilters(FEXCore::Core::CpuStateFrame* Frame) { auto ParentThread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); auto Threads = SyscallHandler->TM.GetThreads(); for (auto& Thread : *Threads) { if (Thread == ParentThread) { // Skip same thread. continue; } Thread->Filters.clear(); Thread->Filters = ParentThread->Filters; for (auto& Filter : ParentThread->Filters) { // Need to increment all the refcounters std::atomic_ref(Filter->RefCount)++; } Thread->SeccompMode = ParentThread->SeccompMode; } } // Equivalent to seccomp(SECCOMP_SET_MODE_FILTER, ...); uint64_t SeccompEmulator::SetModeFilter(FEXCore::Core::CpuStateFrame* Frame, uint32_t flags, const sock_fprog* prog) { auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); // Order of checks in this function matter // 1) Check flags // 2) Check if program is invalid uint32_t SUPPORTED_FLAGS = SECCOMP_FILTER_FLAG_TSYNC | // 1U << 0 SECCOMP_FILTER_FLAG_LOG | // 1U << 1 SECCOMP_FILTER_FLAG_SPEC_ALLOW | // 1U << 2 // SECCOMP_FILTER_FLAG_NEW_LISTENER | // 1U << 3 SECCOMP_FILTER_FLAG_TSYNC_ESRCH | // 1U << 4 0; const bool DoingTsync = flags & SECCOMP_FILTER_FLAG_TSYNC; if (flags & ~SUPPORTED_FLAGS) { // Unknown flags passed in. return -EINVAL; } if ((flags & SECCOMP_FILTER_FLAG_TSYNC) && (flags & SECCOMP_FILTER_FLAG_NEW_LISTENER) && !(flags & SECCOMP_FILTER_FLAG_TSYNC_ESRCH)) { /// If NEW_LISTENER and TSYNC are both used then TSYNC_ESRCH must also be set. /// Otherwise on error there would be no way to tell the difference between success and failure. return -EINVAL; } if (!prog) { return -EFAULT; } if (::prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 0) { // The caller did not have the CAP_SYS_ADMIN capability in its user namespace, or had not set no_new_privs before using SECCOMP_SET_MODE_FILTER. return -EACCES; } if (prog->len > BPF_MAXINSNS || prog->len == 0) { // operation specified SECCOMP_SET_MODE_FILTER, but the filter program pointed to by args was not valid or the length of the filter // program was zero or exceeded BPF_MAXINSNS (4096) instructions. return -EINVAL; } // Don't interrupt me while I'm jitting. auto lk = FEXCore::MaskSignalsAndLockMutex(FilterMutex); const size_t TotalFinalInstructions = TotalFilterInstructions + prog->len + Thread->Filters.size() * BPF_MULTIFILTERPENALTY; if (TotalFinalInstructions > BPF_MAX_INSNS_PER_PATH) { return -ENOMEM; } if constexpr (false) { // Useful for debugging seccomp problems. DumpProgram(prog); } if (DoingTsync) { auto TSyncThread = CanDoTSync(Frame); if (TSyncThread != 0) { if (flags & SECCOMP_FILTER_FLAG_TSYNC_ESRCH) { // This flag explicitly ensures that if TSYNC can't sync then it won't return a TID. return -ESRCH; } else { // Return the TID that caused a tsync problem. return TSyncThread; } } } BPFEmitter emit {}; const bool LoggingEnabled = flags & SECCOMP_FILTER_FLAG_LOG; auto Result = emit.JITFilter(flags, prog); if (Result == 0) { auto& it = Filters.emplace_back(SeccompFilterInfo {(SeccompFilterFunc)emit.GetFunc(), 1, emit.AllocationSize(), prog->len, LoggingEnabled}); TotalFilterInstructions += prog->len; // Append the filter to the thread. Thread->Filters.emplace_back(&it); Thread->SeccompMode = SECCOMP_MODE_FILTER; if (flags & SECCOMP_FILTER_FLAG_TSYNC) { TSyncFilters(Frame); } } return Result; } // Equivalent to seccomp(SECCOMP_GET_ACTION_AVAIL, ...); uint64_t SeccompEmulator::GetActionAvail(uint32_t flags, const uint32_t* action) { if (flags != 0) { // Unknown flags passed in return -EINVAL; } if (!action) { // Invalid action return -EFAULT; } switch (*action) { case SECCOMP_RET_KILL_PROCESS: case SECCOMP_RET_KILL_THREAD: case SECCOMP_RET_TRAP: case SECCOMP_RET_ERRNO: case SECCOMP_RET_LOG: case SECCOMP_RET_ALLOW: return 0; case SECCOMP_RET_USER_NOTIF: case SECCOMP_RET_TRACE: default: break; } return -EOPNOTSUPP; } // Equivalent to seccomp(SECCOMP_GET_NOTIF_SIZES, ...); uint64_t SeccompEmulator::GetNotifSizes(uint32_t flags, struct seccomp_notif_sizes* sizes) { if (flags != 0) { // Unknown flags passed in return -EINVAL; } sizes->seccomp_notif = sizeof(struct seccomp_notif); sizes->seccomp_notif_resp = sizeof(struct seccomp_notif_resp); sizes->seccomp_data = sizeof(struct seccomp_data); return 0; } } // namespace FEX::HLE